// // AppController.m // Jumpcut // // Created by Steve Cook on 4/3/06. // Copyright 2006 __MyCompanyName__. All rights reserved. // // This code is open-source software subject to the MIT License; see the homepage // at for details. #import "AppController.h" #import "SGHotKey.h" #import "SGHotKeyCenter.h" #import "SRRecorderCell.h" #import "UKLoginItemRegistry.h" #import "NSWindow+TrueCenter.h" #define _DISPLENGTH 40 @implementation AppController - (id)init { [[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInt:15], @"displayNum", [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:[NSNumber numberWithInt:9],[NSNumber numberWithInt:1179648],nil] forKeys:[NSArray arrayWithObjects:@"keyCode",@"modifierFlags",nil]], @"ShortcutRecorder mainHotkey", [NSNumber numberWithInt:40], @"rememberNum", [NSNumber numberWithInt:1], @"savePreference", [NSNumber numberWithInt:0], @"menuIcon", [NSNumber numberWithFloat:.25], @"bezelAlpha", [NSNumber numberWithBool:YES], @"stickyBezel", [NSNumber numberWithBool:NO], @"wraparoundBezel", [NSNumber numberWithBool:NO],// No by default @"loadOnStartup", [NSNumber numberWithBool:YES], @"menuSelectionPastes", // Flycut new options [NSNumber numberWithFloat:500.0], @"bezelWidth", [NSNumber numberWithFloat:320.0], @"bezelHeight", nil] ]; return [super init]; } - (void)awakeFromNib { // We no longer get autosave from ShortcutRecorder, so let's set the recorder by hand if ( [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"ShortcutRecorder mainHotkey"] ) { [mainRecorder setKeyCombo:SRMakeKeyCombo([[[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"ShortcutRecorder mainHotkey"] objectForKey:@"keyCode"] intValue], [[[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"ShortcutRecorder mainHotkey"] objectForKey:@"modifierFlags"] intValue] ) ]; }; // Initialize the JumpcutStore clippingStore = [[JumpcutStore alloc] initRemembering:[[NSUserDefaults standardUserDefaults] integerForKey:@"rememberNum"] displaying:[[NSUserDefaults standardUserDefaults] integerForKey:@"displayNum"] withDisplayLength:_DISPLENGTH]; NSRect screenFrame = [[NSScreen mainScreen] frame]; widthSlider.maxValue = screenFrame.size.width; heightSlider.maxValue = screenFrame.size.height; // Set up the bezel window NSRect windowFrame = NSMakeRect(0, 0, [[NSUserDefaults standardUserDefaults] floatForKey:@"bezelWidth"], [[NSUserDefaults standardUserDefaults] floatForKey:@"bezelHeight"]); bezel = [[BezelWindow alloc] initWithContentRect:windowFrame styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]; [bezel trueCenter]; [bezel setDelegate:self]; // Create our pasteboard interface jcPasteboard = [NSPasteboard generalPasteboard]; [jcPasteboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil]; pbCount = [[NSNumber numberWithInt:[jcPasteboard changeCount]] retain]; // Build the statusbar menu statusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength] retain]; [statusItem setHighlightMode:YES]; if ( [[NSUserDefaults standardUserDefaults] integerForKey:@"menuIcon"] == 1 ) { [statusItem setTitle:[NSString stringWithFormat:@"%C",0x2704]]; } else if ( [[NSUserDefaults standardUserDefaults] integerForKey:@"menuIcon"] == 2 ) { [statusItem setTitle:[NSString stringWithFormat:@"%C",0x2702]]; } else { [statusItem setImage:[NSImage imageNamed:@"com.generalarcade.flycut.16.png"]]; } [statusItem setMenu:jcMenu]; [statusItem setEnabled:YES]; // If our preferences indicate that we are saving, load the dictionary from the saved plist // and use it to get everything set up. if ( [[NSUserDefaults standardUserDefaults] integerForKey:@"savePreference"] >= 1 ) { [self loadEngineFromPList]; } // Build our listener timer pollPBTimer = [[NSTimer scheduledTimerWithTimeInterval:(1.0) target:self selector:@selector(pollPB:) userInfo:nil repeats:YES] retain]; // Finish up srTransformer = [[[SRKeyCodeTransformer alloc] init] retain]; pbBlockCount = [[NSNumber numberWithInt:0] retain]; [pollPBTimer fire]; // Stack position starts @ 0 by default stackPosition = 0; [NSApp activateIgnoringOtherApps: YES]; } -(IBAction) activateAndOrderFrontStandardAboutPanel:(id)sender { [[NSApplication sharedApplication] activateIgnoringOtherApps:YES]; [[NSApplication sharedApplication] orderFrontStandardAboutPanel:sender]; } -(IBAction) setBezelAlpha:(id)sender { // In a masterpiece of poorly-considered design--because I want to eventually // allow users to select from a variety of bezels--I've decided to create the // bezel programatically, meaning that I have to go through AppController as // a cutout to allow the user interface to interact w/the bezel. [bezel setAlpha:[sender floatValue]]; } -(IBAction) setBezelWidth:(id)sender { NSSize bezelSize = NSMakeSize([sender floatValue], bezel.frame.size.height); NSRect windowFrame = NSMakeRect( 0, 0, bezelSize.width, bezelSize.height); [bezel setFrame:windowFrame display:NO]; [bezel trueCenter]; } -(IBAction) setBezelHeight:(id)sender { NSSize bezelSize = NSMakeSize(bezel.frame.size.width, [sender floatValue]); NSRect windowFrame = NSMakeRect( 0, 0, bezelSize.width, bezelSize.height); [bezel setFrame:windowFrame display:NO]; [bezel trueCenter]; } -(IBAction) switchMenuIcon:(id)sender { if ([sender indexOfSelectedItem] == 1 ) { [statusItem setImage:nil]; [statusItem setTitle:[NSString stringWithFormat:@"%C",0x2704]]; } else if ( [sender indexOfSelectedItem] == 2 ) { [statusItem setImage:nil]; [statusItem setTitle:[NSString stringWithFormat:@"%C",0x2702]]; } else { [statusItem setTitle:@""]; [statusItem setImage:[NSImage imageNamed:@"com.generalarcade.flycut.16.png"]]; } } -(IBAction) setRememberNumPref:(id)sender { int choice; int newRemember = [sender intValue]; if ( newRemember < [clippingStore jcListCount] && ! issuedRememberResizeWarning && ! [[NSUserDefaults standardUserDefaults] boolForKey:@"stifleRememberResizeWarning"] ) { choice = NSRunAlertPanel(@"Resize Stack", @"Resizing the stack to a value below its present size will cause clippings to be lost.", @"Resize", @"Cancel", @"Don't Warn Me Again"); if ( choice == NSAlertAlternateReturn ) { [[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithInt:[clippingStore jcListCount]] forKey:@"rememberNum"]; [self updateMenu]; return; } else if ( choice == NSAlertOtherReturn ) { [[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithBool:YES] forKey:@"stifleRememberResizeWarning"]; } else { issuedRememberResizeWarning = YES; } } if ( newRemember < [[NSUserDefaults standardUserDefaults] integerForKey:@"displayNum"] ) { [[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithInt:newRemember] forKey:@"displayNum"]; } [clippingStore setRememberNum:newRemember]; [self updateMenu]; } -(IBAction) setDisplayNumPref:(id)sender { [self updateMenu]; } -(IBAction) showPreferencePanel:(id)sender { int checkLoginRegistry = [UKLoginItemRegistry indexForLoginItemWithPath:[[NSBundle mainBundle] bundlePath]]; if ( checkLoginRegistry >= 1 ) { [[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithBool:YES] forKey:@"loadOnStartup"]; } else { [[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithBool:NO] forKey:@"loadOnStartup"]; } if ([prefsPanel respondsToSelector:@selector(setCollectionBehavior:)]) [prefsPanel setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces]; [NSApp activateIgnoringOtherApps: YES]; [prefsPanel makeKeyAndOrderFront:self]; issuedRememberResizeWarning = NO; } -(IBAction)toggleLoadOnStartup:(id)sender { if ( [[NSUserDefaults standardUserDefaults] boolForKey:@"loadOnStartup"] ) { [UKLoginItemRegistry addLoginItemWithPath:[[NSBundle mainBundle] bundlePath] hideIt:NO]; } else { [UKLoginItemRegistry removeLoginItemWithPath:[[NSBundle mainBundle] bundlePath]]; } } - (void)pasteFromStack { if ( [clippingStore jcListCount] > stackPosition ) { [self addClipToPasteboardFromCount:stackPosition]; [self performSelector:@selector(hideApp) withObject:nil afterDelay:0.2]; [self performSelector:@selector(fakeCommandV) withObject:nil afterDelay:0.2]; } else { [self performSelector:@selector(hideApp) withObject:nil afterDelay:0.2]; } } - (void)metaKeysReleased { if ( ! isBezelPinned ) { [self pasteFromStack]; } } -(void)fakeCommandV /*" +fakeCommandV synthesizes keyboard events for Cmd-v Paste shortcut. "*/ { CGEventSourceRef sourceRef = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState); if (!sourceRef) { NSLog(@"No event source"); return; } //9 = "v" CGEventRef eventDown = CGEventCreateKeyboardEvent(sourceRef, (CGKeyCode)9, true); CGEventSetFlags(eventDown, kCGEventFlagMaskCommand); CGEventRef eventUp = CGEventCreateKeyboardEvent(sourceRef, (CGKeyCode)9, false); CGEventPost(kCGHIDEventTap, eventDown); CGEventPost(kCGHIDEventTap, eventUp); CFRelease(eventDown); CFRelease(eventUp); CFRelease(sourceRef); } -(void)pollPB:(NSTimer *)timer { NSString *type = [jcPasteboard availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]]; if ( [pbCount intValue] != [jcPasteboard changeCount] ) { // Reload pbCount with the current changeCount // Probably poor coding technique, but pollPB should be the only thing messing with pbCount, so it should be okay [pbCount release]; pbCount = [[NSNumber numberWithInt:[jcPasteboard changeCount]] retain]; if ( type != nil ) { NSString *contents = [jcPasteboard stringForType:type]; if ( contents == nil ) { // NSLog(@"Contents: Empty"); } else { if (( [clippingStore jcListCount] == 0 || ! [contents isEqualToString:[clippingStore clippingContentsAtPosition:0]]) && ! [pbCount isEqualTo:pbBlockCount] ) { [clippingStore addClipping:contents ofType:type ]; // The below tracks our position down down down... Maybe as an option? // if ( [clippingStore jcListCount] > 1 ) stackPosition++; stackPosition = 0; [self updateMenu]; if ( [[NSUserDefaults standardUserDefaults] integerForKey:@"savePreference"] >= 2 ) { [self saveEngine]; } } } } else { // NSLog(@"Contents: Non-string"); } } } - (void)processBezelKeyDown:(NSEvent *)theEvent { int newStackPosition; // AppControl should only be getting these directly from bezel via delegation if ( [theEvent type] == NSKeyDown ) { if ( [theEvent keyCode] == [mainRecorder keyCombo].code ) { if ( [theEvent modifierFlags] & NSShiftKeyMask ) { [self stackUp]; } else { [self stackDown]; } return; } unichar pressed = [[theEvent charactersIgnoringModifiers] characterAtIndex:0]; switch ( pressed ) { case 0x1B: [self hideApp]; break; case 0x3: case 0xD: // Enter or Return [self pasteFromStack]; break; case NSUpArrowFunctionKey: case NSLeftArrowFunctionKey: [self stackUp]; break; case NSDownArrowFunctionKey: case NSRightArrowFunctionKey: [self stackDown]; break; case NSHomeFunctionKey: if ( [clippingStore jcListCount] > 0 ) { stackPosition = 0; [bezel setCharString:[NSString stringWithFormat:@"%d of %d", stackPosition + 1, [clippingStore jcListCount]]]; //[bezel setCharString:[NSString stringWithFormat:@"%d", stackPosition + 1]]; [bezel setText:[clippingStore clippingContentsAtPosition:stackPosition]]; } break; case NSEndFunctionKey: if ( [clippingStore jcListCount] > 0 ) { stackPosition = [clippingStore jcListCount] - 1; [bezel setCharString:[NSString stringWithFormat:@"%d of %d", stackPosition + 1, [clippingStore jcListCount]]]; [bezel setText:[clippingStore clippingContentsAtPosition:stackPosition]]; } break; case NSPageUpFunctionKey: if ( [clippingStore jcListCount] > 0 ) { stackPosition = stackPosition - 10; if ( stackPosition < 0 ) stackPosition = 0; [bezel setCharString:[NSString stringWithFormat:@"%d of %d", stackPosition + 1, [clippingStore jcListCount]]]; [bezel setText:[clippingStore clippingContentsAtPosition:stackPosition]]; } break; case NSPageDownFunctionKey: if ( [clippingStore jcListCount] > 0 ) { stackPosition = stackPosition + 10; if ( stackPosition >= [clippingStore jcListCount] ) stackPosition = [clippingStore jcListCount] - 1; [bezel setCharString:[NSString stringWithFormat:@"%d of %d", stackPosition + 1, [clippingStore jcListCount]]]; [bezel setText:[clippingStore clippingContentsAtPosition:stackPosition]]; } break; case NSBackspaceCharacter: break; case NSDeleteCharacter: break; case NSDeleteFunctionKey: break; case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: // Numeral case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: // We'll currently ignore the possibility that the user wants to do something with shift. // First, let's set the new stack count to "10" if the user pressed "0" newStackPosition = pressed == 0x30 ? 9 : [[NSString stringWithCharacters:&pressed length:1] intValue] - 1; if ( [clippingStore jcListCount] >= newStackPosition ) { stackPosition = newStackPosition; [bezel setCharString:[NSString stringWithFormat:@"%d of %d", stackPosition + 1, [clippingStore jcListCount]]]; [bezel setText:[clippingStore clippingContentsAtPosition:stackPosition]]; } break; default: // It's not a navigation/application-defined thing, so let's figure out what to do with it. NSLog(@"PRESSED %d", pressed); NSLog(@"CODE %ld", [mainRecorder keyCombo].code); break; } } } - (void)applicationDidFinishLaunching:(NSNotification *)notification { //Create our hot key [self toggleMainHotKey:[NSNull null]]; } - (void) showBezel { if ( [clippingStore jcListCount] > 0 && [clippingStore jcListCount] > stackPosition ) { [bezel setCharString:[NSString stringWithFormat:@"%d of %d", stackPosition + 1, [clippingStore jcListCount]]]; [bezel setText:[clippingStore clippingContentsAtPosition:stackPosition]]; } if ([bezel respondsToSelector:@selector(setCollectionBehavior:)]) [bezel setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces]; [bezel makeKeyAndOrderFront:nil]; isBezelDisplayed = YES; } - (void) hideBezel { [bezel orderOut:nil]; [bezel setCharString:@""]; isBezelDisplayed = NO; } -(void)hideApp { [self hideBezel]; isBezelPinned = NO; [NSApp hide:self]; } - (void) applicationWillResignActive:(NSApplication *)app; { // This should be hidden anyway, but just in case it's not. [self hideBezel]; } - (void)hitMainHotKey:(SGHotKey *)hotKey { if ( ! isBezelDisplayed ) { [NSApp activateIgnoringOtherApps:YES]; if ( [[NSUserDefaults standardUserDefaults] boolForKey:@"stickyBezel"] ) { isBezelPinned = YES; } [self showBezel]; } else { [self stackDown]; } } - (IBAction)toggleMainHotKey:(id)sender { if (mainHotKey != nil) { [[SGHotKeyCenter sharedCenter] unregisterHotKey:mainHotKey]; [mainHotKey release]; mainHotKey = nil; } mainHotKey = [[SGHotKey alloc] initWithIdentifier:@"mainHotKey" keyCombo:[SGKeyCombo keyComboWithKeyCode:[mainRecorder keyCombo].code modifiers:[mainRecorder cocoaToCarbonFlags: [mainRecorder keyCombo].flags]]]; [mainHotKey setName: @"Activate Flycut HotKey"]; //This is typically used by PTKeyComboPanel [mainHotKey setTarget: self]; [mainHotKey setAction: @selector(hitMainHotKey:)]; [[SGHotKeyCenter sharedCenter] registerHotKey:mainHotKey]; } -(IBAction)clearClippingList:(id)sender { int choice; [NSApp activateIgnoringOtherApps:YES]; choice = NSRunAlertPanel(@"Clear Clipping List", @"Do you want to clear all recent clippings?", @"Clear", @"Cancel", nil); // on clear, zap the list and redraw the menu if ( choice == NSAlertDefaultReturn ) { [clippingStore clearList]; [self updateMenu]; if ( [[NSUserDefaults standardUserDefaults] integerForKey:@"savePreference"] >= 1 ) { [self saveEngine]; } [bezel setText:@""]; } } - (void)updateMenu { NSArray *returnedDisplayStrings = [clippingStore previousDisplayStrings:[[NSUserDefaults standardUserDefaults] integerForKey:@"displayNum"]]; NSArray *menuItems = [[[jcMenu itemArray] reverseObjectEnumerator] allObjects]; NSArray *clipStrings = [[returnedDisplayStrings reverseObjectEnumerator] allObjects]; int passedSeparator = 0; //remove clippings from menu for (NSMenuItem *oldItem in menuItems) { if( [oldItem isSeparatorItem]) { passedSeparator++; } else if ( passedSeparator == 2 ) { [jcMenu removeItem:oldItem]; } } for(NSString *pbMenuTitle in clipStrings) { NSMenuItem *item; item = [[NSMenuItem alloc] initWithTitle:pbMenuTitle action:@selector(processMenuClippingSelection:) keyEquivalent:@""]; [item setTarget:self]; [item setEnabled:YES]; [jcMenu insertItem:item atIndex:0]; // Way back in 0.2, failure to release the new item here was causing a quite atrocious memory leak. [item release]; } } -(IBAction)processMenuClippingSelection:(id)sender { int index=[[sender menu] indexOfItem:sender]; [self addClipToPasteboardFromCount:index]; if ( [[NSUserDefaults standardUserDefaults] boolForKey:@"menuSelectionPastes"] ) { [self performSelector:@selector(hideApp) withObject:nil]; [self performSelector:@selector(fakeCommandV) withObject:nil afterDelay:0.2]; } } -(BOOL) isValidClippingNumber:(NSNumber *)number { return ( ([number intValue] + 1) <= [clippingStore jcListCount] ); } -(NSString *) clippingStringWithCount:(int)count { if ( [self isValidClippingNumber:[NSNumber numberWithInt:count]] ) { return [clippingStore clippingContentsAtPosition:count]; } else { // It fails -- we shouldn't be passed this, but... return @""; } } -(void) setPBBlockCount:(NSNumber *)newPBBlockCount { [newPBBlockCount retain]; [pbBlockCount release]; pbBlockCount = newPBBlockCount; } -(BOOL)addClipToPasteboardFromCount:(int)indexInt { NSString *pbFullText; NSArray *pbTypes; if ( (indexInt + 1) > [clippingStore jcListCount] ) { // We're asking for a clipping that isn't there yet // This only tends to happen immediately on startup when not saving, as the entire list is empty. NSLog(@"Out of bounds request to jcList ignored."); return false; } pbFullText = [self clippingStringWithCount:indexInt]; pbTypes = [NSArray arrayWithObjects:@"NSStringPboardType",NULL]; [jcPasteboard declareTypes:pbTypes owner:NULL]; [jcPasteboard setString:pbFullText forType:@"NSStringPboardType"]; [self setPBBlockCount:[NSNumber numberWithInt:[jcPasteboard changeCount]]]; return true; } -(void) loadEngineFromPList { NSString *path = [[NSString stringWithString:@"~/Library/Application Support/Flycut/JCEngine.save"] stringByExpandingTildeInPath]; NSDictionary *loadDict = [[NSDictionary alloc] initWithContentsOfFile:path]; NSArray *savedJCList; NSRange loadRange; int rangeCap; if ( loadDict != nil ) { savedJCList = [loadDict objectForKey:@"jcList"]; if ( [savedJCList isKindOfClass:[NSArray class]] ) { int rememberNumPref = [[NSUserDefaults standardUserDefaults] integerForKey:@"rememberNum"]; // There's probably a nicer way to prevent the range from going out of bounds, but this works. rangeCap = [savedJCList count] < rememberNumPref ? [savedJCList count] : rememberNumPref; loadRange = NSMakeRange(0, rangeCap); NSArray *toBeRestoredClips = [[[savedJCList subarrayWithRange:loadRange] reverseObjectEnumerator] allObjects]; for( NSDictionary *aSavedClipping in toBeRestoredClips) { [clippingStore addClipping:[aSavedClipping objectForKey:@"Contents"] ofType:[aSavedClipping objectForKey:@"Type"]]; } } else { NSLog(@"Not array"); } [self updateMenu]; [loadDict release]; } } -(void) stackDown { stackPosition++; if ( [clippingStore jcListCount] > stackPosition ) { [bezel setCharString:[NSString stringWithFormat:@"%d of %d", stackPosition + 1, [clippingStore jcListCount]]]; [bezel setText:[clippingStore clippingContentsAtPosition:stackPosition]]; } else { if ( [[NSUserDefaults standardUserDefaults] boolForKey:@"wraparoundBezel"] ) { stackPosition = 0; [bezel setCharString:[NSString stringWithFormat:@"%d of %d", 1, [clippingStore jcListCount]]]; [bezel setText:[clippingStore clippingContentsAtPosition:stackPosition]]; } else { stackPosition--; } } } -(void) stackUp { stackPosition--; if ( stackPosition < 0 ) { if ( [[NSUserDefaults standardUserDefaults] boolForKey:@"wraparoundBezel"] ) { stackPosition = [clippingStore jcListCount] - 1; [bezel setCharString:[NSString stringWithFormat:@"%d of %d", stackPosition + 1, [clippingStore jcListCount]]]; [bezel setText:[clippingStore clippingContentsAtPosition:stackPosition]]; } else { stackPosition = 0; } } if ( [clippingStore jcListCount] > stackPosition ) { [bezel setCharString:[NSString stringWithFormat:@"%d of %d", stackPosition + 1, [clippingStore jcListCount]]]; [bezel setText:[clippingStore clippingContentsAtPosition:stackPosition]]; } } -(void) saveEngine { NSMutableDictionary *saveDict; NSMutableArray *jcListArray = [NSMutableArray array]; int i; BOOL isDir; NSString *path; path = [[NSString stringWithString:@"~/Library/Application Support/Flycut"] stringByExpandingTildeInPath]; if ( ![[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir] || ! isDir ) { NSLog(@"Creating Application Support directory"); [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:[NSDictionary dictionaryWithObjectsAndKeys: @"NSFileModificationDate", [NSNull null], @"NSFileOwnerAccountName", [NSNull null], @"NSFileGroupOwnerAccountName", [NSNull null], @"NSFilePosixPermissions", [NSNull null], @"NSFileExtensionsHidden", [NSNull null], nil] error:nil ]; } saveDict = [NSMutableDictionary dictionaryWithCapacity:3]; [saveDict setObject:@"0.7" forKey:@"version"]; [saveDict setObject:[NSNumber numberWithInt:[[NSUserDefaults standardUserDefaults] integerForKey:@"rememberNum"]] forKey:@"rememberNum"]; [saveDict setObject:[NSNumber numberWithInt:_DISPLENGTH] forKey:@"displayLen"]; [saveDict setObject:[NSNumber numberWithInt:[[NSUserDefaults standardUserDefaults] integerForKey:@"displayNum"]] forKey:@"displayNum"]; for ( i = 0 ; i < [clippingStore jcListCount]; i++) { [jcListArray addObject:[NSDictionary dictionaryWithObjectsAndKeys: [clippingStore clippingContentsAtPosition:i], @"Contents", [clippingStore clippingTypeAtPosition:i], @"Type", [NSNumber numberWithInt:i], @"Position", nil ] ]; } [saveDict setObject:jcListArray forKey:@"jcList"]; if ( [saveDict writeToFile:[path stringByAppendingString:@"/JCEngine.save"] atomically:true] ) { // NSLog(@"Engine contents saved."); } else { NSLog(@"Engine contents NOT saved."); } } - (void)setHotKeyPreferenceForRecorder:(SRRecorderControl *)aRecorder { if (aRecorder == mainRecorder) { [[NSUserDefaults standardUserDefaults] setObject: [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:[NSNumber numberWithInt:[mainRecorder keyCombo].code],[NSNumber numberWithInt:[mainRecorder keyCombo].flags],nil] forKeys:[NSArray arrayWithObjects:@"keyCode",@"modifierFlags",nil]] forKey:@"ShortcutRecorder mainHotkey"]; } } - (BOOL)shortcutRecorder:(SRRecorderControl *)aRecorder isKeyCode:(NSInteger)keyCode andFlagsTaken:(NSUInteger)flags reason:(NSString **)aReason { return NO; } - (void)shortcutRecorder:(SRRecorderControl *)aRecorder keyComboDidChange:(KeyCombo)newKeyCombo { if (aRecorder == mainRecorder) { [self toggleMainHotKey: aRecorder]; [self setHotKeyPreferenceForRecorder: aRecorder]; } NSLog(@"code: %lu, flags: %lu", newKeyCombo.code, newKeyCombo.flags); } - (void)applicationWillTerminate:(NSNotification *)notification { if ( [[NSUserDefaults standardUserDefaults] integerForKey:@"savePreference"] >= 1 ) { NSLog(@"Saving on exit"); [self saveEngine]; } //Unregister our hot key (not required) [[SGHotKeyCenter sharedCenter] unregisterHotKey: mainHotKey]; [mainHotKey release]; mainHotKey = nil; [self hideBezel]; [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:@"AppleKeyboardPreferencesChangedNotification" object:nil]; [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:@"AppleSelectedInputSourcesChangedNotification" object:nil]; } - (void) dealloc { [bezel release]; [srTransformer release]; [super dealloc]; } @end