Applescript and Cocoa - cocoa

I would like to call a IBAction within a cocoa app from applescript:
I want to call:
- (IBAction)reverse:(id)pId;
{
direction = -1;
}
with a line in an external applescript file like:
tell application "theapp"
reverse
end tell
Any Ideas?
Thanks in Advance

Use NSAppleScript.
NSAppleScript *as = [[NSAppleScript alloc] initWithSource:#"tell application \"theapp\"\nreverse\nend tell"];
NSDictionary *err = nil;
NSAppleEventDescriptor *desc = [as executeAndReturnError:&err];
NSLog(#"Error: %#\nData: %#", err, desc.data);
[as release];
There is also a good answer about Scripting Bridge here

Related

How do I create an AppleScript file using NSAppleScript?

My code looks like this:
NSMutableString* cmd = [NSMutableString new];
[cmd appendString:#"script createScriptFile\n"];
[cmd appendString:#"beep\n"];
[cmd appendString:#"end script\n"];
[cmd appendString:#"store script createScriptFile in \"/tmp/narg.scpt\"\n"];
NSAppleScript* script = [[NSAppleScript alloc] initWithSource:cmd];
NSDictionary* err = nil;
NSAppleEventDescriptor *result = [script executeAndReturnError:&err];
which runs fine without errors, but the resulting script file (narg.scpt) looks like this:
script createScriptFile
beep
end script
store script createScriptFile in "/tmp/narg.scpt"
and what I want is for narg.sctp to look like this:
beep
What am I doing wrong?
Thanks,
Chris

How to predefine Apple Script constants or variables

Is it possible to predefine constant or variable for an AppleScript in cocoa application?
in other words is the function "addConstantToAppleScript" (used in the following code) definable?
addConstantToAppleScript("myText", "Hello!");
char *src = "display dialog myText";
NSString *scriptSource = [NSString stringWithCString:src];
NSAppleScript *appleScript = [[NSAppleScript alloc] initWithSource:scriptSource];
NSDictionary *scriptError = [[NSDictionary alloc] init];
[appleScript executeAndReturnError:scriptError];
thanks.
If you want to prepend an NSDictionary of key/value pairs to the beginning of an NSString containing AppleScript you could use something like the following function. Personally I would do this as a category on NSString but you have asked for a function.
NSString *addConstantsToAppleScript(NSString *script, NSDictionary *constants) {
NSMutableString *constantsScript = [NSMutableString string];
for(NSString *name in constants) {
[constantsScript appendFormat:#"set %# to \"%#\"\n", name, [constants objectForKey:name]];
}
return [NSString stringWithFormat:#"%#%#", constantsScript, script];
}
This function converts the key/value pairs to AppleScript statements of the form set <key> to "<value>". These statements are then added to the front of the supplied script string. The resulting script string is then returned.
You would use the above function as follows:
// Create a dictionary with two entries:
// myText = Hello\rWorld!
// Foo = Bar
NSDictionary *constants = [[NSDictionary alloc ] initWithObjectsAndKeys:#"Hello\rWorld!", #"myText", #"Bar", #"Foo", nil];
// The AppleScript to have the constants prepended to
NSString *script = #"tell application \"Finder\" to display dialog myText";
// Add the constants to the beginning of the script
NSString *sourceScript = addConstantsToAppleScript(script, constants);
// sourceScript now equals
// set Foo to "Bar"
// set myText to "Hello\rWorld!"
// tell application "Finder" to display dialog myText
NSAppleScript *appleScript = [[NSAppleScript alloc] initWithSource:sourceScript];

How do you wait for an application to close in OS X?

I am using the code below to check if an application is running and close it. Can someone provide an example of how to request an application calose and wait for it to close before proceeding?
+ (BOOL)isApplicationRunningWithName:(NSString *)applicationName {
BOOL isAppActive = NO;
NSDictionary *aDictionary;
NSArray *selectedApps = [[NSWorkspace sharedWorkspace] runningApplications];
for (aDictionary in selectedApps) {
if ([[aDictionary valueForKey:#"NSApplicationName"] isEqualToString: applicationName]) {
isAppActive = YES;
break;
}
}
return isAppActive;
}
+ (void)stopApplication:(NSString *)pathToApplication {
NSString *appPath = [[NSWorkspace sharedWorkspace] fullPathForApplication:pathToApplication];
NSString *identifier = [[NSBundle bundleWithPath:appPath] bundleIdentifier];
NSArray *selectedApps = [NSRunningApplication runningApplicationsWithBundleIdentifier:identifier];
// quit all
[selectedApps makeObjectsPerformSelector:#selector(terminate)];
}
You can use Key-Value Observing to observe the terminated property of each running application. This way, you'll get notified when each application terminates, without having to poll.
One way would be to periodically call isApplicationRunningWithName on a timer, and wait until that function returns NO.
The commandline timelimit will let you send a close signal to an app, wait x seconds, then kill it (or send any other signal you like, kill is -9) if hasn't obeyed the "warning" signal.
(Note: I haven't tried compiling it on Mac, but I believe it's fairly POSIX-compliant code and not Linux-specific as it runs on BSD and others.)

Xcode console, clear screen programmatically

I'm kinda new to Xcode and even programming.
From Xcode, in my code, how do I show the console and clear the screen?
I know I could do it with the Xcode preferences, but I would like to do it programmatically.
This works for me - leave out the last activate part if you wish Xcode to stay on top of your app:
bool ClearXCodeDebuggerConsole()
{
NSString *const scriptText = #"\
tell application \"System Events\"\n\
set currentapp to the name of the current application\n\
end tell\n\
tell application \"Xcode\" to activate\n\
tell application \"System Events\"\n\
keystroke \"r\" using {command down, control down, option down}\n\
end tell\n\
tell application currentapp to activate\n\
return true";
NSAppleScript *script = [[[NSAppleScript alloc] initWithSource:scriptText] autorelease];
[scriptText release];
NSDictionary *dictError = nil;
NSAppleEventDescriptor *result = [script executeAndReturnError:&dictError];
if (!result) return false;
if ([result booleanValue] != YES) return false;
return true;
}
You can display the console window by pressing Shift + Command + R. You can clear the console window by pressing Control + Option + Command + R. Both options are available from the Run menu.

Getting URL From beginSheetModalForWindow:

I'm using an OpenPanel to get a file path URL. This works:
[oPanel beginSheetModalForWindow:theWindow completionHandler:^(NSInteger returnCode)
{
NSURL *pathToFile = nil;
if (returnCode == NSOKButton)
pathToFile = [[oPanel URLs] objectAtIndex:0];
}];
This doesn't, resulting in an 'assignment of read-only variable' error:
NSURL *pathToFile = nil;
[oPanel beginSheetModalForWindow:theWindow completionHandler:^(NSInteger returnCode)
{
if (returnCode == NSOKButton)
pathToFile = [[oPanel URLs] objectAtIndex:0];
}];
return pathToFile;
In general, any attempt to extract pathToFile from the context of oPanel has failed. This isn't such a big deal for small situations, but as my code grows, I'm forced to stuff everything -- XML parsing, core data, etc -- inside an inappropriate region. What can I do to extract pathToFile?
Thanks.
This doesn't, resulting in an 'assignment of read-only variable' error:
NSURL *pathToFile = nil;
[oPanel beginSheetModalForWindow:theWindow completionHandler:^(NSInteger returnCode)
{
if (returnCode == NSOKButton)
pathToFile = [[oPanel URLs] objectAtIndex:0];
}];
return pathToFile;
Yes, because you're trying to assign to the copy of the pathToFile variable that gets made when the block is created. You're not assigning to the original pathToFile variable that you declared outside the block.
You could use the __block keyword to let the block assign to this variable, but I don't think this will help because beginSheetModalForWindow:completionHandler: doesn't block. (The documentation doesn't mention this, but there's no reason for the method to block, and you can verify with logging that it doesn't.) The message returns immediately, while the panel is still running.
So, you're trying to have your completion-handler block assign to a local variable, but your method in which you declared the local variable will probably have returned by the time block runs, so it won't be able to work with the value that the block left will leave in the variable.
Whatever you do with pathToFile should be either in the block itself, or in a method (taking an NSURL * argument) that the block can call.
you can also runModal after you begin the sheet you just need to make sure you end the sheet later. This way you don't have to bend to apple's will, it isn't deprecated and it should still work perfectly.
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
[openPanel beginSheetModalForWindow:window completionHandler:nil];
NSInteger result = [openPanel runModal];
NSURL *url = nil;
if (result == NSFileHandlingPanelOKButton)
{
url = [openPanel URL];
}
[NSApp endSheet:openPanel];
It seems a little bit like black magic coding but it does work.

Resources