Finder file icon badging (icon overlay) on Mac OS X >= 10.6 - macos

I'm searching for a solution to do File icon overlays (icon badging) (like Dropbox does it on mac) with cocoa on Mac.
Does anyone knows a way to do this?
I've searched the Xcode docs and looked into scpplugins source code which is kind of old carbon code.

A litte bit late, but maybe will be help someone.
I solved same problem with class NSWorkspace (see setIcon:forFile:options)
Basic idea:
Try to get preview of file with QLThumbnailImageCreate (if not NULL you will get thumbnail icon)
If you didn't get thumbnail, then get default OS X icon for file (NSWorkspace iconForFile)
Combine thumbnail (or default icon) with your badge
Set new icon to the file (NSWorkspace setIcon:forFile:options)

Since the Finder was reworked in Snow Leopard, the older Carbon methods will no longer work. The route I've taken to be able to badge icons in Finder involves writing a custom bundle which then needs to be injected into the Finder.
Look into Wolf Rentzsch's mach_inject (https://github.com/rentzsch/mach_star/tree/master/mach_inject) to be able to inject a custom bundle to a Cocoa application.
Use class-dump to be able to get a look into the header files of a Cocoa application (such as the Finder in Snow Leopard and Lion) to get an idea of what you will need to override in your own bundle.

I know this is an old question.
In recent time there is a library that implement this functionality: https://github.com/liferay/liferay-nativity.

