Internal commands in Quicksilver are essentially actions that don’t require a subject (direct object) defined by the user. Most tasks, such as “Force Catalog Rescan”, and some commands like “QS Preferences” are internal commands. The Clipboard plugin also adds a large number of internal commands so as you can see, they’re quite useful in some circumstances.
![Internal commands in the Quicksilver catalog [Quicksilver internal commands screenshot]](http://dev.lipidity.com/wp-content/uploads/2007/06/quicksilver-internal-commands1.jpg)
Internal commands are found in the Quicksilver section of the Catalog.
There’s absolutely no documentation on how to write an internal command for Quicksilver anywhere and figuring out the internal workings of the application can be frustrating at best. This tutorial aims to fill that void and, like some of my other Quicksilver-related articles, provide you with the knowledge and resources to make a working internal command.
Prerequisites
An internal command is written much like a standard Quicksilver action, so the only thing required is the Xcode project template for creating Quicksilver plugins. (However, if you really need another project template for writing internal commands, just ask)
It would also be a good idea to familiarize yourself with writing a Quicksilver action before going on.
Info.plist keys
Somewhere in the plist, you need to set QSLoadImmediately to true for Quicksilver to load your internal command properly. That’s simply:
<key>QSLoadImmediately</key>
<true/>
To register our internal command with Quicksilver, we need to add QSInternalMessages to the QSRegistration key. So, find the QSRegistrationTemplate key, rename it to QSRegistration, and configure the command in the following manner:
<key>QSRegistration</key>
<dict>
<key>QSInternalMessages</key>
<dict>
<key>testInternalCommand</key>
<dict>
<key>target</key>
<string>testCommand</string>
<key>action</key>
<string>doTestCommand:</string>
<key>name</key>
<string>Just a Test</string>
<key>icon</key>
<string></string>
</dict>
...
What do the keys mean?
- target
-
The class that will be running the internal command. This needs to be a singleton. More on that later.
- action
-
An instance method that the target class responds to; this is the command.
- name
-
A brief and meaningful name for your internal command.
- icon
-
An icon for your command. You can use an identifier like
com.blacktree.Quicksilverorcom.apple.AddressBookand Quicksilver will load up the appropriate icon for you.
The class
As I mentioned earlier, the class that runs the command should be a singleton - that means only one copy of the class is instantiated and used (Apple’s documentation on singletons). The internal command will be called like: [[targetClass sharedInstance] doActionMethod:nil] where targetClass is the class you specified under target and doActionMethod: is the method for the key action in the Info.plist.
From the example Info.plist excerpt given above, the class that would be written would look something like this:
// testCommand.h
#import <QSCore/QSActionProvider.h>
@interface testCommand : QSActionProvider
{
}
+ (id)sharedInstance;
-(IBAction)doTestCommand:(id)sender;
@end
// testCommand.m
#import "testCommand.h"
@implementation testCommand
// Following few methods to make this class a singleton
static testCommand *sharedTestCommandManager = nil;
+ (id)sharedInstance {
if (sharedTestCommandManager == nil) {
[[self alloc] init]; // assignment not done here
}
return sharedTestCommandManager;
}
+ (id)allocWithZone:(NSZone *)zone{
// @synchronized(self) {
if (sharedTestCommandManager == nil) {
sharedTestCommandManager = [super allocWithZone:zone];
return sharedTestCommandManager; // assignment and return on first allocation
}
// }
return nil; //on subsequent allocation attempts return nil
}
- (id)copyWithZone:(NSZone *)zone{
return self;
}
- (id)retain{
return self;
}
- (unsigned)retainCount{
return UINT_MAX; //denotes an object that cannot be released
}
- (void)release{
//do nothing
}
- (id)autorelease{
return self;
}
// Method to perform the internal command
-(IBAction)doTestCommand:(id)sender
{
NSLog(@"Running test internal command...");
// do something useful
}
@end
And that’s it. Possibly the shortest ever Quicksilver development tutorial you’re going to see in a while.
I thought I’d drive home the usefulness of internal commands by showing off a little something I’ve been playing with for a while.
![Quicksilver cube interface smoking [Quicksilver Cube Smoking]](http://dev.lipidity.com/wp-content/uploads/2007/06/quicksilver-cube-disco-smoking.png)
A simple internal command allows any interface to smoke
Yeah, it’s the cube interface. Smoking.
In fact, with this internal command, I can make anything smoke.
Summary
- Add QSLoadImmediately key, set to true
- Add QSInternalMessages to the QSRegistration key
- Write singleton class that runs the command
As always, I’ll be thrilled to see what you come up with.
2 Comments so far
Leave a commentWould you mind sharing your smoke plugin? I’d love to be able to make my flame-colored cube smoke.
composed by Casey on December 4, 2007 3:53 am | Permalink
Casey, see here
disclosed by Ankur on December 4, 2007 8:51 am | Permalink
Leave a comment