PDF Browser Plugin, SafariNoTimeout, Pacifist, and Iconographer (and Filthy Hypocrites)

Welcome to the newest Helpful Tiger category! Here’s where you’ll find out about the utilities I’ve found most useful in my programming endeavors.

PDF Browser Plugin
In-browser viewer for PDF documents. Uses Apple’s PDF engine, so not a full-featured as Adobe’s viewer, but certainly very convenient, and it gets improvements when Apple’s engine improves. So, for instance, on 10.3 it can open Postscript documents.

I’ve found this plugin extremely useful for looking at documentation that I may not need permanently, but is only available in PDF format, such as the Unicode code charts.

Despite the fact that it’s free and Shubertit’s Web site also features shareware apps the poor man actually gets paid for, currently his Support page is mostly about PDF Browser Plugin and very informative.

He also has another free plugin for Microsoft Word.

And, err, some other stuff. Go look at that, too.

A free Unsanity “haxie” that removes Safari’s 60-second connection timeout limit. Very helpful with Apple’s mailing list search pages, which I’ve talked about most recently in this post.

What!?, you’re saying to yourself. Didn’t I spend all that time telling you how to bypass Apple’s search pages? Don’t I “eat my own dog food”?

Okay, I admit it! I am a filthy hypocrite. I do use my own downloaded archives. Honestly. But– Apple’s site allows you to search for unrelated terms in a way unavailable to the client applications that I use to search the archives. If anybody is aware of an application that allows such OR-style searching, let me know. (And I’m not talking about grep, helpful as that can be.)

Also, if you’re on a new machine without the archives for a particular mailing list handy, it’s much easier to download this than wait 8 hours for all the archives to download, eh? It’s nice to have both techniques available when you need them.

A $20 shareware utility to open Mac OS X .pkg package files and allow you to extract individual files and folders out of them, as well as many other features.

This one’s a bit more special-purpose than the others, but I’ve found it helpful in the past when I’ve dealt more with OS X installer packages.

A $15 shareware icon editor, especially useful for OS X-style icons.

I’ve found Icon Composer, the editor included with Apple’s developer tools, wanting in several respects, and Iconographer is in my opinion the best alternative for the money.

Cocoa Links, Part of a Good Breakfast

For a while now, I’ve been meaning to put together some good lists of links for Mac developers.

Here’s my first one, a list of Cocoa sites.

It’s based on a list from Paul Gobble of StudioBox, Inc. Thanks, Paul! I’ve edited the list and expanded it with my own contributions.

I’ll be adding to this in the future, as well as finding a permanent home for it. Feel free to suggest your own additions in the comments or, once they’re turned off, by emailing me. Enjoy!

Big Nerd Ranch, classes, books, and online resources, by Aaron Hillegass and others:

Blackhole Media BSD-Licensed Goodies:

Borkware LLC, some projects with source, MacEdition links, etc., by Mark Dalrymple and others:

CamelBones, an Objective-C/Perl bridge framework, by Sherm Pendley:

Cocoa Dev Central, articles for “Cocoa newbies”:

CocoaDev, an OS X developer wiki (community-edited info site):

The Cocoa Files, writings by Andrew Stone:

Cocoa Literature List, by Jeff Biggus:

Deep Cocoa, OpenGL tutorials by Katherine Tatters:

Karelia Software‘s Cocoa Open Source:

MacDevCenter.com, articles, focus on Open Source, by O’Reilly:

Mamasam, a Resource Center for Cocoa Developers
Searchable archive of Apple’s cocoa-dev and OmniGroup’s MacOSX-dev mailing lists, and other resources.

The Omni Group developer mailing lists:

Note the Omni Group does the Right Thing with its mailing lists and provides compressed archives for previous years’ messages. Apple? Hello? If you want such a feature yourself for Apple’s lists, see my rather high-bandwidth solution (original link and update).

Ranchero Software Weblog:

