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"];
Related
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?
NSDictionary *error = nil;
//AppleScript to get all running windows
NSAppleScript *appleScriptFindWindows = [[NSAppleScript alloc] initWithSource:
#"tell application \"System Events\" to get the title of every window of process \"TestWindow\" whose name contains \"Black\" end tell"];
while (true) {
#autoreleasepool {
//Execute and get the result of the OSAScript
NSAppleEventDescriptor *result = [appleScriptFindWindows executeAndReturnError:&error];
//Convert the result to a string
NSString *windowNames = [NSString stringWithFormat:#"%#", result];
error = nil;
sleep(0.25);
}
}
I know I am not currently doing anything with result but I will do once I have the issue fixed.
I will be monitoring various windows/files using applescript on a continuous loop, however I have noticed that when I run this code my memory usage skyrockets at 12mb/s and energy impact is high. I cannot release or de-alloc AppleEventDescriptor because of arc.
Is there a way to release the event descriptor or perhaps I am missing something in the applescript itself to correctly exit after execution?
I am a bit lost on this one and being new to obj-c I am wondering if there is a better way to execute applescript within obj-c if that is the issue.
I am working on Mac OS X. How do I find out names of running applications in foreground as well as in background?
You can get all running applications with NSWorkSpace
NSArray *runningApplications = [[NSWorkspace sharedWorkspace] runningApplications];
The array contains instances of NSRunningApplication
Assuming you mean hidden with background and visible with foreground - the equivalent to the ⌘H shortcut in an application menu – you can filter the array with a predicate
NSPredicate *visiblePredicate = [NSPredicate predicateWithFormat:#"hidden == NO"];
NSPredicate *hiddenPredicate = [NSPredicate predicateWithFormat:#"hidden == YES"];
NSArray *visibleApplications = [runningApplications filteredArrayUsingPredicate:visiblePredicate];
NSArray *hiddenApplications = [runningApplications filteredArrayUsingPredicate:hiddenPredicate];
NSLog(#"%#", visibleApplications);
NSLog(#"%#", hiddenApplications);
To determine which application is currently active, filter with the active property.
NSPredicate *activePredicate = [NSPredicate predicateWithFormat:#"active == YES"];
NSArray *activeApplications = [runningApplications filteredArrayUsingPredicate:activePredicate]; // actually it's always an array containing one application
NSLog(#"%#", activeApplications[0]);
I've been working on adding icloud to my project (which is quite a pain in the buns) and I'm able to save and remove files, but I can't get a list of the files stored in iCloud. I've tried solutions from about 10 different websites (including the Apple documentation). Whenever I call [self.query startQuery]; everything seems to be working: The correct methods get called, the methods execute exactly as they should. Then when I ask for an nsarray of the files in my app's iCloud Documents directory I get two parenthesis with nothing between them (when I view in NSLog): File List: ( ). I know for a fact that there are many different documents of all shapes, extensions, sizes, and names in my app's iCloud Documents directory because I've been using the iCloud Developer site to check if things are working. My first method to setup the query is as follows:
- (void)syncWithCloud {
self.query = [[NSMetadataQuery alloc] init];
NSURL *mobileDocumentsDirectoryURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
[query setSearchScopes:[NSArray arrayWithObjects:NSMetadataQueryUbiquitousDocumentsScope, nil]];
//[query setPredicate:[NSPredicate predicateWithFormat:#"%K LIKE '*'", NSMetadataItemFSNameKey]];
[query setPredicate:[NSPredicate predicateWithFormat:[NSString stringWithFormat:#"%%K like \"%#*\"", [mobileDocumentsDirectoryURL path]], NSMetadataItemPathKey]];
//Pull a list of all the Documents in The Cloud
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(processFiles:)
name:NSMetadataQueryDidFinishGatheringNotification object:self.query];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(processFiles:)
name:NSMetadataQueryDidUpdateNotification object:self.query];
[self.query startQuery];
}
The process files method provided by Apple is next:
- (void)processFiles:(NSNotification*)aNotification {
NSMutableArray *discoveredFiles = [NSMutableArray array];
//Always disable updates while processing results.
[query disableUpdates];
//The query reports all files found, every time.
NSArray *queryResults = [query results];
for (NSMetadataItem *result in queryResults) {
NSURL *fileURL = [result valueForAttribute:NSMetadataItemURLKey];
NSNumber *aBool = nil;
// Don't include hidden files.
[fileURL getResourceValue:&aBool forKey:NSURLIsHiddenKey error:nil];
if (aBool && ![aBool boolValue])
[discoveredFiles addObject:fileURL];
}
//Update the list of documents.
[FileList removeAllObjects];
[FileList addObjectsFromArray:discoveredFiles];
//[self.tableView reloadData];
//Reenable query updates.
[query enableUpdates];
NSLog(#"File List: %#", FileList);
}
Why doesn't this give me a list of files or at least some kind of data? Am I defining the NSMetadata query wrong, maybe my predicate isn't formatted right? I know I'm doing something wrong because there's no way iCloud could be this complicated (or could it?).
Thanks for the help in advance!
Edit #1: I am continuing to try different approaches to this problem. Using one of the answers below I have changed the predicate filter as follows:
[query setPredicate:[NSPredicate predicateWithFormat:#"NSMetadataItemFSNameKey LIKE '*'"]];
I have also added the following lines before the [query enableUpdates] call:
for (NSMetadataItem *item in query.results) {
[FileList addObject:[item valueForAttribute:NSMetadataItemFSNameKey]];
}
In the processFiles method, I've tried placing all of the code on the background thread, but this makes no difference - as a matter of fact, when the code is not executed on the background thread FileList gives me this (null) instead of this ( ).
Could my problem have to do with thread management or memory allocation? Please note that I am using ARC.
Edit #2: The FileList variable is an NSMutableArray defined in my #interface and initialized in the -(id)init method before calling the processFiles method.
Edit #3: When testing my code with breakpoints I found that the following for-loop never gets run through - not even once. This leads me to believe that:
A. The proper directory isn't being connected with
B. iCloud can't see the files in the directory
C. My NSMetadataQuery isn't being setup properly
D. Something completely different
Here's the code that starts the for-loop which never gets run:
NSArray *queryResults = [query results];
for (NSMetadataItem *result in queryResults) {
Since you already set the search scope correct, there's no need to use special filters in the predicate.
Just use:
query.predicate = [NSPredicate predicateWithFormat:#"NSMetadataItemFSNameKey == '*'"];
And to get the array use:
NSMutableArray *array = [NSMutableArray array];
for (NSMetadataItem *item in query.results) {
[array addObject:[item valueForAttribute:NSMetadataItemFSNameKey]];
}
I've solved my problem. Getting the list of files in iCloud was just a matter of correctly defining, allocating, and initializing properties. SAE's answer and this SO posting helped me solve my problem and create this GitHub Repo called iCloud Document Sync. The iCloud Document Sync class simplifies the whole iCloud Document storage process down to a few lines of code. The commit linked here fixes the issues from my question.
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:¤tPosition 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