From f742e08867a2b6e51e3e14715062598ffc2bba6e Mon Sep 17 00:00:00 2001 From: Ryan Graciano Date: Sat, 10 Dec 2011 13:56:08 -0500 Subject: [PATCH] The goal here is to add a new hotkey to QuickCursor: "Insert". Currently, QuickCursor always copies all text from the target window before opening the external editor. After the editor is opened and the text is edited, QuickCursor eliminates everything from the original buffer in the paste. This is not so great if you are, say, replying to an email and only want to edit your response in the external editor without destroying the rest of the email chain. Hence, "insert" - "insert" will NOT copy or select the buffer before opening the external editor. It'll just open up the editor, and upon save/quit, it'll paste the text back in at the cursor. While I was in there, I also made one or two minor enhancements. Now, if you pick a shortcut that is already in use, the app will look through the existing shortcuts and remove dupes. This also works with the Edit/Insert feature; the app will actually pull the dupe shortcut out of the other control if it's currently displaying. This is the first thing I have ever done in Objective-C, so apologies if I'm slightly off on common idioms and that sort of thing. --- English.lproj/MainMenu.xib | 307 +++++++++++++++++++++---- QCAppDelegate.h | 28 ++- QCAppDelegate.m | 310 ++++++++++++++++++++------ QCSRDelegate.h | 25 +++ QCSRDelegate.m | 47 ++++ QuickCursor.xcodeproj/project.pbxproj | 6 + 6 files changed, 599 insertions(+), 124 deletions(-) create mode 100644 QCSRDelegate.h create mode 100644 QCSRDelegate.m diff --git a/English.lproj/MainMenu.xib b/English.lproj/MainMenu.xib index 03cb2f9..cc69ff5 100644 --- a/English.lproj/MainMenu.xib +++ b/English.lproj/MainMenu.xib @@ -61,7 +61,7 @@ 7 2 - {{235, 207}, {435, 331}} + {{235, 207}, {435, 385}} 1685588992 QuickCursor Preferences NSWindow @@ -75,9 +75,10 @@ 268 - {{102, 286}, {153, 26}} + {{102, 314}, {153, 26}} + 2 YES @@ -115,15 +116,16 @@ 268 - {{17, 293}, {82, 17}} + {{17, 322}, {82, 17}} + 2 YES 68288064 71304192 - Shortcuts: + Edit All @@ -162,6 +164,7 @@ {394, 171} + 2 YES @@ -170,15 +173,14 @@ {394, 17} + 2 - - + + -2147483392 {{224, 0}, {16, 17}} - - 2 @@ -293,6 +295,7 @@ {{224, 17}, {15, 102}} + 2 _doScroller: @@ -304,6 +307,7 @@ {{1, 301}, {381, 15}} + 2 1 @@ -326,19 +330,17 @@ 4 - - {{20, 49}, {396, 189}} + {{19, 49}, {396, 189}} - + 2 133682 - QSAAAEEgAABBmAAAQZgAAA @@ -347,12 +349,13 @@ {{17, 246}, {401, 17}} + 2 YES 68288064 272630784 - Temporary File Extensions: + Temporary File Extensions @@ -362,9 +365,10 @@ 12 - {{20, 269}, {395, 5}} + {{19, 269}, {395, 5}} + {0, 0} 67239424 @@ -393,9 +397,10 @@ 268 - {{20, 19}, {40, 23}} + {{19, 19}, {40, 23}} + YES -2080244224 @@ -418,9 +423,10 @@ 268 - {{59, 19}, {40, 23}} + {{58, 19}, {40, 23}} + YES -2080244224 @@ -443,18 +449,105 @@ 268 - {{260, 289}, {155, 22}} + {{260, 318}, {155, 22}} + + + + SRRecorderControl + + + + 268 + {{102, 286}, {153, 26}} + + + + 2 + YES + + -2076049856 + 134219776 + + + -2035138305 + 129 + + + + 400 + 75 + + YES + + OtherViews + + YES + + + + -1 + 1 + YES + YES + 2 + + + + + 268 + {{261, 288}, {155, 22}} + SRRecorderControl + + + 268 + {{17, 290}, {82, 17}} + + + + 2 + YES + + 68288064 + 71304192 + Insert Only + + + + + + + + + 268 + {{303, 348}, {68, 17}} + + + + _NS:3944 + YES + + 68288064 + 138413056 + Shortcut + + _NS:3944 + + + + + - {435, 331} + {435, 385} + 2 - {{0, 0}, {1920, 1178}} + {{0, 0}, {1680, 1050}} {10000000000000, 10000000000000} QuickCursorPreferences YES @@ -777,14 +870,6 @@ 546 - - - editInPopUpButtonClicked: - - - - 551 - showPreferences: @@ -803,11 +888,43 @@ - shortcutRecorder + insertInPopUpButton + + + + 900 + + + + insertShortcutRecorder + + + + 901 + + + + editShortcutRecorder - 887 + 902 + + + + popUpButtonClicked: + + + + 904 + + + + popUpButtonClicked: + + + + 905 @@ -902,6 +1019,14 @@ 886 + + + delegate + + + + 893 + @@ -958,12 +1083,16 @@ YES + + + + + - @@ -1303,6 +1432,62 @@ + + 888 + + + YES + + + + + + 889 + + + YES + + + + + + 890 + + + + + 892 + + + + + 894 + + + YES + + + + + + 895 + + + + + 898 + + + YES + + + + + + 899 + + + @@ -1319,6 +1504,7 @@ 533.NSWindowTemplate.visibleAtLaunch 534.IBPluginDependency 540.IBPluginDependency + 540.userInterfaceItemIdentifier 541.IBPluginDependency 542.IBPluginDependency 547.IBPluginDependency @@ -1369,6 +1555,15 @@ 871.IBPluginDependency 872.IBPluginDependency 885.IBPluginDependency + 888.IBPluginDependency + 888.userInterfaceItemIdentifier + 889.IBPluginDependency + 890.IBPluginDependency + 892.IBPluginDependency + 894.IBPluginDependency + 895.IBPluginDependency + 898.IBPluginDependency + 899.IBPluginDependency YES @@ -1382,6 +1577,7 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + edit com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -1432,6 +1628,15 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + insert + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin @@ -1446,7 +1651,7 @@ - 887 + 905 @@ -1458,8 +1663,8 @@ YES YES - beginQuickCursorEdit: - editInPopUpButtonClicked: + beginQuickCursorAction: + popUpButtonClicked: showAbout: showHelp: showPreferences: @@ -1477,8 +1682,8 @@ YES YES - beginQuickCursorEdit: - editInPopUpButtonClicked: + beginQuickCursorAction: + popUpButtonClicked: showAbout: showHelp: showPreferences: @@ -1486,11 +1691,11 @@ YES - beginQuickCursorEdit: + beginQuickCursorAction: id - editInPopUpButtonClicked: + popUpButtonClicked: id @@ -1512,16 +1717,20 @@ YES editInPopUpButton + editShortcutRecorder + insertInPopUpButton + insertShortcutRecorder openAtLogin preferencesWindow - shortcutRecorder YES NSPopUpButton + SRRecorderControl + NSPopUpButton + SRRecorderControl NSButton NSWindow - SRRecorderControl @@ -1529,9 +1738,11 @@ YES editInPopUpButton + editShortcutRecorder + insertInPopUpButton + insertShortcutRecorder openAtLogin preferencesWindow - shortcutRecorder YES @@ -1539,6 +1750,18 @@ editInPopUpButton NSPopUpButton + + editShortcutRecorder + SRRecorderControl + + + insertInPopUpButton + NSPopUpButton + + + insertShortcutRecorder + SRRecorderControl + openAtLogin NSButton @@ -1547,10 +1770,6 @@ preferencesWindow NSWindow - - shortcutRecorder - SRRecorderControl - diff --git a/QCAppDelegate.h b/QCAppDelegate.h index 333564d..3f9a9b4 100644 --- a/QCAppDelegate.h +++ b/QCAppDelegate.h @@ -8,22 +8,36 @@ #import #import - +#import "QCSRDelegate.h" +#import "PTKeyCombo.h" @class PTHotKey; +@class QCSRDelegate; + +extern NSString * const TYPE_EDIT; +extern NSString * const TYPE_INSERT; @interface QCAppDelegate : NSObject { IBOutlet NSWindow *preferencesWindow; - IBOutlet NSPopUpButton *editInPopUpButton; - IBOutlet SRRecorderControl *shortcutRecorder; IBOutlet NSButton *openAtLogin; - + + /* Keyboard shortcut popups and controls */ + IBOutlet NSPopUpButton *editInPopUpButton; + IBOutlet NSPopUpButton *insertInPopUpButton; + IBOutlet SRRecorderControl *editShortcutRecorder; + IBOutlet SRRecorderControl *insertShortcutRecorder; + NSStatusItem *quickCursorStatusItem; NSMutableSet *quickCursorSessionQCUIElements; NSMutableArray *registeredHotKeys; + + QCSRDelegate *editSRDelegate; + QCSRDelegate *insertSRDelegate; } + (BOOL)universalAccessNeedsToBeTurnedOn; ++ (NSString *)makeUserDefaultString:(NSDictionary *)representedObject; +- (void)updateHotKeys:(NSString *)userDefaultString addingKeyCombo:(PTKeyCombo *)newKeyCombo usingRecorder:(SRRecorderControl *)addingWithRecorder; //@property(assign) BOOL enableLoginItem; @@ -32,7 +46,9 @@ - (IBAction)showHelp:(id)sender; - (IBAction)showAbout:(id)sender; - (IBAction)showPreferences:(id)sender; -- (IBAction)editInPopUpButtonClicked:(id)sender; -- (IBAction)beginQuickCursorEdit:(id)sender; +- (IBAction)beginQuickCursorAction:(id)sender; + +/* Generic function for any shortcut popup button */ +- (IBAction)popUpButtonClicked:(id)sender; @end \ No newline at end of file diff --git a/QCAppDelegate.m b/QCAppDelegate.m index 963bb1a..29393f2 100644 --- a/QCAppDelegate.m +++ b/QCAppDelegate.m @@ -12,6 +12,8 @@ #import "QCUIElement.h" #import "ODBEditor.h" +NSString * const TYPE_EDIT = @"edit"; +NSString * const TYPE_INSERT = @"insert"; @implementation QCAppDelegate @@ -35,7 +37,38 @@ + (BOOL)universalAccessNeedsToBeTurnedOn { } } -- (NSArray *)validatedEditorMenuItems:(SEL)action { +/* + * We can't just identify the user default by the bundleID anymore, because + * we need to track two pieces of information in the stored shortcut: the type + * of action (edit vs insert) and the bundleID (identifies the application). + */ ++ (NSString *)makeUserDefaultString:(NSDictionary *)representedObject { + NSString *repObjectType = [representedObject objectForKey:@"type"]; + NSString *repObjectBundleID = [representedObject objectForKey:@"bundleID"]; + + // Example: @"edit org.myeditor.MyEditor" or @"insert org.myeditor.MyEditor" + return [NSString stringWithFormat:@"%@ %@", repObjectType, repObjectBundleID]; +} + +- (void)dealloc { + if (editSRDelegate) { + [editSRDelegate release]; + } + if (insertSRDelegate) { + [insertSRDelegate release]; + } + [super dealloc]; +} + +/* + * Loops through QuickCursor-Info.plist, grabs all of the possible bundle names, + * then finds installed bundles by those names. For each found name, two menu items + * are created - one for "Edit" and one for "Insert". + * + * Returns NSDictionary with key "edit" holding NSMenuItems for edit shortcuts, + * and key "Insert" holding NSMenuItems for insert shortcuts. + */ +- (NSMutableArray *)validatedEditorMenuItems:(SEL)action { static NSArray *cachedMenuItems = nil; if (!cachedMenuItems) { @@ -70,19 +103,50 @@ - (NSArray *)validatedEditorMenuItems:(SEL)action { cachedMenuItems = [menuItems retain]; } + // We build one big set of menu items. Two types are represented: "edit" and "insert" items. + // We can tell the difference by looking at [[each representedObject] stringForKey:@"type"] NSMutableArray *results = [NSMutableArray array]; for (NSMenuItem *each in cachedMenuItems) { - NSMenuItem *eachCopy = [[each copy] autorelease]; - [eachCopy setTarget:self]; - [eachCopy setAction:action]; - [results addObject:eachCopy]; + + // For each cached item, we will make one "Edit" and one "Insert" item. + + // The current representedObject is the string bundleID, like @"org.gnu.Aquamacs" + NSString *oldRepObject = (NSString *)[each representedObject]; + + // Creating the "Edit" item + NSMenuItem *editCopy = [[each copy] autorelease]; + + // The new represented object is a dictionary with: + // @"type" => @"edit", + // @"bundleID" => @"org.gnu.Aquamacs" + NSArray *editDictObjects = [NSArray arrayWithObjects:TYPE_EDIT, oldRepObject, nil]; + NSArray *editDictKeys = [NSArray arrayWithObjects:@"type", @"bundleID", nil]; + [editCopy setRepresentedObject:[NSDictionary dictionaryWithObjects:editDictObjects forKeys:editDictKeys]]; + + // Finish up the "Edit" item + [editCopy setTarget:self]; + [editCopy setAction:action]; + [results addObject:editCopy]; + + // Do the same thing to create the "Insert" item + NSMenuItem *insertCopy = [[each copy] autorelease]; + + NSArray *insertDictObjects = [NSArray arrayWithObjects:TYPE_INSERT, oldRepObject, nil]; + NSArray *insertDictKeys = [NSArray arrayWithObjects:@"type", @"bundleID", nil]; + [insertCopy setRepresentedObject:[NSDictionary dictionaryWithObjects:insertDictObjects forKeys:insertDictKeys]]; + + [insertCopy setTarget:self]; + [insertCopy setAction:action]; + [results addObject:insertCopy]; } - + return results; } -- (void)updateHotKeys { +/* When updating keys from an addition, pass in userDefaultString and the key combo, and + * this method will also make sure that there are no duplicates. */ +- (void)updateHotKeys:(NSString *)userDefaultString addingKeyCombo:(PTKeyCombo *)newKeyCombo usingRecorder:(SRRecorderControl *)addingWithRecorder { PTHotKeyCenter *hotKeyCenter = [PTHotKeyCenter sharedCenter]; NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; @@ -91,14 +155,46 @@ - (void)updateHotKeys { } [registeredHotKeys removeAllObjects]; - - for (NSMenuItem *each in [self validatedEditorMenuItems:NULL]) { - id eachKeyComboPlist = [userDefaults objectForKey:[each representedObject]]; - if (eachKeyComboPlist) { - PTKeyCombo *keyCombo = [[[PTKeyCombo alloc] initWithPlistRepresentation:eachKeyComboPlist] autorelease]; - PTHotKey *hotKey = [[[PTHotKey alloc] initWithIdentifier:[each representedObject] keyCombo:keyCombo] autorelease]; - [hotKey setTarget:self]; - [hotKey setAction:@selector(beginQuickCursorEdit:)]; + + for (NSMenuItem *each in [self validatedEditorMenuItems:NULL]) { + NSString *curUserDefaultString = [QCAppDelegate makeUserDefaultString:[each representedObject]]; + + id eachKeyComboPlist = [userDefaults objectForKey:curUserDefaultString]; + if (eachKeyComboPlist) { + PTKeyCombo *curKeyCombo = [[[PTKeyCombo alloc] initWithPlistRepresentation:eachKeyComboPlist] autorelease]; + + // If we found an existing key combo with the same one we're about to add, + // and it's not for this program, then we skip it because it just got overwritten + // by the addition. + if (newKeyCombo) { + if ([newKeyCombo isEqual:curKeyCombo]) { + if (![curUserDefaultString isEqualToString:userDefaultString]) { + [userDefaults removeObjectForKey:curUserDefaultString]; + + SRRecorderControl *otherRecorder; + if (addingWithRecorder == editShortcutRecorder) { + otherRecorder = insertShortcutRecorder; + } else { + otherRecorder = editShortcutRecorder; + } + + NSString *addingComboString = [addingWithRecorder keyComboString]; + NSString *otherComboString = [otherRecorder keyComboString]; + + // If the combo we're changing is actually displayed right now on the other control + // in the preferences dialog, then we clear it out right now + if ([addingComboString isEqualToString:otherComboString]) { + [otherRecorder setKeyCombo:SRMakeKeyCombo(ShortcutRecorderEmptyCode, ShortcutRecorderEmptyFlags)]; + } + + continue; + } + } + } + + PTHotKey *hotKey = [[[PTHotKey alloc] initWithIdentifier:curUserDefaultString keyCombo:curKeyCombo] autorelease]; + [hotKey setTarget:self]; + [hotKey setAction:@selector(beginQuickCursorAction:)]; [hotKeyCenter registerHotKey:hotKey]; [registeredHotKeys addObject:hotKey]; } @@ -106,8 +202,10 @@ - (void)updateHotKeys { } - (BOOL)validateMenuItem:(NSMenuItem *)anItem { - if ([anItem action] == @selector(beginQuickCursorEdit:)) { - id keyComboPlist = [[NSUserDefaults standardUserDefaults] objectForKey:[anItem representedObject]]; + if ([anItem action] == @selector(beginQuickCursorAction:)) { + NSString *userDefaultString = [QCAppDelegate makeUserDefaultString:[anItem representedObject]]; + + id keyComboPlist = [[NSUserDefaults standardUserDefaults] objectForKey:userDefaultString]; BOOL clear = YES; if (keyComboPlist) { @@ -143,14 +241,36 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { [quickCursorStatusItem setHighlightMode:YES]; NSMenu *quickCursorMenu = [[[NSMenu alloc] init] autorelease]; - - [quickCursorMenu addItemWithTitle:NSLocalizedString(@"Edit In...", nil) action:NULL keyEquivalent:@""]; - NSArray *supportedAppsMenuItems = [self validatedEditorMenuItems:@selector(beginQuickCursorEdit:)]; - if ([supportedAppsMenuItems count] > 0) { - for (NSMenuItem *each in supportedAppsMenuItems) { + NSMutableArray *validatedItems = [self validatedEditorMenuItems:@selector(beginQuickCursorAction:)]; + + NSMutableArray *editItems = [NSMutableArray array]; + NSMutableArray *insertItems = [NSMutableArray array]; + + // Build separate Insert and Edit items for the menus... + for (NSMenuItem *each in validatedItems) { + NSString *type = [[each representedObject] objectForKey:@"type"]; + if ([type isEqualToString:TYPE_EDIT]) { + [editItems addObject:each]; + } else { + [insertItems addObject:each]; + } + } + + // Add "Edit" and "Insert" to menu bar + if ([validatedItems count] > 0) { + [quickCursorMenu addItemWithTitle:NSLocalizedString(@"Edit In...", nil) action:NULL keyEquivalent:@""]; + + for (NSMenuItem *each in editItems) { [quickCursorMenu addItem:each]; } + + [quickCursorMenu addItemWithTitle:NSLocalizedString(@"Insert In...", nil) action:NULL keyEquivalent:@""]; + + for (NSMenuItem *each in insertItems) { + [quickCursorMenu addItem:each]; + } + } else { [quickCursorMenu addItemWithTitle:NSLocalizedString(@"No Supported Text Editors Found", nil) action:nil keyEquivalent:@""]; [[[quickCursorMenu itemArray] lastObject] setIndentationLevel:1]; @@ -178,7 +298,7 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { [quickCursorStatusItem setMenu:quickCursorMenu]; - [self updateHotKeys]; + [self updateHotKeys:NULL addingKeyCombo:NULL usingRecorder:NULL]; NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; if (![userDefaults boolForKey:@"SuppressWelcomeDefaultKey"]) { @@ -261,22 +381,48 @@ - (IBAction)showAbout:(id)sender { [NSApp orderFrontStandardAboutPanel:sender]; } -- (IBAction)showPreferences:(id)sender { - if ([editInPopUpButton numberOfItems] == 0) { +- (void)setupPrefsForShortcuts:(NSPopUpButton *)popUpButton shortcutRecorder:(SRRecorderControl *)shortcutRecorder { + if ([popUpButton numberOfItems] == 0) { [shortcutRecorder setCanCaptureGlobalHotKeys:YES]; - [shortcutRecorder setDelegate:self]; - for (NSMenuItem *each in [self validatedEditorMenuItems:NULL]) { - [[editInPopUpButton menu] addItem:each]; + // Straight up pointer equality is fine here, since these are pointing to the same place + BOOL isEditType = (popUpButton == editInPopUpButton); + + if (isEditType) { + if (!editSRDelegate) { + editSRDelegate = [[QCSRDelegate alloc] initWithOwnerAndBtn:self popUpButton:editInPopUpButton]; + } + [shortcutRecorder setDelegate:editSRDelegate]; + } + else { + if (!insertSRDelegate) { + insertSRDelegate = [[QCSRDelegate alloc] initWithOwnerAndBtn:self popUpButton:insertInPopUpButton]; + } + [shortcutRecorder setDelegate:insertSRDelegate]; + } + + NSMutableArray *validatedItems = [self validatedEditorMenuItems:NULL]; + NSString *onlyUseType = isEditType ? TYPE_EDIT : TYPE_INSERT; + + for (NSMenuItem *each in validatedItems) { + NSString *type = [[each representedObject] objectForKey:@"type"]; + if ([type isEqualToString:onlyUseType]) { + [[popUpButton menu] addItem:each]; + } } - [self editInPopUpButtonClicked:nil]; + [self popUpButtonClicked:popUpButton]; - if ([editInPopUpButton numberOfItems] == 0) { - [editInPopUpButton setEnabled:NO]; + if ([popUpButton numberOfItems] == 0) { + [popUpButton setEnabled:NO]; [shortcutRecorder setEnabled:NO]; } } +} + +- (IBAction)showPreferences:(id)sender { + [self setupPrefsForShortcuts:editInPopUpButton shortcutRecorder:editShortcutRecorder]; + [self setupPrefsForShortcuts:insertInPopUpButton shortcutRecorder:insertShortcutRecorder]; [NSApp activateIgnoringOtherApps:YES]; [preferencesWindow center]; @@ -287,54 +433,86 @@ - (IBAction)showHelp:(id)sender { [[NSWorkspace sharedWorkspace] openFile:[[NSBundle mainBundle] pathForResource:@"QuickCursor User's Guide" ofType:@"pdf"]]; } -- (IBAction)editInPopUpButtonClicked:(id)sender { - id clicked = [[editInPopUpButton selectedItem] representedObject]; +/* + * Generic function to process pop-up button clicks (selects the application shortcut) + * Can process both 'Edit' and 'Insert' shortcut clicks. + */ +- (IBAction)popUpButtonClicked:(id)sender { + if (![sender isKindOfClass:[NSPopUpButton class]]) { + return; + } + + // Since we know the object type, cast it + NSPopUpButton *popupSender = (NSPopUpButton *)sender; + + // Figure out which shortcut is being set right now + NSString *uiID = [popupSender identifier]; + SRRecorderControl *recorder; + recorder = ([uiID isEqualToString:TYPE_EDIT]) ? editShortcutRecorder : insertShortcutRecorder; + + id clicked = [QCAppDelegate makeUserDefaultString:[[popupSender selectedItem] representedObject]]; if (clicked) { id keyComboPlist = [[NSUserDefaults standardUserDefaults] objectForKey:clicked]; if (keyComboPlist) { KeyCombo keyCombo; PTKeyCombo *keyComboObject = [[[PTKeyCombo alloc] initWithPlistRepresentation:keyComboPlist] autorelease]; keyCombo.code = [keyComboObject keyCode]; - keyCombo.flags = [shortcutRecorder carbonToCocoaFlags:[keyComboObject modifiers]]; - [shortcutRecorder setKeyCombo:keyCombo]; + keyCombo.flags = [recorder carbonToCocoaFlags:[keyComboObject modifiers]]; + [recorder setKeyCombo:keyCombo]; } else { - [shortcutRecorder setKeyCombo:SRMakeKeyCombo(ShortcutRecorderEmptyCode, ShortcutRecorderEmptyFlags)]; + [recorder setKeyCombo:SRMakeKeyCombo(ShortcutRecorderEmptyCode, ShortcutRecorderEmptyFlags)]; } } } -- (IBAction)beginQuickCursorEdit:(id)sender { +- (IBAction)beginQuickCursorAction:(id)sender { if ([QCAppDelegate universalAccessNeedsToBeTurnedOn]) { return; } - NSString *bundleID = nil; + NSString *repObjStr = nil; if ([sender isKindOfClass:[NSMenuItem class]]) { - bundleID = [sender representedObject]; + repObjStr = [sender representedObject]; } else { - bundleID = [sender identifier]; + repObjStr = [sender identifier]; } + + NSArray *repObjComponents = [repObjStr componentsSeparatedByString:@" "]; + if ([repObjComponents count] != 2) { + return; + } + + NSString *type = (NSString *)[repObjComponents objectAtIndex:0]; + NSString *bundleID = (NSString *)[repObjComponents objectAtIndex:1]; QCUIElement *focusedElement = [QCUIElement focusedElement]; - QCUIElement *sourceApplicationElement = [focusedElement application]; - NSString *editString = [sourceApplicationElement readString]; + QCUIElement *sourceApplicationElement = [focusedElement application]; NSString *processName = [sourceApplicationElement processName]; - - if (editString) { - NSDictionary *context = [NSDictionary dictionaryWithObjectsAndKeys:sourceApplicationElement, @"sourceApplicationElement", bundleID, @"editorBundleID", editString, @"originalString", processName, @"processName", nil]; - NSString *windowTitle = focusedElement.window.title; - NSString *correctedWindowTitle = [windowTitle stringByReplacingOccurrencesOfString:@"/" withString:@":"]; - NSString *editorCustomPath = [NSString stringWithFormat:@"%@ - %@", processName, correctedWindowTitle]; - [[ODBEditor sharedODBEditor] setEditorBundleIdentifier:bundleID]; - [[ODBEditor sharedODBEditor] editString:editString options:[NSDictionary dictionaryWithObject:editorCustomPath forKey:ODBEditorCustomPathKey] forClient:self context:context]; - } else { - [[NSAlert alertWithMessageText:[NSString stringWithFormat:NSLocalizedString(@"Could not copy text from %@", nil), processName] - defaultButton:NSLocalizedString(@"OK", nil) - alternateButton:nil - otherButton:nil - informativeTextWithFormat:[NSString stringWithFormat:NSLocalizedString(@"QuickCursor could not copy text from %@. Please make sure that a text area has focus and try again.", nil), processName]] runModal]; - } + NSString *editString; + + if ([type isEqualToString:TYPE_EDIT]) { + editString = [sourceApplicationElement readString]; + + if (!editString) { + [[NSAlert alertWithMessageText:[NSString stringWithFormat:NSLocalizedString(@"Could not copy text from %@", nil), processName] + defaultButton:NSLocalizedString(@"OK", nil) + alternateButton:nil + otherButton:nil + informativeTextWithFormat:[NSString stringWithFormat:NSLocalizedString(@"QuickCursor could not copy text from %@. Please make sure that a text area has focus and try again.", nil), processName]] runModal]; + + return; + } + } else { + editString = @""; + } + + NSDictionary *context = [NSDictionary dictionaryWithObjectsAndKeys:sourceApplicationElement, @"sourceApplicationElement", bundleID, @"editorBundleID", editString, @"originalString", processName, @"processName", nil]; + NSString *windowTitle = focusedElement.window.title; + NSString *correctedWindowTitle = [windowTitle stringByReplacingOccurrencesOfString:@"/" withString:@":"]; + NSString *editorCustomPath = [NSString stringWithFormat:@"%@ - %@", processName, correctedWindowTitle]; + [[ODBEditor sharedODBEditor] setEditorBundleIdentifier:bundleID]; + [[ODBEditor sharedODBEditor] editString:editString options:[NSDictionary dictionaryWithObject:editorCustomPath forKey:ODBEditorCustomPathKey] forClient:self context:context]; } #pragma mark ODBEditor Callbacks @@ -382,20 +560,4 @@ - (void)odbEditor:(ODBEditor *)editor didCloseFileForString:(NSString *)newStrin } } -#pragma mark shortcutRecorder Delegate - -- (BOOL)shortcutRecorder:(SRRecorderControl *)aRecorder isKeyCode:(NSInteger)keyCode andFlagsTaken:(NSUInteger)flags reason:(NSString **)aReason { - return NO; -} - -- (void)shortcutRecorder:(SRRecorderControl *)aRecorder keyComboDidChange:(KeyCombo)newKeyCombo { - signed short code = newKeyCombo.code; - unsigned int flags = [aRecorder cocoaToCarbonFlags:newKeyCombo.flags]; - PTKeyCombo *keyCombo = [[[PTKeyCombo alloc] initWithKeyCode:code modifiers:flags] autorelease]; - NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; - [userDefaults setObject:[keyCombo plistRepresentation] forKey:[[editInPopUpButton selectedItem] representedObject]]; - [self updateHotKeys]; - [userDefaults synchronize]; -} - -@end \ No newline at end of file +@end diff --git a/QCSRDelegate.h b/QCSRDelegate.h new file mode 100644 index 0000000..abedba8 --- /dev/null +++ b/QCSRDelegate.h @@ -0,0 +1,25 @@ +// +// QCSRDelegate.h +// QuickCursor +// +// Created by Ryan Graciano on 12/8/11. +// Copyright (c) 2011. All rights reserved. +// +// One shortcut recorder control is used by each Edit and Insert. +// This class serves as a delegate for the SRRecorderControl +// and remembers which type of action (edit/ins) it's delegating. + +#import +#import +#import "QCAppDelegate.h" + +@class QCAppDelegate; + +@interface QCSRDelegate : NSObject { + NSPopUpButton *targetBtn; + QCAppDelegate *owner; +} + +- (id)initWithOwnerAndBtn:(id)newOwner popUpButton:(NSPopUpButton *)popUpButton; + +@end diff --git a/QCSRDelegate.m b/QCSRDelegate.m new file mode 100644 index 0000000..28e7c61 --- /dev/null +++ b/QCSRDelegate.m @@ -0,0 +1,47 @@ +// +// QCSRDelegate.m +// QuickCursor +// +// Created by Ryan Graciano on 12/8/11. +// Copyright (c) 2011. All rights reserved. +// + +#import "QCSRDelegate.h" +#import "PTHotKey.h" +#import "QCAppDelegate.h" + +@implementation QCSRDelegate + +- (void)shortcutRecorder:(SRRecorderControl *)aRecorder keyComboDidChange:(KeyCombo)newKeyCombo { + signed short code = newKeyCombo.code; + unsigned int flags = [aRecorder cocoaToCarbonFlags:newKeyCombo.flags]; + PTKeyCombo *keyCombo = [[[PTKeyCombo alloc] initWithKeyCode:code modifiers:flags] autorelease]; + + NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; + NSString *userDefaultString = [QCAppDelegate makeUserDefaultString:[[targetBtn selectedItem] representedObject]]; + [userDefaults setObject:[keyCombo plistRepresentation] forKey:userDefaultString]; + + [owner updateHotKeys:userDefaultString addingKeyCombo:keyCombo usingRecorder:aRecorder]; + [userDefaults synchronize]; +} + +- (id)initWithOwnerAndBtn:(id)newOwner popUpButton:(NSPopUpButton *)popUpButton { + self = [super init]; + + if (self) { + owner = newOwner; + [owner retain]; + + targetBtn = popUpButton; + [targetBtn retain]; + } + return self; +} + +- (void)dealloc { + [owner release]; + [targetBtn release]; + [super dealloc]; +} + +@end diff --git a/QuickCursor.xcodeproj/project.pbxproj b/QuickCursor.xcodeproj/project.pbxproj index f303625..df49a7c 100644 --- a/QuickCursor.xcodeproj/project.pbxproj +++ b/QuickCursor.xcodeproj/project.pbxproj @@ -36,6 +36,7 @@ 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; }; 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; }; 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; + 9496F7961490A85500D429FB /* QCSRDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 9496F7951490A85500D429FB /* QCSRDelegate.m */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -100,6 +101,8 @@ 88D3FC46133D0F8B00C2E651 /* ReceiptVerification.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReceiptVerification.h; sourceTree = ""; }; 88D3FC47133D0F8B00C2E651 /* ReceiptVerification.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReceiptVerification.m; sourceTree = ""; }; 8D1107320486CEB800E47090 /* QuickCursor.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = QuickCursor.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 9496F7941490A85500D429FB /* QCSRDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QCSRDelegate.h; sourceTree = ""; }; + 9496F7951490A85500D429FB /* QCSRDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QCSRDelegate.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -132,6 +135,8 @@ 88C3174C1469D7E0003E71E0 /* NSPasteboard_Extensions.m */, 882A8C26128C368400D05C94 /* ODB Editor */, 882A8C27128C369100D05C94 /* Hot Key */, + 9496F7941490A85500D429FB /* QCSRDelegate.h */, + 9496F7951490A85500D429FB /* QCSRDelegate.m */, ); name = Classes; sourceTree = ""; @@ -331,6 +336,7 @@ 882A8BDD128C349F00D05C94 /* NSAppleEventDescriptor-Extensions.m in Sources */, 88D3FC48133D0F8B00C2E651 /* ReceiptVerification.m in Sources */, 88C3174D1469D7E0003E71E0 /* NSPasteboard_Extensions.m in Sources */, + 9496F7961490A85500D429FB /* QCSRDelegate.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; };