NSDockTile makes this very simple:
NSDockTile *dockTile = [NSApp dockTile];
[dockTile setBadgeLabel:#"33"];

You can use the following two methods to have icon overlay over the folders/files.
You can use -setIcon:forFile:options: method on NSWorkspace if you want to change the icon of a file or folder in Mac OS X.
However after you apply icon overlay using this method, overlay exits even though you moved that file/folder outside. This may not be the exact solution.
Instead use the Finder Sync Extension target (File - New - Target - Finder Sync Extension) inside your app.
Once you created the extension, your application doesn't have direct communication with this target. In order to activate, use AppleScript command (I don't think there is a better alternative for this.)
To activate
NSString *pluginPath = [[[NSBundle mainBundle] builtInPlugInsPath] stringByAppendingPathComponent:#"yourextension.appex"];
NSString *pluginkitString = [NSString stringWithFormat:#"pluginkit -e use -a \"%#\"", pluginPath];
system([pluginkitString cStringUsingEncoding:NSUTF8StringEncoding]);
Once target activated, there are couple of ways were our application can communicate with that extension. Few of them are:
Using NSDistributedNotificationCenter. This class provides a way to send notifications to objects in other tasks(like the extension here).
Other way is to use:
[[NSUserDefaults alloc] initWithSuiteName:#"teamid.com.company.test"];
Both your application and the target should have common group identifier(i.e "teamid.com.company.test").
For this enable "App Groups" under Target - Capabilities - App Groups and give the identifier like the above (i.e"teamid.com.company.test") were teamid is the id that you will get it from your apple developer portal. Do the same steps for your Extension target as well.
Before to conclude, be sure that extension is activated or not. To check that go to System Preference - Extensions - Your App Finder.
This is the global point were user can enable/disable icon overlay for your application.

Related

Programmatically change order of full screen apps on macOS with AppKit

I’m trying to build a macOS utility that will allow me to programmatically change the order of full screen apps on my machine.
For example, let’s say the full screen apps on my machine are currently ordered as follows from left to right:
Chrome
iTerm
Xcode
I want to be able to call a method in AppKit to say something like “Move Xcode from full screen index 2 to index 0”. However, I can't identify an AppKit API that will help me do this.
NSScreen only gives the physical displays, not the full screen apps / spaces
NSApplication only gives the list of my app's windows
CGWindowListCopyWindowInfo looked promising, but it doesn't have information about the order of full screen apps. It also doesn't allow me to get a NSWindow object to have any hope of changing the order
Is what I'm trying to do even possible with AppKit?
Thank you!
Spaces (desktops and fullscreen apps are both just spaces) are a feature of the system Window Manager and only manageable by the user through the app Mission Control.
Of course, the Window Manager needs to expose an API for management, otherwise how would Mission Control interact with it? But that API is not public.
Reverse engineered headers for those private APIs can be found online, e.g.
https://github.com/NUIKit/CGSInternal
With those headers in place, you can write code like this:
#import "CGSInternal/CGSSpace.h"
CGSSpaceID activeSpace =
CGSGetActiveSpace(_CGSDefaultConnection());
NSLog(#"activeSpace: %zu", activeSpace);
CFArrayRef spaceArray =
CGSCopySpaces(_CGSDefaultConnection(), kCGSAllSpacesMask);
NSLog(#"allSpaces: %#", (__bridge id)spaceArray);
But the usual warnings apply here:
You cannot commit code using private APIs to Apple's App Store. There is no official documentation, all that is known about that API has been reversed engineered and may not always be correct. As the API is not public, Apple can change it at any time without any previous warning and thus your working code could break with every system update.

OS X Send data to another app

I have an sandboxed OS X app that is capable to open files through Finder services and the "Open with…" option from contextual menus. I have made another sandboxed app that should send compatible files to the first app.
How can I perform an "Open with" action programmatically? I could use a service interaction too. Is that even possible? I guess I could register an URL Scheme in my first app but I'd prefer not having to release an update if everything can be done on sender app
Thx
If you just want to open a normal file, then take a look at
[[NSWorkspace sharedWorkspace] openFile:#"/path/to/file" withApplication:#"YourApp"];
You can find it here in the documentation.

Mac OS X: interacting with an application programmatically

I am working on a project where I need to call methods on an existing application (my own) and use some of its functionality. For e.g. my application ThunderBolt runs on Mac OS X 10.10. It also provides a dictionary of events that can be called externally through Apple Script or some other way that I don't know yet.
My question is what are the different (and better) ways of interacting with an application programmatically on Mac OSX? If I use something like the following code in Apple Script Editor:
tell application "ThunderBolt"
set open_file to (choose file with prompt "Choose the file you wish to parse")
set theContents to read open_file as data
set retPict to (image convert theContents)
end tell
then it is going to launch ThunderBolt with a splash screen and then call "image convert". This can be done via NSAppleScript but still it would launch the application and call methods/events on it.
Is it possible to somehow create an instance of (or get a pointer to) one of the class inside the application and use that? Something similar to COM or automation on a Windows system?
If you're working on OS X 10.10, you might consider taking a look at JavaScript for Automation (JXA).
With it you can apparently build methods into your app that can be invoked from client scripts written in JS (although I'm not yet familiar with the particulars of how to handle implementation of such a thing on the app side). But many of the apps that ship as part of OS X Yosemite have such APIs built in (e.g. iTunes and Finder).
Here's a great tutorial on JXA written by Alex Guyot: http://www.macstories.net/tutorials/getting-started-with-javascript-for-automation-on-yosemite/
The JXA-Cookbook repo also appears to be a nice resource: https://github.com/dtinth/JXA-Cookbook/wiki
Here's a brief example - this script makes iTunes go back one track. Try it while iTunes is playing (by putting the text into Script Editor, with the language option set to JavaScript, and hitting the Run button):
iTunes = Application('iTunes')
state = iTunes.playerState()
// Console msgs show up in the Messages tab of the bottom view:
console.log("playerState: " + state)
iTunes.backTrack()
Alternatively, you can place the code into a .js file and run it on the command line:
$ osascript itunes-backTrack.js
playerState: playing
The way you specify the 'tell application' is the best way, in my opinion.
What do you do with your app that needs to be called? Maybe some of the functionalities can be done with Applescript? It would simplify things a lot.

How to change retina display system preferences in OSX?

I am wondering if there is a way to programmatically change the current display mode that is set in System Preferences? Specifically, I want to be able to detect and switch between the 5 Retina display modes on the new Retina MacBook Pro.
I looked through the NSUserDefaults NSGlobalDomain to try to find how to change the current display mode in System Preferences, and couldn't find anything relating to the display at all.
Thanks.
Check out http://www.reddit.com/r/apple/comments/vi9yf/set_your_retina_macbook_pros_resolution_to/ -- uses the private API CGSGetDisplayModeDescriptionOfLength and CGSConfigureDisplayMode to set. Code's included in the kindly distributed .app bundle.
I just wrote up a lengthy post about my endeavours in trying to answer the same question - see
http://garethjenkins.com/2012/07/01/investigating-a-high-resolution-retina-utility-for-macbook-pro-1x-and-2x-modes/

How to implement "Open With" contextual menu in OS X

Apps in OS X that can open files to launch their respective applications often let the user choose the app that'll open the file. An example is the Finder.
I am still unclear about what's the best solution to implement this. The challenges are performance and showing the app's icon.
First, to get the list of apps, I found only LSCopyApplicationURLsForURL().
The major difficulty for me now is to get the app icons. All I have is the URLs (paths) to the apps. The only way I know of to get the icons would be to create a CFBundle object and then read the app's plist to get the icon data. But that appears to invole lots of disk access, and I could imagine it'll be quite inefficient if the app is located on a remote file server.
I believe that there's also a cached database about the apps, which includes icons and display names (without extension), and such. But where is the API for that?
Note: The app is Carbon based, but I can make use of Cocoa (NS...) functions if necessary. Also, Support for 10.5, even 10.4, would be appreciated.
Alright. I solved it eventually.
I now use the Carbon function GetIconRefFromFileInfo(), which still works in Lion, though it's marked deprecated/obsolete.
I had also tried to use QLThumbnailImageCreate(), but that didn't get me any results, oddly. No idea what I did wrong.
For Cocoa apps, [[NSWorkspace sharedWorkspace] iconForFile:path] can be used (thanks to C. Grunenberg of DevonTechnologies for this tip, where it's used with EasyFind).

Resources