Editor's Note: In this first of a two-part series, Harold Martin shows you how to get started with creating Sherlock channels by walking you through the process of creating a channel to search, browse, and display articles from the MacOSXHints.com site. Along with Apple's Sherlock documentation (those documents should be installed on your hard drive at /Developer/Documentation/Sherlock), you can begin to explore the world of developing for Sherlock.
This article is included on the Mac OS X Innovators contest site as an example of the many development opportunities that the Mac platform offers programmers and hobbyists alike.
Sherlock was first introduced by Apple with Mac OS 8.5 in 1998. With the introduction of Mac OS 9 in 1999, a new version was released, Sherlock 2. At that time it received a user interface overhaul, even though the underlying channel structure stayed the same for the most part. Now, with the introduction of Mac OS X 10.2 Jaguar, Sherlock is completely overhauled both internally and externally and becomes Sherlock 3. For the first time, Sherlock developers are able to create complete user interfaces that take full advantage of Aqua and Mac OS X. With the increased power also comes new challenges when developing channels: there was a dearth of documentation to help developers create a channel from start to finish. That is, until now.
Mac OS X 10.2 Jaguar, preferably the latest version.
The Apple Developer Tools. These are on the gray CD that came with Jaguar. You'll also need to download and install the December 2002 update from the Apple Development Tools site.
The Sherlock 3 Developer Channels. These are channels themselves that we'll use later on.
In order for this channel to run in developer mode, which allows us to, among other things, reload a channel without restarting Sherlock, we need to set the SHERLOCK_DEBUG_CHANNELS environment variable.
/Developer/Applications/Property List EditorSHERLOCK_DEBUG_CHANNELS1environment.plist in your home directory and quit Property List Editormkdir .MacOSX
mv environment.plist .MacOSX//Developer/Applications/Project Builder
You now have a Sherlock Channel just waiting to be developed! You may be wondering why you should create it in ~/Sites, but we'll get to that in a minute.
The first thing to do after you have created your channel is to give it a unique identifier. This way Sherlock never gets confused between channels, even if they have the same name. In Project Builder (PB) lick on the file "SherlockChannel.xml". This is where all the basic information about your channel will be stored. You should see a line like
identifier="com.MyCompany.Search"
Change this to reflect your Web site, or if you don't have one, your email address. So if your email is stevej@mac.com your identifier would be com.mac.stevej.macosxhints.
After you change the identifier, we'll want to name our channel. Note: When I mention files and their paths (unless I state otherwise) I'm referring to the Project Builder structure, not the Finder file structure. Under the folder Channel/LocalizedResources.plist/ choose the file en. You'll see
<key>CHANNEL_NAME</key>
<string>Sherlock Channel</string>
Let's change "Sherlock Channel" to "Mac OS X Hints".
Now you're ready to launch Sherlock in developer mode. Make sure Sherlock isn't running. In PB click the icon with the hammer over the monitor, or press Command-R. Sherlock will open up, but you won't see your channel because you haven't subscribed to it yet. First make sure Personal Web Sharing is on in the Sharing System Preferences pane. Now, open a browser and type
sherlock://localhost/~stevej/macosxhints/SherlockChannel.xml?action=add
replacing stevej with your username. Now you see that the reason the channel was saved in ~/Sites is because everything in ~/Public will be available on the internet when you start the Web server, which was done in the Sharing preference pane.
Now let's pick apart the URL: sherlock:// is how all channels are opened, localhost means to get them from your computer, ~stevej is to get the channels from stevej's Sites folder, "macosxhints" is the folder within the Sites folder to get the channel, SherlockChannel.xml is the file where Sherlock gets the initial information about the channel (it's the same file we edited the channel identifier in earlier), and ?action=add tells Sherlock to keep that channel on your toolbar.
In this section, we'll step back and take a look at the concepts and languages you'll use to start writing the channel.
One of the most difficult part of writing channel is that you have to use two languages, JavaScript and XQuery. Even though it is theoretically possible to use only one, it's practically impossible. So let's look at the two and see how they are used together in writing a Sherlock channel.
|
Related Reading
|
The Web page designer's friend (and the language that drives you nuts with rollovers and pop-ups), JavaScript it a rather easy-to-learn language that is ideal for retrieving and setting values in the DataStore (we'll get to that in a minute) and opening up external URLs, as well as some other things. I would highly recommend you read through (at least) the first six chapters of JavaScript: The Definitive Guide 4th ed.
XQuery, a newer language, is designed to parse XML (that's where the "X" come from). Although it has less documentation than JavaScript, XQuery will do many important things for our channel We'll use it for retrieving web pages and other files over the internet and parsing those file to get the information our channel needs out of them. Apple has added numerous extensions, particularly in the area of making http requests.
This article is too short to teach either of these languages in depth, but because of the Sherlock specific extensions and the limited documentation, more focus will be given to XQuery.
These concepts are fundamental to writing channels, so pay attention! Triggers are the pieces of code (written in XQuery or JavaScript) that respond to events in the DataStore. The DataStore is the mostly invisible "glue" of a channel, where our code connects to our interface, and our interface connects to our code. Paths (in this context, these are not related to XPath) are how the DataStore knows where to find the interface widgets and the corresponding code.
Here's an example: when the user clicks on the "Search" button in the channel it will let the DataStore know it has been clicked. The button's name is "SearchButton" and the name of the top level view (the whole window) is "Internet" so the button's complete path is "Internet.SearchButton". Which means the DataStore will look for the path "Internet.SearchButton.action" in the code when the button is clicked, and execute the proper code. The code could then use a path to insert text into a text field. I don't want to get you bogged down in all the details right now, but keep this in mind, we'll look at it more later when we start coding.
There are also a few things you need to know about creating the interface. The first it that the interfaces widgets you'll use are Object Oriented (OO). OO means different things to different people, but for the interfaces we'll create it means that widgets can inherit properties and methods from their parents.
For example: the SearchButton is an instance (which means it's an actual use of) the NSButton object. NSButton inherits the "action" property (among others) from NSControl. An important reason to remember this is that Apple's Sherlock reference, which you'll come to rely on, only lists the methods specific to that object, assuming that you'll remember to look at the object's parent(s) to see it's other methods and properties. And in case you're wondering what the NS in front of each class is for it's a relic from NextStep, the OS from which Mac OS X is derived.
|
Before you create your interface, think about how it will be used. This is all users will ever see of your hard work, so it's worth taking the time to get it right. Get a vision in your mind of what it will look like. Sketch it out. Don't overload it with unnecessary widgets, but don't make it just an Aqua-ized search interface either.
Open up our project in PB, then open the Channel folder and within that the Channel.nib folder. This is where the localized interfaces are stored. Double click en, which will open Interface Builder, the other edge of the Apple's main tools sword. This is where you'll create our channel's interface.
We will start by adding a row of buttons to the bottom edge of the window. Clicking on either the table or the "HTMLView" box you'll see eight dots surround the two widgets. These are handles, and you can resize that table and the HTMLView with these since they are connected in a split view. Drag the bottom center dot upwards, shrinking it until it has about double the margin it previously did from the bottom edge of the window. Under the Cocoa-Views palette (you may need to press command-/ do make it appear) click on the item with a button and a text box. Drag the button labeled (coincidentally) "Button" to the lower left corner of the window.
As you drag the button, you'll see dashed blue lines appear. Those are there to help you create an interface that complies with the Aqua Human Interface Guidelines (HIG), which is a document that specifies how many pixels different widgets are to be from each other, how they're supposed to be used, and aligned.
Fortunately, Apple realized that developers wouldn't be able to remember the exact specifications so the blue lines are there to help you create your interface correctly. In the case of the button you just put on, align it with the left hand side of the split view and the bottom of the window.
To change the text on the button, double click it. Type "Enlarge Font" and press enter. One guideline to remember when creating the interface is that utility buttons, or buttons that aren't going to be used often should be small. To make this button small, press "command-shift-i". This bring up a window where you can change many different properties of the button, but for developing Sherlock channels, we'll only use a few of the panes. Press "command-1" to open the attributes pane and check the "Small" check box in the "Options" section.
Now you'll need to re-align the button with the bottom left of the window again. You can now load your channel in Sherlock (if you already have Sherlock open you'll need to reload the channel by pressing command-r) and see the new button! Now try the steps above to create a similar button, but this time in the lower right corner with the text "Get more Sherlock Channels".
Now reload the channel and try resizing the window. See the problem? The new button doesn't stay in the corner like you would expect it to. The problem here is that we didn't set the springs. Springs are how your buttons are "pressed" against a particular side of the window as it is resized. The other button stayed in the same place because the window isn't resized from the left side.
To set spring open the info window for the button and press command-3 , which opens the Size pane. In the Autosizing box you can see a faint square with four lines coming out of it and four lines inside of it. The box represents the widget and the eight lines are how you determine the way the object is resized. To keep the button in the lower right corner, click the top and left outside lines which should change them into springs. The spring from the top is pushing the button to the bottom of the window and the spring on the left is pushing the button to the right of the window. Now add springs to the first button to keep it in the lower left corner.
Create another small button just to the left of the "Get more Sherlock Channels" button called "About". Now add one last button, "Shrink Font" to the right of the Enlarge Font button. Be sure to set the springs properly!
The last thing to do to make this part on the interface look right is to adjust the SplitView to be the correct distance away from the buttons. Click on the SplitView and drag the bottom center dot toward the buttons until the blue dashed lines appear.
After you save your changes, you can now look at your channel in Sherlock and see the new buttons. As you develop your own channels remember: if your widget is showing up in IB but not in Sherlock, you probably didn't set the springs.
Now to adjust the top part of the interface. First, we'll only need one column in our table, not two, so we'll delete one. Click the table once (which selects the NSSplitView), then double click the table (which selects the NSScrollView which contains the NSTableView), then double click on the table again, which finally selects the table itself.
In the table's attributes change "# Colms:" to 1 and press enter. Close the info window and you'll see one column with a title in it, but it still appears like there's two columns. Move the cursor over the column divider and drag it all the way to the right of the table (don't worry if you drag it too far and the other end of the table looks like it disappearing).
We'll also want to get rid of the text above the search box, since it's not what the box will be used for. If you'd still like to provide help or instructions, there is a better way. Click the search box, open up it's info, and click command-4 to open up the Help pane. Here you can type something like "Enter the key words you want to search for in this box".
But now that we've removed the help line, we'll need to re-adjust the interface so that there isn't a gap along the top of the interface. Under the size pane of the text box, enter 10 in the y text box (make sure that Top/Left is selected in the list above the box). Drag the rest of the widgets in the top row to align with the button and drag the split view to be the correct distance from it.
In my opinion, the rounded text box looks better than the square one so open the text box's attributes and check Rounded in the Options box. Another change you'll want to make is to allow users to press the Return key from the search box to start a search. Open up the attributes for the search button and in the pop-up menu to the right of Equiv: choose Return. We'll also want to add a button next to Search for people to get the latest hints after they've done a search (remember what I said earlier about fully thinking your interface through). Drop a regular size button next to Search and title it "Newest Hints".
Now we will need to set paths for each new widget. Open a widgets info window and then the Sherlock pane (command-7). You set a path be entering a name in the Name text box, and the complete path will be shown in the "Data Store Path:" box below. Set the name for "Get more Sherlock Channels" to "GetMore", the "Newest Hints" button to "NewestHints", and the "About" button to "about". You might be wondering why you don't need to set names for the two font buttons, but we'll cover that later in this tutorial.
Sherlock depends on the code to tell it how small to allow the user to make the interface. We want to make sure the minimum size specified in our code matches the size of the window in IB. Open the window's size pane in it's info. Here you see it's width (w) and it's height (h). Copy those two values down and in PB open up Channel.xml in the Channel folder. Near the top you'll see the line:
DataStore.Set("Internet.minViewSize", "{width=310; height=260}");
Change the width and height values to whatever you copied out of IB. You're now done with the interface and are ready to move on to coding. Quit IB (saving your changes of course) and get ready to dig into PB.
In the second part of this tutorial, which will publish on Tuesday, April 1, you'll get into the code. Just as you spent time thinking about your interface before you made it, you should also spend time thinking about your code--what you want it to do and how you want it to react to the UI. There is a tremendous difference between code that just works, and code that is fun to work on.
See you next week!
Harold Martin is a freelance software developer and author. Visit him at his blog.
Return to the Mac Innovators Contest.
Return to the Mac DevCenter.
Copyright © 2007 O'Reilly Media, Inc.