Find the currently active Finder window/folder - objective-c

Is there any way to determine the currently active window, or a folder, in the Finder? I need this to determine, in some sense, an appropriate "default" location in which to do some particular things in my app.
Actually, does this question even make sense? Does this concept of a "currently active Finder window/folder" even exist in the first place? If it does not, I kindly ask how to get the currently selected Finder item.

Yes, the concept of the currently active Finder window does exist, as well as the currently selected item.
For example, the following AppleScript gets the selection which is the current selection in the frontmost window. Since this returns a list of files or folders even if there is a single item, the next line gets the first item out of that list (after making sure that the count of the list is greater than 0). You can then ask the Finder for the container window of the selected item, which will return a Finder window object.
tell application "Finder"
set selectedItems to selection
if ((count of selectedItems) > 0) then
set selectedItem to (item 1 of selectedItems) as alias
container window of selectedItem
end if
end tell
I'm pretty sure the code sidyll posted will work okay in 10.5 and earlier, but it errors out in 10.6 due to the inevitable changes and quirkiness that AppleScript seems to have from one version of OS X to the next.
[EDIT] Actually, I just figured out what's going on.
I usually have the Finder's Inspector window open all the time (the dynamic Get Info window you get through Command-Option-i), the upper right panel in the image below:
That image shows 3 different classes of windows:
1) The upper left, a Get Info window, is an information window, which inherits from the generic window class.
2) The upper right, an Inspector window, is a plain window.
3) The lower image shows a Finder window, which inherits from the generic window class.
If I run the following script with the setup of windows shown above:
tell app "Finder"
every window
end tell
it returns the following result:
{window "mdouma46 Info" of application
"Finder", information window "mdouma46
Info" of application "Finder", Finder
window id 1141 of application
"Finder"}
So, what was happening in my case is that, since the Inspector window is a floating utility panel, if it's currently being shown, asking the Finder for window 1 will always return the Inspector panel, since it's always floating in front of the other windows.
So the error I was getting when running the code was:
error "Can’t make «class fvtg» of
window 1 of application \"Finder\"
into type alias." number -1700 from
«class fvtg» of window 1 to alias
(In other words, the Inspector window, a plain window, doesn't have the FileViewer target (fvtg) property; only Finder windows do).
So, your code will work fine as long as the user doesn't have the Inspector window, the Preferences window, or a Get Info window that is frontmost. By changing window to Finder window, though, you can make sure that you only look at the file viewer windows that have the target property.
So, like this:
NSDictionary *errorMessage = nil;
NSAppleScript *script = [[[NSAppleScript alloc] initWithSource:
#"tell application \"Finder\"\n"
" if ((count of Finder windows) > 0) then\n"
" return (POSIX path of (target of Finder window 1 as alias))\n"
"end if\n"
"end tell"] autorelease];
if (script == nil) {
NSLog(#"failed to create script!");
return nil;
}
NSAppleEventDescriptor *result = [script executeAndReturnError:&errorMessage];
if (result) {
// POSIX path returns trailing /'s, so standardize the path
NSString *path = [[result stringValue] stringByStandardizingPath];
return path;
}

I needed to do this in a project in the past and recurred to AppleScript:
// Get path
NSAppleScript *script = [[NSAppleScript alloc] initWithSource:
#"tell application \"Finder\"\n"
" return POSIX path of (target of window 1 as alias)\n"
"end tell"];
NSDictionary *errors = nil;
NSAppleEventDescriptor *descriptor = [script executeAndReturnError:&errors];
if ((errors != nil) || (descriptor == nil)) {
// There is no opened window or an error occured
} else {
// what was retrieved by the script
path = [descriptor stringValue];
}
[script release];

Related

plist.Info so that app takes icon based on command line arguments

I have a .app. I want to edit the plist.Info such that if a command line argument of -P "main" is in the path it will use another of the icons in my resources folder. And if user right clicked and said "keep in dock" it will keep in dock with command line arguments, so on next click it will launch with same command line arguments.
Is this possible?
Worst cast scenario: Any objective-c way to check the path to see if any command line arguments there? Then I'll run setApplicationIconImage programtically (worst case meaning if the above is not possible) (and then ill also have to programtically fetch the miniaturized windows with [NSWindow miniwindowImage] and draw the mini icon on their msyelf and also listen to future notifications of NSWindowWillMiniaturizeNotification and do the draw when that notification fires, so this is worst case scenario)
I am not sure I follow you fully.
But I don't think you need to edit the plist.Info and I think it is not good to do that any way.
I would just to write to the apps preference file with CFPreferencesSetValue and change an entry which determines if the app changes it's icon.
Call made from you argument check:
[self changIcon:(CFBooleanRef)false];
-(void) changIcon:(CFBooleanRef)prefValue
{
CFStringRef appID = CFSTR("com.yourApp.BundleID");
CFStringRef Key = CFSTR("swapIcon");
CFBooleanRef Value = prefValue ;// kCFBooleanTrue;//or kCFBooleanFalse
// Set up the preference.
CFPreferencesSetValue(Key,
Value,
appID,
kCFPreferencesCurrentUser,
kCFPreferencesAnyHost);
// Write out the preference data.
CFPreferencesSynchronize(appID,
kCFPreferencesCurrentUser,
kCFPreferencesAnyHost);
}
Change the icon
NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
BOOL swapIcon = [defaults boolForKey:#"swapIcon"];
if (swapIcon ) {
NSImage * b1Image = [NSImage imageNamed:#"bl1"];
[NSApp setApplicationIconImage:b1Image];
}else {
[NSApp setApplicationIconImage:nil];//--Nil will make the app use thenormal icon
}
For a better answer you would need to explain a bit more clearly.

Single Instance Application- Activate Window - Cocoa

I have two cocoa apps. Application1 calls Application2(abc.app) as below-
if ([[NSWorkspace sharedWorkspace] respondsToSelector:#selector(launchApplicationAtURL:options:configuration:error:)])
return nil != [[NSWorkspace sharedWorkspace] launchApplicationAtURL:[NSURL fileURLWithPath:#"abc.app" isDirectory:NO] options:NSWorkspaceLaunchDefault configuration:nil error:NULL];
This should open Application2 (abc.app). Now If application 1 calls application 2 again, I want to activate abc.app (If this is minimised in the dock).I want to ensure there is single instance of abc.app running. How can we achieve this?
Not quite sure of your problem. Mac OS X by default only launches one instance of an app. (Unless you have several physical copies of the executable on disk, but even for that case there's an Info.plist key that prohibits launching an app if one with the same bundle ID is already running).
Also, by default, NSWorkspace should bring to front and un-collapse your application if it has no other windows open (it should behave as if you'd double-clicked it again in Finder, or clicked its dock icon when it's already running), and it will call the second app's 'reopen application' handler.
If it doesn't do it, you could try to explicitly un-collapse your main window from the 'reopen' delegate method, or if you don't want this to happen generally (but why wouldn't you?), you could look into sending an Apple Event between the two applications.
Also, you can check if the second application is already running by looking at the runningApplications and looking for an entry with the same bundle ID.
You can check if your second application is running and check if it's the active application (frontmost) with NSRunningApplicationclass.
// check if abc.app is running
NSArray *apps = [NSRunningApplication runningApplicationsWithBundleIdentifier:#"com.youApplication.abc"];
if ([apps count] == 0)
{
// not running, launch it
[[NSWorkspace sharedWorkspace] launchApplicationAtURL:[NSURL fileURLWithPath:#"abc.app" isDirectory:NO] options:NSWorkspaceLaunchDefault configuration:nil error:NULL];
}
// check if abc.app is frontmost
NSArray *apps = [NSRunningApplication runningApplicationsWithBundleIdentifier:#"com.youApplication.abc"];
if ([apps count])
{
// abc.app is running, check if active
if (![(NSRunningApplication*)[apps objectAtIndex:0] isActive])
{
// not active, activate it
[(NSRunningApplication*)[apps objectAtIndex:0] activateWithOptions: NSApplicationActivateAllWindows];
}
}
You can´t from App1. You can check if your App2 is launched from App2 with Notifications like here

Cocoa Document Won't Open Again After It's Closed

This is a pretty weird issue. I have a table in my Cocoa application that displays a list of recently opened files. You can double click on an entry to open the associated file. Trouble is, though, after the file is opened once, whether through the Open panel, the Recent Documents menu, or through the aforementioned table, it can't be opened again until the application has quit and re-opened. Other documents, however, can be opened, but once they're closed they can't be opened again either.
This is pretty odd behavior, and I'm not sure what's causing it. But it's certainly annoying. For reference, the Release on Closed attribute of the window from Xcode does nothing and, if selected, does not do anything. I can't think of any other attributes which might cause this behavior. For reference, here's a photo of the attributes panel:
Here's the code for the table which opens the recently opened file:
- (void)respondToRecentFileDoubleClick {
NSInteger clickedRow = [_recentFileBrowser clickedRow];
if (clickedRow != -1) { // We're in the row.
NSDocumentController *docControl = [NSDocumentController sharedDocumentController];
NSURL *selectedDocument = (NSURL *)[docControl recentDocumentURLs][clickedRow];
NSLog(#"Selected row %ld.", (long)clickedRow);
[[NSDocumentController sharedDocumentController] openDocumentWithContentsOfURL:selectedDocument display:YES completionHandler:nil];
}
}
The documentation for openDocumentWithContentsOfURL: says that the document won't be opened if it's already opened, but in this case, all of the document windows are closed, so that can't be what causes this behavior. And the NSLog() statement inside the if block prints, so I know the code is being executed.
Anyone know what might be causing this bizarre issue?
From the Xcode image, it appears that you are using your own WindowController. The default close menu item is wired up to call performClose. The performClose method is implemented in NSWindow. So, what is happening is that the window is closing, but the document is not removed from the open document list. Try adding < NSWindowDelegate> to your WindowController interface (in the .h file). Then add to your WindowController .m file:
- (void) windowWillClose:(NSNotification *)notification {
[ourdoc close];
}
Substitute whatever variable you using to hold your document reference for ourdoc. Typically the method setDocument will get called with your document reference. (Also in your WindowController.)
- (void) setDocument:(NSDocument *)document {
ourdoc = (yourNSDocumentsubclass *)document;
}
completionHandler:nil change for debugging:
completionHandler:^(NSDocument *doc, BOOL documentWasAlreadyOpened, NSError *error) {
if (documentWasAlreadyOpened) {
NSLog(#"document was already opened");
NSArray *rats = [[NSDocumentController sharedDocumentController] documents];
NSLog(#"%s seriously: %#", __PRETTY_FUNCTION__, rats);
}
}

ScriptingBridge without sdef? (cocoa)

I'd like to get properties of the currently active app. I understand that this should be possible with ScriptingBridge, however, this seems to require you generate an sdef file and import this in your project for the app you are trying to target. Since I want to target all apps, is there another way to do this?
Example of accessing system preferences:
SystemPreferencesApplication *systemPreferences =
[SBApplication
applicationWithBundleIdentifier:#"com.apple.systempreferences"];
If there's another way to access properties of any active app, please do share. (For example; window title)
Thanks.
I assume you want to run an applescript. The scripting bridge is good if you have a lot of applescript code to run. However if you only have a small amount then a simpler way is with NSApplescript.
For example if you wanted to run this applescript...
tell application "System Events"
set theProcesses to processes
repeat with aProcess in theProcesses
tell aProcess to get properties
end repeat
end tell
Then you can write it this way...
NSString* cmd = #"tell application \"System Events\"\nset theProcesses to processes\nrepeat with aProcess in theProcesses\ntell aProcess to get properties\nend repeat\nend tell";
NSAppleScript* theScript = [[NSAppleScript alloc] initWithSource:cmd];
NSDictionary* errorDict = nil;
NSAppleEventDescriptor* result = [theScript executeAndReturnError:&errorDict];
[theScript release];
if (errorDict) {
NSLog(#"Error:%# %#", [errorDict valueForKey:#"NSAppleScriptErrorNumber"], [errorDict valueForKey:#"NSAppleScriptErrorMessage"]);
return;
}
// do something with result
NSLog(#"result: %#", result);
You can get a list of every currently running Application with
NSWorkSpace.sharedWorkspace.runningApplications;
Each object in that array should be an NSRunningApplication, which you can query and manipulate freely.

CGWindowListCreate generates a hugely long list of windows

When I use CGWindowListCreate from quartz window services, it generates a very long array of window id's. I tried to turn on the option to exclude desktop elements, but I get a list of 30-40 windows even if there are only 3 or 4 of what I would call windows open.
Here is how I am doing it:
CGWindowListOption opt = 1 << 4;
CFArrayRef windowids =CGWindowListCreate(opt,kCGNullWindowID);
I am wondering what I am doing wrong that is causing this problem, and what I can do to fix it. I simply want the program to list windows created by applications, such as finder windows or browser windows, and not whatever else it is including. Thank you in advance for your help.
This will return every window whether it is on screen or off screen, you should combine it with the option kCGWindowListOptionOnScreenOnly (and also don't hardcode the one you are using). It will look like this:
CGWindowListOption opt = kCGWindowListOptionOnScreenOnly|kCGWindowListExcludeDesktopElements;
CFArrayRef windowids =CGWindowListCreate(opt,kCGNullWindowID);
That is what I gathered from the docs anyway.
I discovered a solution is to filter the window list to only those windows "below" the Dock (in terms of window layering).
The code below worked well for me. It fetches all on screen windows (excluding desktop elements). It extracts the window ID for the "Dock" window out of the list. Then fetches on screen windows again, filtering to only those windows "below" the Dock window.
// Fetch all on screen windows
CFArrayRef windowListArray = CGWindowListCreate(kCGWindowListOptionOnScreenOnly|kCGWindowListExcludeDesktopElements, kCGNullWindowID);
NSArray *windows = CFBridgingRelease(CGWindowListCreateDescriptionFromArray(windowListArray));
NSLog(#"All on screen windows: %#", windows);
// Find window ID of "Dock" window
NSNumber *dockWindowNumber = nil;
for (NSDictionary *window in windows) {
if ([(NSString *)window[(__bridge NSString *)kCGWindowName] isEqualToString:#"Dock"]) {
dockWindowNumber = window[(__bridge NSString *)kCGWindowNumber];
break;
}
}
NSLog(#"dockWindowNumber: %#", dockWindowNumber);
CFRelease(windowListArray);
if (dockWindowNumber) {
// Fetch on screen windows again, filtering to those "below" the Dock window
// This filters out all but the "standard" application windows
windowListArray = CGWindowListCreate(kCGWindowListOptionOnScreenBelowWindow|kCGWindowListExcludeDesktopElements, [dockWindowNumber unsignedIntValue]);
NSArray *windows = CFBridgingRelease(CGWindowListCreateDescriptionFromArray(windowListArray));
NSLog(#"On screen application windows: %#", windows);
}
else {
NSLog(#"Could not find Dock window description");
}