Writing a Snow Leopard Service for Finder.app
Asked Answered
C

1

7

I am currently looking into solving the problem with the inability to quickly create new files in the Finder. I will open source what I write because I think the Mac community needs this solved.

On Windows, you can right-click, create new text file. OS X, you should be able to do this with a service which would work like this:

  • Right-Click > Services > Create New Text File

Writing a Finder Service in Snow Leopard is theoretically the way to accomplish this, although I haven't been able to find any sample code. (I'll admit I've only looked at the documentation very briefly).

I'm not sure how to get started, whether Apple provide a Services template in Xcode. Basically I'm looking for help with getting a working services project running. The implementation code should then be rather trivial for me to write in Obj-C. So what can I do to create a new working Services project? If i'm wrong about this then tell me the right way to do this and please provide sample code or some steps to get me started.

Edit: Trust me guys, I'm not an OS X noob. Have tried many apps to achieve work arounds: PathFinder, Automator, Terminal,etc and I'm happy with none of them.

I want to create a right-clickable menu item for creating New files, the same as Windows has. If this API doesn't let me do this, then I will modify system files if necessary. But I would rather do it in such a way that doesn't require me hack OS X.

The sad fact is that Apple disabled 3rd party contextual menu items when Snow Leopard was released and devs weren't happy. You could create services under the contextual menu with Automator, but it was very limited.

Yes, Quicksilver is the way I create files at the moment, unless I'm in the terminal when I touch ~/Desktop/file.txt or wherever.

If you cannot answer my question by providing source code for an Xcode project for writing a Service, please keep your opinions on how I should be using my computer to yourself. At any rate, I think I will probably be answering my own question after I go and implement this myself.

Cateyed answered 17/9, 2010 at 1:40 Comment(0)
B
15

Read Services Implementation Guide. If you want a working sample code, see this project I cooked up. As explained in the guide, what you need to do is to install the service handler:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    [NSApp setServicesProvider:self];
    NSUpdateDynamicServices();
}

- (void)handleServices:(NSPasteboard *)pboard
          userData:(NSString *)userData 
         error:(NSString **)error {
    if([[pboard types] containsObject:NSFilenamesPboardType]){
        NSArray* fileArray=[pboard propertyListForType:NSFilenamesPboardType];
            // do something...
    }
}

and advertise it in your Info.plist:

<key>NSServices</key>
<array>
    <dict>
        <key>NSMenuItem</key>
        <dict>
            <key>default</key>
            <string>Service Handling Demo</string>
        </dict>
        <key>NSMessage</key>
        <string>handleServices</string> <!-- This specifies the selector -->
        <key>NSPortName</key>
        <string>services</string>       <!-- This is the name of the app -->
        <key>NSSendTypes</key>
        <array>
            <string>NSFilenamesPboardType</string>
        </array>
    </dict>
</array>

It's as easy as this! You might need to manually turn on the service entry in the Keyboard Shortcut section of the System Preferences.app. If you want to turn it on automatically, you can write into pbs.plist inside Library/Preferences/, but that's not a documented way of doing things.

The problem is that Finder doesn't show this service item when you right-click an empty region in the Finder window. You can't do anything with it, other than injecting the code. This is inherent in the Finder's support of the service system. If you want to change that behavior, you need to inject code inside Finder.app. That's not that hard. On Snow Leopard, it's standard to use the OSAX loading trick, described at e.g. in this blog post. Then you can use Objective-C runtime functions to patch the behavior of the right-clicking of the Finder, by changing the methods described in this Apple document. (I don't know which of the method Finder uses to respond to the right-click event, though.)

Instead, if you're OK with clicking a button on the toolbar of the Finder window instead of right-clicking, you can add a button, as in this utility cd-to. This uses the ability to put an icon of the app to the Finder toolbar. The app itself just reads the path of the frontmost Finder window via Apple events, and opens a Terminal window for that. I think you can tweak the code of this app to do what you want.

Here follow subjective stuffs:

Honestly, you don't have to use Objective-C to write a Finder service if you just want to make a new file. Automator can do the same thing with a shell script and/or Applescript.

If you want to manage files efficiently on Mac, there are already great utilities around: try Butler or launchbar or Quicksilver, for example.

Baleen answered 17/9, 2010 at 4:5 Comment(12)
Automator is limited. It does not let you create an action for creating a new text file. I forget the details exactly but I tried this some time ago and was unable to achieve this. Anyway, I don't want to use Automator. I want to do this in code.Cateyed
@Yuji: "Open the app from the Dock or the Spotlight." That is not one step. (1) Open app, (2) Command+S (3) Drag the proxy of the finder window to the App save dialog. Sometimes i have to Alt-Tab depening on window sizes and what apps are open (4) Press enter to save file. ---- At least 4 steps.Cateyed
Did you try "Run shell script" action in Automator? You can do whatever you want with it. The Automator passes the folder name to the shell script. A shell script is a code, right? Unfortunately you can't right-click the empty part of the Finder window to invoke that service; instead you need to right-click a folder in which you want to create a file. That restriction is, I think, not a limitation of Automator but is a limitation of the service system itself.Baleen
As for your second comment, you first decide in which directory you make a file and the name? Why don't you work on the Untitled file for a while? Do you immediately do (2), (3) and (4) when you open an app? Well everyone has his/her way...Baleen
I did try the Shell script I had an issue getting the path for the currently open (and focussed) Finder window. I remember needing to write apple script or something. A buildable plugin is a much cleaner way to do this anyway. If you can't answer my question that's okay .Cateyed
Right-click? Use a mouse? How retro! I think I'll stick to a keystroke or two in Quicksilver or LaunchBar.Thickknee
I've been a Mac user for 6 years and an Objective-C programmer for 3 years. I'm not new, I just get sick of not having the ability to right click to create a new file (especially when i'm on a Mac that doesn't have Quicksilver installed. I just think that the Mac community need a solution to this problem even if Apple don't want you to do it.Cateyed
@Yuji, I tried to download the code, the link is broken. I am trying to find out if I can put an option in the Finder right click context menu and have an action performed by my Cocoa app when the user clicks that option. Please let me know what you think. I am using Mountain Lion and the latest Xcode to write an app for the mac. Thanks for the help!Kwangju
@ShumaisUlHaq I added the code in another server. I'm sure the code won't work anymore. You should ask your question as a separate, new question, not as a comment here. That way, you might get new answers.Baleen
@Baleen thanks for uploading the project, I really appreciate it. The code works, but I have to enable the services option from within the keyboard shortcut settings. I am trying to add an option like the one that Droplr and other file sharing services add, the problem is that they are auto enabled. I am going to have a look at the pbs.plist thing that you mentioned, I am not sure whether that would be acceptable for the AppStore though. I didnt create a separate question since I got most of what I needed from your answer and just needed the code to verify things :) You have been of great help!Kwangju
@Baleen I have tried to port your solution to Swift2 but with no luck. I opened a bounty here: #31404526 Would be great if you have an idea.Naseberry
It's important to also add a "NSRequiredContext" entry, even if it's empty, to the NSServices entry, or the service remains disabled on recent OSX versions. See here: https://mcmap.net/q/1478118/-how-do-i-automatically-activate-an-item-in-the-os-x-services-menuCollinsworth

© 2022 - 2024 — McMap. All rights reserved.