More Ranchero-provided Cocoa samples can be found at http://ranchero.com/cocoa/.

Unofficial cocoa-dev FAQ, by Alastair Houghton:

Double or Nothing: Pitfalls with NSTableView’s doubleAction

One of the core capabilities of Interface Builder, when used as the resource editor of a Cocoa application, is connecting UI widgets to code with just a bit of click and drag. The code that is connected to consists of outlets and actions.

Outlets are straightforward, just pointers to objects. The basic thing to remember, as with all Interface Builder connections, is that you shouldn’t try to refer to them in your init methods; use awakeFromNib instead.

Actions are methods, more specifically methods referred to by name (in the nib file) or selector (at runtime) instead of (as in C/C++) by pointer. To connect an action in Interface Builder, you control-drag from the UI widget that will send the action message to the object that will receive it, and then you choose the method in that receiver whose message will be sent.

The reason actions are more complicated than outlets is because actions are really part of a target/action pair. Interface Builder needs to remember more than just the message, it needs to remember the target to send that message to.

Until recently, to me at least, the Interface Builder UI seemed to be a little fuzzy on that distinction. In the Info window for an object, under the Connections pane, there was an Outlets column and an Actions column. Under the latest Interface Builder in Panther, what was called Actions is now referred to as “Target/Action”, which states the actual situation more explicitly.

Another thing to keep in mind is that the Cocoa framework has the action/target pair functionality built into Interface Builder solely for itself. You can’t make your own method/object pointer pair variables in a class and set them via the same Interface Builder UI that Cocoa uses for action/target; there’s just no way to tell Interface Builder to treat your variables that way.

With me so far? Good, because it’s about to get a little more complicated.

A while ago, I made a specialized outline view. When you double-clicked some of the cells, instead of getting the usual text editor, for certain columns and under certain circumstances, you would get more specialized behavior, such as a popup menu or a sheet with multi-line text editing capabilities.

The way to get this behavior is (a) turn off the regular editing behavior for the cell by having the delegate for the outline view return NO for the method outlineView:shouldEditTableColumn:item: and (b) send the message setDoubleAction: to the outline view with, as its parameter, the selector to the method you want to use to invoke the custom editor.

Those of you who know how this works already will protest, “Hey, you missed a step!” And indeed I did. But the above steps work under special circumstances, and they work because of how target/action pairs work.

The control-drag that I mentioned for actions at the top of this post fills in two variables in NSControl and its subclasses. It fills in the target variable with a pointer to the object. And it fills in the action variable with the selector to the method. Now, doubleAction is action‘s cousin in NSTableView. It works exactly the same way. But you can’t control-drag to connect it via Interface Builder, you need to set it via code. So, you would probably guess correctly that setDoubleAction: only sets the method, not the target, and you might look around in vain for setDoubleTarget:. Nope, doesn’t exist. Instead, it also uses target!

Why would this work even when you haven’t set target to anything? Well, if the target isn’t set, the Cocoa framework tries to make a guess what the target should be. You can see the order in which it checks here. This order doesn’t mention the delegate for a control, but NSTableView may be a special case since not all controls have delegates. In any case, it’s a good idea to send a setTarget: message explicitly.

There you have it. As with the solution to most pitfalls, esp. in Cocoa, the solution is very simple, but might not be easy to come up with on your own. Enjoy!

List Searching Update: Minor Bugfix and Xcode

I’ve uploaded a minor bugfix to my applelist.pl script, which I first announced on this Nov. 25, 2003 post. Line 154 has been changed from

system "./getlinks.pl $daypage $dayDir ".*.txt$"";


system "\"./getlinks.pl\" \"$daypage\" \"$dayDir\" \".*\.txt$\"";

Yes, ladies and gentlemen, I was bitten by the bug that often hits Unix scriptwriters getting used to the “Mac Way”: the annoying habit of real users to put spaces in their directory names. (Real users like me.)

