One of the oldest inter-application communication technologies in Mac OS X is provided by the Services menu. Actually, services have been there since the very first version of the OS, when it was still called NeXTStep:
The services facility allows an application to make use of the services of other applications without knowing in advance what those services might be.
For example, a text editing application lets the system know that it's willing to" provide plain ASCII text or rich text (RTF) on the pasteboard any time there is a selection. Any service-providing application is then able to receive that text and act upon it. A service-providing application could thus provide such services as grammar checking, encryption, reformatting, language translation, conversion to speech, or any number of useful functions. Service-providing applications can also place data back on the pasteboard to be received by the main application.
In this way, data can be seamlessly exchanged between applications, and any application can extend the functionality of many others.
Xendai Solutions has just released the latest version of Bellhop (v. 1.0.5), an application that makes it super easy to create and publish services using F-Script (as well as other languages including AppleScript, Perl, Python and Ruby). Bellhop can be used for free, and you are encouraged to make a donation if you like the product. The application comes with excellent documentation and is fun to use.
To experiment with it, I've created three services so far:
- The first one is very simple and consists in capitalizing the selected text.
- The second one is more useful: it creates a new task in iCal named after the selected text, using of the Calendar Store framework introduced by Apple in Leopard.
- The third one is fun, and demonstrates that services are in no way limited to working with textual data: it takes a list of numbers, creates a nice looking chart using these numbers and returns the chart as an image in the original application.
The Capitalize Words Service
This service is used as a basic example in Bellhop's documentation. As usual, we have full access to Cocoa from F-Script. For this service, we make use of the capitalized
method provided by NSString
. Here is the code that we can enter in the Bellhop graphical editor:
" Sample F-Script service to capitalize all words in the currently
selected text. This service might be labeled 'Capitalize Words'
in the Services menu. "
runService := [ :aPasteboard |
"Read string from the pasteboard"
pbString := Pasteboard readStringOfType:NSStringPboardType forPasteboard:aPasteboard.
"Capitalize string properly"
pbString := pbString capitalizedString.
"Declare returned data type on pasteboard and write it back"
Pasteboard declareTypes:{NSStringPboardType} forPasteboard:aPasteboard.
Pasteboard writeString:pbString ofType:NSStringPboardType forPasteboard:aPasteboard.
].
The service configuration is also done using Bellhop:
Finally, the Bellhop document is saved in one of the directories that Bellhop scans automatically (e.g. the "Documents/Bellhop" subfolder in my home folder).
And, oh miracle, the Capitalize Words functionality is now available in other applications.
For example, I can select some text in an email message I’m editing, apply the service and have the selected text become capitalized.
But… My services menu is a mess!
By default, the services menu displays all the services available on your system, which often makes it much too crowded. This is where comes another nifty utility, Service Scrubber. This utility lets you fully customize the services menu. This is particularly useful for removing services that you never use (typically, a lot), or associating keyboard shortcuts to some services.
The New Task Service
This service uses the selected text to create a new task in iCal. To that end, it makes use of a new framework introduced in Leopard, the Calendar Store framework:
Calendar Store is a framework that allows Cocoa applications to access iCal data. You can fetch iCal records?such as calendars, events, and tasks?and receive notifications when these records change in iCal. You can also make some local changes to records and save them to the Calendar Store database.
The CalTask
and CalCalendarStore
classes we use for implementing the service are provided by the Calendar Store framework. They take care of communicating with the Calendar Store Server which manage the Calendar database:
Here is the F-Script code for our service:
runService := [ :aPasteboard |
"Read string from the pasteboard"
pbString := Pasteboard readStringOfType:NSStringPboardType forPasteboard:aPasteboard.
"Load the Calendar Store framework"
(NSBundle bundleWithPath:'/System/Library/Frameworks/CalendarStore.framework') load.
"Create and configure the task"
task := CalTask task.
task setTitle:pbString.
task setCalendar:(CalCalendarStore defaultCalendarStore calendars objectAtIndex:0).
"Save the task in the calendar store"
CalCalendarStore defaultCalendarStore saveTask:task error:nil.
].
The "New Task" service is now available in other applications. For example, I can select some text in Safari…
…and then activate the service. The selected text is immediately saved in the system-wide calendar database, and appears in the tasks panel of iCal:
Thanks to services, I shall not screw up my next telepathy session like last time.
The Line Chart Service
This service creates a chart from the selected list of numbers (the numbers must be separated by spaces). The chart is created using the Google Chart Web Service. You'll find more about using this Web service with F-Script in Google Chart API Fun with Cocoa and F-Script.
runService := [ :aPasteboard |
"Read string from the pasteboard"
pbString := Pasteboard readStringOfType:NSStringPboardType forPasteboard:aPasteboard.
"Parse the string to get the numbers"
values := (pbString componentsSeparatedByString:' ') doubleValue.
"Construct the call to the Google Chart Web service"
max := values \ #max: .
scaledValuesString := values * 100 / max componentsJoinedByString:','.
chartString := 'http://chart.apis.google.com/chart?cht=lc&chs=250x150&chxt=y&chxr=0,0,' ++ max printString ++'&chd=t:' ++ scaledValuesString.
escapedChartString := chartString stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding.
"Call Google Chart and create an NSImage with the result"
image := NSImage alloc initWithContentsOfURL:(NSURL URLWithString:escapedChartString).
"Declare returned data type on pasteboard and put the image in TIFF format"
Pasteboard declareTypes:{NSTIFFPboardType} forPasteboard:aPasteboard.
Pasteboard writeData:image TIFFRepresentation ofType:NSTIFFPboardType forPasteboard:aPasteboard.
].
Then, all we have to do is to tell Bellhop that the service accepts a string, returns a TIFF image and is active. We can now use the service in applications that supports such images. Here is an example with TextEdit (editing a document in RTFD format). Before using the service…
And after…
That's all for today. Enjoy!
Read Full Post »