Permalink
Browse files

Trying alternative approach, copy and paste from source app instead o…

…f directly access text. Good news... should work with many more apps including Mail.app. Bad news... still not quite working.
  • Loading branch information...
1 parent 6a656cd commit c5392a618a1d24d730b254c3a0a7c23ef2d5bb95 @jessegrosjean jessegrosjean committed Jan 5, 2010
Showing with 111 additions and 12 deletions.
  1. +19 −2 QCAppDelegate.m
  2. +5 −1 QCUIElement.h
  3. +86 −8 QCUIElement.m
  4. +1 −1 QuickCursor.xcodeproj/project.pbxproj
View
21 QCAppDelegate.m
@@ -287,6 +287,23 @@ - (IBAction)beginQuickCursorEdit:(id)sender {
QCUIElement *focusedElement = [QCUIElement focusedElement];
+ if ([focusedElement readString]) {
+ NSString *string = [[NSPasteboard generalPasteboard] stringForType:NSStringPboardType];
+ NSDictionary *context = [NSDictionary dictionaryWithObject:focusedElement forKey:@"uiElement"];
+ NSString *processName = [focusedElement processName];
+ NSString *windowTitle = focusedElement.window.title;
+ NSString *editorCustomPath = [NSString stringWithFormat:@"%@ - %@", processName, windowTitle];
+ [[ODBEditor sharedODBEditor] setEditorBundleIdentifier:bundleID];
+ [[ODBEditor sharedODBEditor] editString:string options:[NSDictionary dictionaryWithObject:editorCustomPath forKey:ODBEditorCustomPathKey] forClient:self context:context];
+ } else {
+ [[NSAlert alertWithMessageText:NSLocalizedString(@"Could not edit text", nil)
+ defaultButton:NSLocalizedString(@"OK", nil)
+ alternateButton:nil
+ otherButton:nil
+ informativeTextWithFormat:NSLocalizedString(@"QuickCursor could not find any text to edit. Make sure that a text view has keyboard focus, and then try again.", nil)] runModal];
+ }
+
+ /*
id value = focusedElement.value;
if ([value isKindOfClass:[NSString class]]) {
@@ -303,7 +320,7 @@ - (IBAction)beginQuickCursorEdit:(id)sender {
alternateButton:nil
otherButton:nil
informativeTextWithFormat:NSLocalizedString(@"QuickCursor could not find any text to edit. Make sure that a text view has keyboard focus, and then try again.", nil)] runModal];
- }
+ }*/
}
#pragma mark ODBEditor Callbacks
@@ -318,7 +335,7 @@ - (void)odbEditor:(ODBEditor *)editor didClosefile:(NSString *)path context:(NSD
- (void)odbEditor:(ODBEditor *)editor didModifyFileForString:(NSString *)newString context:(NSDictionary *)context; {
QCUIElement *uiElement = [context valueForKey:@"uiElement"];
- if (![uiElement setValue:newString]) {
+ if (![uiElement writeString:newString]) {
[NSApp activateIgnoringOtherApps:YES];
[[NSAlert alertWithMessageText:NSLocalizedString(@"Could not save text", nil)
defaultButton:NSLocalizedString(@"OK", nil)
View
6 QCUIElement.h
@@ -27,9 +27,11 @@
@property(readonly) pid_t processID;
@property(readonly) NSString *processName;
@property(readonly) QCUIElement *application;
+@property(readonly) QCUIElement *menuBar;
@property(readonly) QCUIElement *topLevelUIElement;
@property(readonly) QCUIElement *window;
@property(readonly) QCUIElement *parent;
+@property(readonly) NSArray *children;
@property(readonly) NSString *title;
@property(readonly) NSString *role;
@@ -39,8 +41,10 @@
- (id)valueForAttribute:(NSString *)attributeName;
- (BOOL)setValue:(id)newValue forAttribute:(NSString *)attributeName;
-#pragma mark Process
+#pragma mark Actions
- (BOOL)activateProcess;
+- (NSString *)readString;
+- (BOOL)writeString:(NSString *)pasteString;
@end
View
94 QCUIElement.m
@@ -25,7 +25,7 @@ + (QCUIElement *)systemWideElement {
}
+ (QCUIElement *)focusedElement {
- return [[self systemWideElement] valueForAttribute:@"AXFocusedUIElement"];
+ return [[self systemWideElement] valueForAttribute:(NSString *)kAXFocusedUIElementAttribute];
}
#pragma mark Init
@@ -67,13 +67,17 @@ - (NSString *)processName {
}
- (QCUIElement *)application {
- QCUIElement *uiElement = self.topLevelUIElement;
+ QCUIElement *uiElement = self;
while (uiElement && ![[uiElement role] isEqualToString:(NSString *)kAXApplicationRole]) {
uiElement = uiElement.parent;
}
return uiElement;
}
+- (QCUIElement *)menuBar {
+ return [[self application] valueForAttribute:(NSString *)kAXMenuBarAttribute];
+}
+
- (QCUIElement *)topLevelUIElement {
return [self valueForAttribute:(NSString *)kAXTopLevelUIElementAttribute];
}
@@ -86,6 +90,10 @@ - (QCUIElement *)parent {
return [self valueForAttribute:(NSString *)kAXParentAttribute];
}
+- (NSArray *)children {
+ return [self valueForAttribute:(NSString *)kAXChildrenAttribute];
+}
+
- (NSString *)title {
return [self valueForAttribute:(NSString *)kAXTitleAttribute];
}
@@ -105,20 +113,19 @@ - (BOOL)setValue:(id)value {
- (NSArray *)attributeNames {
NSArray* attributeNames;
AXUIElementCopyAttributeNames(uiElementRef, (CFArrayRef *)&attributeNames);
- return attributeNames;
+ return [(id)attributeNames autorelease];
}
- (id)valueForAttribute:(NSString *)attributeName {
id result = nil;
CFTypeRef theValue;
-
+
AXError error = AXUIElementCopyAttributeValue(uiElementRef, (CFStringRef)attributeName, &theValue);
if (error != kAXErrorSuccess) {
- NSLog(@"error in AXUIElementCopyAttributeValue for attribute %@", attributeName);
return nil;
- }
-
+ }
+
if (AXValueGetType(theValue) == kAXValueCGPointType) {
NSLog(@"unimplemented, should not be used by QuickCursor");
} else if (AXValueGetType(theValue) == kAXValueCGSizeType) {
@@ -130,7 +137,12 @@ - (id)valueForAttribute:(NSString *)attributeName {
} else if (CFGetTypeID(theValue) == AXUIElementGetTypeID()) {
result = [[[QCUIElement alloc] initWithAXUIElementRef:theValue] autorelease];
} else if (CFGetTypeID(theValue) == CFArrayGetTypeID()) {
- NSLog(@"unimplemented, should not be used by QuickCursor");
+ CFIndex count = CFArrayGetCount(theValue);
+ NSMutableArray *children = [NSMutableArray arrayWithCapacity:count];
+ for (CFIndex i = 0; i < count; i++) {
+ [children addObject:[[[QCUIElement alloc] initWithAXUIElementRef:CFArrayGetValueAtIndex(theValue, i)] autorelease]];
+ }
+ result = children;
} else {
result = [[[(id)theValue description] copy] autorelease];
}
@@ -228,4 +240,70 @@ - (BOOL)activateProcess {
return NO;
}
+- (NSString *)readString {
+ NSArray *menuBarItems = [[self menuBar] children];
+ QCUIElement *editMenu = [[[menuBarItems objectAtIndex:3] children] lastObject];
+
+ for (QCUIElement *eachMenuItem in editMenu.children) {
+ NSString *shortcut = [eachMenuItem valueForAttribute:(NSString *)kAXMenuItemCmdCharAttribute];
+
+ if ([shortcut isEqualToString:@"C"]) {
+ if ([[eachMenuItem valueForAttribute:(NSString *)kAXMenuItemCmdModifiersAttribute] isEqual:@"0"]) {
+ NSPasteboard *pboard = [NSPasteboard generalPasteboard];
+ NSString *savedString = [pboard stringForType:NSStringPboardType];
+ NSString *copiedString = nil;
+
+ if (AXUIElementPerformAction(eachMenuItem->uiElementRef, kAXPressAction) == kAXErrorSuccess) {
+ if (AXUIElementPerformAction(eachMenuItem->uiElementRef, kAXPressAction) == kAXErrorSuccess) { // twice seems neccessary to give pastboard time to record value.
+ copiedString = [pboard stringForType:NSStringPboardType];
+ }
+ }
+
+ [pboard setString:savedString forType:NSStringPboardType];
+
+ return copiedString;
+ }
+ }
+ }
+
+ return nil;
+}
+
+- (BOOL)writeString:(NSString *)pasteString {
+ NSArray *menuBarItems = [[self menuBar] children];
+ QCUIElement *editMenu = [[[menuBarItems objectAtIndex:3] children] lastObject];
+
+ for (QCUIElement *eachMenuItem in editMenu.children) {
+ NSString *shortcut = [eachMenuItem valueForAttribute:(NSString *)kAXMenuItemCmdCharAttribute];
+
+ if ([shortcut isEqualToString:@"V"]) {
+ NSString *modifiers = [eachMenuItem valueForAttribute:(NSString *)kAXMenuItemCmdModifiersAttribute];
+
+ if ([modifiers isEqualToString:@"0"]) {
+ NSPasteboard *pboard = [NSPasteboard generalPasteboard];
+ NSString *savedString = [pboard stringForType:NSStringPboardType];
+ BOOL result = NO;
+
+ [pboard setString:pasteString forType:NSStringPboardType];
+
+ if ([self activateProcess]) {
+ sleep(2);
+ NSLog(pasteString, nil);
+ if (AXUIElementPerformAction(eachMenuItem->uiElementRef, kAXPressAction) == kAXErrorSuccess) {
+ if (AXUIElementPerformAction(eachMenuItem->uiElementRef, kAXPressAction) == kAXErrorSuccess) {
+ result = YES;
+ }
+ }
+ }
+
+ [pboard setString:savedString forType:NSStringPboardType];
+
+ return result;
+ }
+ }
+ }
+
+ return NO;
+}
+
@end
View
2 QuickCursor.xcodeproj/project.pbxproj
@@ -172,6 +172,7 @@
1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = {
isa = PBXGroup;
children = (
+ 88AAF90B105014A500DFD8E5 /* StatusItemIcon.pdf */,
1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */,
);
name = "Linked Frameworks";
@@ -222,7 +223,6 @@
isa = PBXGroup;
children = (
88864E7B107A3D27001CFB7E /* CrashReporterWindow.xib */,
- 88AAF90B105014A500DFD8E5 /* StatusItemIcon.pdf */,
88AF3356107FCD190045633D /* StatusItemIcon.png */,
88B27E46104D95A000806BAE /* QuickCursor.icns */,
8D1107310486CEB800E47090 /* QuickCursor-Info.plist */,

0 comments on commit c5392a6

Please sign in to comment.