In Unix scripting, spaces are often delimiters between arguments. If you have a script that takes two arguments, the first being a full path and the second being something else, and you pass in a full path not enclosed in quotes as that first argument, the script will parse everything after the first space it finds in that path as the second argument. Ugh.

When you’re working directly in Terminal, using things like tab completion or dragging in file/folders to get their paths, the OS handles this sort of thing for you by escaping out the spaces like this:

My\ HD/My\ Folder/

but you don’t want to do that sort of find and replace in your paths, and quotes are easier, anyway.
Second item of the day: people have been grumbling for a while now on the xcode-users list, which is the replacement list for projectbuilder-users in the same way that Xcode is the replacement for Project Builder (whose corresponding Apple Web site link now points to Xcode – is that a sign, or what?), that it does not yet have an entry in Apple’s list search page.

Now, there isn’t that much content in the list yet, just three months worth, but esp. considering that this is Xcode’s teething period, it’s nice to be able to search for specific problems you’re having or issues you’re facing. So I’m proud to say my applelist.pl script is the only way currently to enable such a search! Yes, yes, I know this state of affairs won’t last, but it’s nice to have bragging rights for a little while, eh?

“I would prefer not to”: Preferences Files Done Badly

I wrote a post I didn’t upload two days ago about how CodeWarrior 6 was working natively in Panther (Mac OS X 10.3) when it hadn’t worked in Jaguar (10.2) and possibly earlier. The appropriate comp.sys.programmer.codewarrior thread can be found here.

CW 6 is probably most useful to developers who still need to do 68K builds. Yes, there are still professional developers who are getting paid to do this in certain cases, whose identities (the cases, not the developers) I will leave as an exercise for the reader.

So I wrote a whole post about the tempestuous history of CodeWarrior 6 on OS X, waxed nostalgic about WWDC 2000, and marveled how something Apple had done in Panther managed to make it work again.

But I was wrong.

A little later I opened CodeWarrior 8, which is a much more respectable version of CodeWarrior to be using in this day and age, and when I tried 6 again for the express reason of verifying the post I’d written, CodeWarrior 6 crashed again just like it had in 10.2.

If I remove the /Metrowerks/ folder that CodeWarrior 8 puts in my OS X ~/Library/Preferences/ folder, then CodeWarrior 6 can be opened again as a native OS X application without crashing.

The problem isn’t something in the OS. The problem is CodeWarrior 6 choking on something in a preferences file it didn’t expect.

So this post is going to be a little different.

I don’t know for a fact exactly why CW 6 is crashing, but it might be related to code like this, which gets a preferences value from a preferences file:

  SInt32 valueInt; // Where we're saving value

  CFTypeRef valueRef = CFPreferencesCopyValue(ValueKey, AppID, (CFStringRef)kCFPreferencesAnyUser, (CFStringRef)kCFPreferencesAnyHost);
  if(value != nil)
    if(CFGetTypeID(valueRef) == CFNumberGetTypeID() && CFNumberGetValue((CFNumberRef)valueRef, kCFNumberSInt32Type, &valueInt))
      // Success


Now, this code does the right thing. (Sorry about the formatting.) It doesn’t assume that the value referred to by ValueKey exists. And (what developers get wrong) it doesn’t assume that if a value is found, that it’s of the assumed type. This is important because the OS X preferences file format is text, XML-based, easy for end users to get into and muck around with. If the type check wasn’t there, and the value were instead, say, a string? The application would most likely crash.

And this is just Carbon. The Cocoa framework takes care of all of this for you, reading preferences XML data into objects. Guess what! Cocoa apps also crash when you muck with the preferences file, just like badly written Carbon apps. There’s nothing you can do about it.

Now, this is not a hugely important detail. Users who muck with preferences files willy-nilly deserve what they get. But I would prefer that my apps get this sort of thing right, and I’d prefer that the apps I use do, too.