How to set an external application always being frontmost on OS X? - objective-c

I am trying to launch the built-in calculator.app on my Mac(which means it is external to my application) within my application and force the calculator to stay frontmost permanently on screen.
Here is my process. Firstly, I launch the calculator and place it frontmost temporarily.
if ([[NSWorkspace sharedWorkspace] respondsToSelector:#selector(launchApplicationAtURL:options:configuration:error:)])
[[NSWorkspace sharedWorkspace] launchApplicationAtURL:[NSURL fileURLWithPath:#"/Applications/Calculator.app/Contents/MacOS/Calculator" isDirectory:NO]
options:NSWorkspaceLaunchDefault
configuration:nil
error:NULL];
After that, I recognize the Calculator by it's owner name and try to pin Calculator.app frontmost. I was stuck here. What I would like to do is either these two ways:
1.Set an attribute to place it always frontmost. (Can't find suitable
attribute, only found attributes to resize or position)
2.Get the NSWindow of Calculator and set the level to frontmost. (Seems to be non-viable: How to convert a Carbon AXUIElementRef to Cocoa NSWindow)
But seems that both of them are not available.
CFArrayRef windowList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, kCGNullWindowID);
NSArray *arr = CFBridgingRelease(windowList);
for (NSMutableDictionary *entry in arr){
NSString *ownerName = [entry objectForKey:(id)kCGWindowOwnerName];
if([ownerName isEqualToString:#"Calculator"]){
pid_t pid = [[entry objectForKey:(id)kCGWindowOwnerPID] intValue];
AXUIElementRef appRef = AXUIElementCreateApplication(pid);
CFArrayRef windowList;
AXUIElementCopyAttributeValue(appRef, kAXWindowsAttribute, (CFTypeRef *)&windowList);
AXUIElementRef windowRef = (AXUIElementRef) CFArrayGetValueAtIndex( windowList, 0);
CFTypeRef role;
AXUIElementCopyAttributeValue(windowRef, kAXRoleAttribute, (CFTypeRef *)&role);
/*Would like to get the window of the application or assign some attribute to set Calculator frontmost*/
}
Are there any ways to achieve the two aspects I've mentioned above? Or are there any suggestions for setting an external application always being frontmost?

Related

NSWindow hide active window and focus on other

How to hide the active current NSWindow A and focus on the last other one B (one level behind the current window) and make it active?
I'm trying with this followed code but it does not work (B will become to front window but not active):
[_parentWindow orderBack:nil];
// Now i want to do some stuffs with last opened App (NSWindow) and it should be focused and activated now.
[_parentWindow orderFront:nil];
Try makeKeyAndOrderFront: instead.
[_window makeKeyAndOrderFront:nil];
Supplement to #bluedome response. You can get information about windows in the current user session:
CFArrayRef windowList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
for (NSDictionary* entry in (__bridge NSArray *)windowList) {
NSString *ownerName = [entry objectForKey:(id)kCGWindowOwnerName];
NSInteger ownerPID = [[entry objectForKey:(id)kCGWindowOwnerPID] integerValue];
NSString *windowName = [entry objectForKey:(id)kCGWindowName];
NSLog(#"%#:%ld, %#", ownerName, (long)ownerPID, windowName);
}

Get the ProcessList like from cmd+tab

I want to get the applications from the cmd+tab menu in OS X. The best way I got now is to associate this with a AppleScript call with the following:
NSDictionary *errorDict;
NSAppleEventDescriptor *returnValue;
NSString *appleScriptText = #"tell application \"System Events\" to get name of (processes where background only is false)";
NSAppleScript *script = [[NSAppleScript alloc] initWithSource:appleScriptText];
Then loop through the stuff coming back from it and to match it to the [[NSWorkspace sharedWorkspace] runningApplications] but this seems a little bit too weird to get this task done.
So my question here: is there a way that is not so quirky like this one?
I am really tense for the answers.
Given that you're already familiar with -[NSWorkspace runningApplications], why don't you just iterate over those and check which ones meet your criteria? The background only property corresponds to NSRunningApplication's activationPolicy property being something other than NSApplicationActivationPolicyRegular.
So, something like (not tested):
NSArray* apps = [[NSWorkspace sharedWorkspace] runningApplications];
NSIndexSet* indexes = [apps indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop){
return [obj activationPolicy] == NSApplicationActivationPolicyRegular;
}];
NSArray* names = [[apps objectsAtIndexes:indexes] valueForKey:#"localizedName"];

How to check CGWindowID still valid

If I am getting a CGWindowID (_windowID) as follows...
NSArray *windowList = (__bridge NSArray *)CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
for (NSDictionary *info in windowList) {
if ([[info objectForKey:(NSString *)kCGWindowOwnerName] isEqualToString:#"window name"] && ![[info objectForKey:(NSString *)kCGWindowName] isEqualToString:#""]) {
_windowID = [[info objectForKey:(NSString *)kCGWindowNumber] unsignedIntValue];
}
}
How do I properly test the window id is still valid and the window has not been closed? Do i just run similar code just checking window id exists?
Thanks in advance
The documentation for the kCGWindowListOptionOnScreenOnly constant says:
List all windows that are currently onscreen. Windows are returned in
order from front to back. When retrieving a list with this option, the
relativeToWindow parameter should be set to kCGNullWindowID.
So the windows would certainly be on screen, since nothing appears to be happening between the call to CGWindowListCopyWindowInfo and your action on it.
Maybe you'd want to test to make sure they are not hidden or minimized?

Getting AppleScript return value in Obj-C

I'm using some AppleScript in my Obj-C cocoa project to control QuickTime player (play, pause, stop, jog forward and back etc.) with great success, though my knowledge of AppleScript is very limited.
However, what I want most of all is the movie's 'Current Time' offset to convert into time-stamps for writing a subtitle script.
The following simple method shows the precise current position in (float) seconds in a dialog, but I'd really like the AppleScript to return me a variable that I can use in the rest of app. How could I modify the code below to do that? Is it even possible to access this value? Thanks a million in advance :-)
-(IBAction)currentPlayTime:(id)sender
{
NSString *scriptString=[NSString stringWithFormat:
// get time of current frame... (works perfectly)!
#"tell application \"QuickTime Player\"\n"
#"set timeScale to 600\n"
#"set curr_pos to current time of movie 1/timeScale\n"
#"display dialog curr_pos\n" // ...not in a practical form to use
#"end tell\n"];
NSDictionary *errorDict= nil;
NSAppleScript *appleScriptObject=[[NSAppleScript alloc] initWithSource:scriptString];
NSAppleEventDescriptor *eventDescriptor=[appleScriptObject executeAndReturnError: &errorDict];
// handle any errors here (snipped for brevity)
[appleScriptObject release]; // can I retain this?
}
Here's the appropriate AppleScript that you'd want to run:
property timeScale : 600
set currentPosition to missing value
tell application "QuickTime Player"
set currentPosition to (current time of document 1) / timeScale
end tell
return currentPosition
In case you're not familiar with it, property is a way to specify a global variable in AppleScript. Also, missing value is the AppleScript equivalent of nil in Objective-C. So, this script first defines a variable named currentPosition, and sets the value to missing value. It then enters the tell block which, if it succeeds, will alter the currentPosition variable. Then, outside of the tell block, it returns the currentPosition variable.
In the Objective-C code, when you create an NSAppleScript with the above code, its -executeAndReturnError: method will return the currentPosition variable in an NSAppleScriptEventDescriptor.
-(IBAction)currentPlayTime:(id)sender {
NSDictionary *error = nil;
NSMutableString *scriptText = [NSMutableString stringWithString:#"property timeScale : 600\n"];
[scriptText appendString:#"set currentPosition to missing value\n"];
[scriptText appendString:#"tell application \"QuickTime Player\"\n "];
[scriptText appendString:#"set currentPosition to (current time of document 1) / timeScale\n"];
[scriptText appendString:#"end tell\n"];
[scriptText appendString:#"return currentPosition\n"];
NSAppleScript *script = [[[NSAppleScript alloc] initWithSource:scriptText] autorelease];
NSAppleEventDescriptor *result = [script executeAndReturnError:&error];
NSLog(#"result == %#", result);
DescType descriptorType = [result descriptorType];
NSLog(#"descriptorType == %#", NSFileTypeForHFSTypeCode(descriptorType));
// returns a double
NSData *data = [result data];
double currentPosition = 0;
[data getBytes:&currentPosition length:[data length]];
NSLog(#"currentPosition == %f", currentPosition);
}
You can extract the contents of the NSAppleEventDescriptor as shown above.
Using the Scripting Bridge framework does have a slight learning curve, but would allow working with native types such as NSNumbers rather than having to go the somewhat "messier" route of extracting the raw bytes out of AppleEvent descriptor.
Use Scripting Bridge. This is a bridge between AppleScript and Objective-C, and other applications (e.g. QuickTime Player) is represented as an Objectve-C object in your code. So, you don't have to construct AppleScript code by hand.
Some say AppScript is better than Scripting Bridge.
NSAppleEventDescriptor has some methods to convert to some objective-C types, if you go to my site and download the NDScript project, it has a category of NSAppleEventDescriptor which adds a lot more methods for coercion to Objective-C type. You can use that category without the rest of the project.
http://homepage.mac.com/nathan_day/pages/source.xml

Cocoa Button Launch outside Application

I have the following setup:
A grid of 4x4 (16 total) buttons (standard NSButton buttons) in an NSWindow.
The NSWindow will come to the front when I press a hotkey combination (DDHotKey)
Now, what I'd like to do is give my buttons the following functionality:
When the button is clicked, open a dialog that shows the /Applications/ directory and allow me to select any of the applications listed there.
When the application is selected store it in a variable (I'm guessing) (or string?) and make it so that when the buttons Key Equivalent is pressed, that application launches
I'm looking around and I'm not exactly sure what to do or really where to begin looking...any clues?
I have this in my appdelegate.m file:
- (void)openDoc:(id)sender
{
int result;
NSArray *fileTypes = [NSArray arrayWithObject:#"td"];
NSOpenPanel *oPanel = [NSOpenPanel openPanel];
[oPanel setAllowsMultipleSelection:YES];
result = [oPanel runModalForDirectory:NSHomeDirectory()
file:nil types:fileTypes];
if (result == NSOKButton) {
NSArray *filesToOpen = [oPanel filenames];
int i, count = [filesToOpen count];
for (i=0; i<count; i++) {
NSString *aFile = [filesToOpen objectAtIndex:i];
id currentDoc = [[ToDoDoc alloc] initWithFile:aFile];
}
}
}
How do I link the button to it?
You can use an NSOpenPanel to choose the application.
Then to launch the application, take a look at this stack overflow question.
store the path to application, then when you want to open them. You can use the system() function.
system("open -a /Applications/someApplication.app");