A Gentle Touch

July 24th, 2006

Anybody who packages software (or other files) for distribution on the Mac is probably familiar with the tedious process of setting up the icons “just right” in the Finder so that when your customer opens the folder for your app, they see things neatly organized the way you left them.

I don’t know how many times I’ve longed for a “nudge” keyboard shortcut in the Finder. If I could just move that icon one pixel to the left, it would be perfect. Inevitably I click and drag just a little bit too far. It’s hard to get right on the first try!

But today my worries came to an end. My NudgeFinderSelection scripts simply ask the Finder to move all the selected icons in a given direction. Since the behavior is is 99% identical for whichever direction the nudge should be, I just wrote a single script and copied it 3 times. The script examines its own name and determines its functionality accordingly.

Note: Because of this “self-determination,” you can customize your own version to go above and beyond the current behavior. Try making a copy and naming it “NudgeDownRight” for instance, and you’ve got yourself a nice little diagonal nudger.

I hooked up all four of these as Finder-specific shortcuts with FastScripts. Now when I’m tweaking icon layout in the Finder, a nudge is just a short cmd-opt-arrow-key away.

Note: my “clever hack” here begs the question, why doesn’t FastScripts respect symbolic links? It would be much cleaner to have a single copy of the file with multiple references to it. Yes, it’s a grievous shortcoming, and hopefully situations like this will inspire me to remedy it soon.

And since you’ve read this far, I think you deserve a little treat. Ever thought that the icons in a window would look best arranged in sine wave formation? Well, you were probably wrong, but SinusInfection will guide you to forming a permanent opinion. Unfortunately, there’s no undo, so don’t run this with an important Finder window front-most!

It’s not a very polished script, and may take some tweaking to work right on your system. C’est la vie! In particular make sure you have an open window with a substantial number of items in it. And turn off any automatic “keep organized” settings in the Finder view options. I’ve also noticed that Finder can sort of freak out while you’re doing this, apparently because so much is changing out from under it. The sine wave tends to get broken and re-originates at a slightly different window offset.

FlexTime 1.0b6

July 24th, 2006

The feedback I’ve gotten on FlexTime since releasing the last beta almost two weeks ago has been simply amazing. I really appreciate all the thoughtful comments people have made. While there has been some scrutinizing the limited feature set of this initial version, most of the feedback has focused on eliminating annoying bugs and making minor tweaks to improve the usability of the product.

I’m really happy with how things are progressing.

FlexTime 1.0b6 contains a number of fixes directly motivated and inspired by beta testers. Please note before downloading that the document format has changed in this release. To ease the transition, I’m including a “Document Upgrader” droplet that should take care of any old documents you have created with previous beta releases. BUT you should update all of your documents and never look back at older FlexTime releases. Please, do it for the children.

Update: Andy Lee found a heinous UI problem in 1.0b6 that prevented repeating cues from being set on new activities. Ouch! I’ve made a quick fix and upped the download to 1.0b7.

Click Here to download FlexTime 1.0b7.
Reminder: Be sure to drag your old documents to the upgrader droplet!

So now that we have the big scary document change out of the way, what else is in store with this release? Here are my notes:

- Revised About Box 
- Custom document icon
- Add "Repeat when finished" checkbox to main document window
- Add a "Remember Layout" menu item to save info in document:
	- Window frame
	- Table column order and sizes
- Scriptability
	- Added support for a "reset" command
	- Added scriptable access to the "running" attribute
		of a routine document
			- Caveat: cannot stop/resume from within a cue
			handler script at this time.
	- Added a "Display Message" command to give script access
		 to the text message mechanism
	- Early support for a special handler in AppleScript cues.
		HandleFlexTimeCue(theDocument, theActivity) gets called
		if exists within the cue script
- Fix a bug that prevented scripts from being chosen for Run Script
- Fix crashing bugs with "Speak Text" cues
- Fix a crash that could occur when a script alias was stale
- New documents now start out with a single default activity
- Open documents are now preserved across launches

Executive summary? Lots of bug fixes, a slightly cleaned up UI, and a controversial “Remember Layout” feature. Let me say now that this item is probably going to disappear before 1.0 final, so don’t get too used to it. The idea is that some FlexTime routines are suited to a particular UI layout and that layout should be remembered by the application. The question is whether the remembering should be automatic or explicit. I think I’ve been convinced that it should be automatic, and be saved in the user’s preferences instead of the document itself. If you’ve got strong feelings about this, now is the time to make your case.

So what’s left before it goes final? Mostly just documentation and finishing up the AppleScript support. Keep your eyes peeled for a final release within the next few weeks.

Iron Coder Three

July 23rd, 2006

The third Iron Coder concluded tonight. Mark Dalrymple emerged victorius! I’m really happy for Mark’s win because his entry was fun, and because it was clear he latched on to the opportunity to use the contest as an opportunity to really learn more about the Mac OS X APIs. I also have a lot of faith that Mark will be a good “chairman” for the next contest.

I came in 3rd or 4th, depending on how you do the math. My entry this time is called What Have You Done For Me Lately. It’s as a relatively snazzy (IMHO) application that gives the user a visual impression of how much time they’re spending in each application over a given period of time. Since the API for this contest was “CoreGraphics,” I used it as an excuse to play with patterns, which I use to fill the bar-graphs.

Download all the entries for the latest contest in a single DMG! Click here. (Thanks to Jon Wight for putting this together).

I take a different approach to Iron Coder than many of the contestants. There is a tendency to make “something cool” at the expense of refinement. These submissions are awesome! They make me laugh and stun me with their creativity. But I stubbornly cling to a different ideal. I use the event as an opportunity to play “what if a product’s entire cycle was 48 hours.” Now, I won’t go so far as to say my submission is bug-free, but I try to make it “release-ready.” That means I hold off adding features if the time limit doesn’t support it. I like the constraints that a 48 hour deadline gives me for producing a “relatively shippable product.” Instead of asking myself how much I can possibly cram into the submission, I ask myself how much can I make “user-ready” and try to accomplish that.

At the end of 48 hours (with time out for sleep, family, and social commitments), what I submit is not exactly product material, but something I can proudly attach the Red Sweater brand to.

The Case of the Missing Check

July 20th, 2006

Things are getting ever-nearer with FlexTime, but as is the case with every project of relative complexity, the number of niggling details expands in proportion to the number of days of development ostensibly remaining.

Today I spent a lot longer than I wish I had working on a confounding bug report with my NSToolbar implementation: the contextual menu doesn’t update to reflect the changed state. If you control or right-click in any standard Cocoa application’s toolbar, you’ll see a contextual menu containing common configuration options. The indicate the enabled state of these options, a check mark is supposed to appear next to them. This is what the menu looks like in FlexTime 1.0b5:

No check marks! Obviously the icons are small, and I’m seeing “Icon and Text.” Something is definitely wrong here. But I’m not responsible for managing this menu. Who is?

If you guessed NSToolbar, don’t feel stupid. I guessed that too, but we’re wrong. My first reaction in debugging this problem was to search the documentation for NSToolbar to see if there are any funny delegate methods I’m supposed to implement. NSToolbar has nary a mention at all of even the existence of this contextual menu! Before getting too deep, I tried a few web searches, to see if anybody else had already run into the problem. I found this from 2001 describing an identical problem. And this from last year following up on it. But where is the fix?

The next phase in my debugging process was to start hacking out code until things work. I figured there must be something funny in my delegate methods, so I just unhooked the delegate and let ‘er rip. Result? An empty toolbar with exactly the same problem.

The Xcode developer tools ship with a source code example, Simple Toolbar (local link). I was running out of patience so I decided to attack the problem from the complete opposite direction. Instead of asking “why doesn’t my menu work right?” I would ask “why do all the other ones work right?” Who is responsible for setting those damn check marks, anyway?

From the SimpleToolbar project, I first launched and confirmed that the check marks show up as expected. Then, I “Paused” execution to get access to the gdb console, and set a breakpoint:

(gdb) b -[NSMenuItem setState:]
Breakpoint 6 at 0x9373b5f8
(gdb)

Somebody is setting those checkmarks, and unless they’re doing it in an extremely unsanctioned way, I’ll catch them at NSMenuItem’s setState: method. The only problem is everytime I “continue,” it reactivates the application and causes my breakpoint to be hit at a spot where I don’t care about it. Once the breakpoint hits I’m stuck back in the debugger and never get a chance to right-click in the toolbar! This is overcome with a simple conditional on the breakpoint. It turns out that it’s just one menu item that is always getting set when I activate the app, so I configure gdb to skip it in that case:

Breakpoint 6 at 0x9373b5f8
(gdb) po $r3
<MenuItem: 0x36b660 Untitled>
(gdb) cond 1 $r3!=0x36b660
(gdb) c

Now I’m able to right-click and provoke the anticipated action. I’m interrupted again by the debugger, this time pointing at one of my items of interest:

Breakpoint 6 at 0x9373b5f8
(gdb) po $r3
<MenuItem: 0x3ab160 Icon & Text>
(gdb) bt
#0 0x9373b5f8 in -[NSMenuItem setState:] ()
#1 0x9382cf90 in -[NSWindow validateUserInterfaceItem:] ()
#2 0x9382ca04 in -[NSWindow validateMenuItem:] ()
#3 0x937e931c in -[NSMenu _enableItems] ()
#4 0x937e72d4 in AppKitMenuEventHandler ()

Note: you don’t have to use commands like “bt” at this point, since Xcode’s debugger is mimicking the same thing in the UI, but once I get started in the console I tend to stay there for a while.

So what have we learned by now? NSToolbar is not responsible for setting those menu item states, NSWindow is! It makes a sort of sick sense, if you consider the overlapping responsibilities of the toolbar and the window. After all, the window is what you “set” a toolbar on. The window has to know how to collapse and expand it. It’s a sick little behind-the-scenes game that fortunately we don’t have to see much of.

So why isn’t validateUserInterfaceItem: called in my app? I set a breakpoint on the method of NSWindow and it never triggers. Hmm, suspicious indeed. But when I, on a whim, search for the method in my sources, my memory is thankfully jostled. Of ourse! FlexTime uses a subclass of NSWindow, and within it, overrides validateUserInterfaceItem! It’s been a while since I did this, so it’s not fresh in my mind. This is what the method looks like:

// MENU Validation
- (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)anItem
{
	BOOL validItem = YES;

	// We use validation to keep the state of NSMenu targets up to date
	SEL thisSelection = [anItem action];
	if ((thisSelection == @selector(undo:)) ||
		(thisSelection == @selector(redo:)))
	{
		validItem = (mUndoDisabled == NO);
	}
	return validItem;
}

At some point I figured out that if I override NSWindow’s menu validation, I get a chance to disable undo and redo. I wanted to do this because it turns out to be very inconvenient timing for a user to suddenly try to “undo” during the middle of a FlexTime routine playback. So what’s wrong with the above method? It cuts NSWindow out of the loop.

Simply adding a “[super validateUserInterfaceItem:anItem]” call at the top of the method does the trick functionally, but it leaves me with a bad taste in my mouth. Specifically, a bad compiler warning. The method is defined as the sole member of the formal NSUserInterfaceValidations protocol, but NSWindow isn’t declared as conforming to that protocol. So I’m stuck in a tough situation. I know my superclass implements this method – I saw it in gdb! I also know that if it doesn’t get its implementation called, my functionality suffers. I decide to code defensively and plan for the best while expecting the worst. If the superclass responds to the method in question, we’ll cast it as being protocol compliant, to satisfy the compiler:

// Let the standard NSWindow validation take place
if ([super respondsToSelector:@selector(validateUserInterfaceItem:)] == YES)
{
	validItem = [(id )super validateUserInterfaceItem:anItem];
}

Now, even in the unlikely event that Apple completely changes the way they handle this, we’ll probably still be safe. Added bonus? No compiler warnings.

But more importantly, the change works! My check marks now appear as expected. One more bug, laboriously down the drain.

How did this bug happen? Several factors played into the mistake on my part. Ultimately it came to an underestimation on my part of the method’s significance to NSWindow. Since NSWindow is not documented as using the method, it was a “lucky break” for me that implementing it in my subclass got me the short-term results I was seeking. But alarm bells should have been going off. I’m overriding a method in a standard system class and seeing interesting results. That means the system must have good reason to call it. I will be much more careful in the future when subclassing and overriding system classes.

The other problems here were with naming convention and habitual expectations. It’s important to remember that, although validateUserInterfaceItem’s main purpose is to enable or disable a part of the UI, its secondary purpose is to allow clients arbitrary access to the UI item. If it was called “validateAndMuckWithUserInterfaceItem,” I might have paused a second or two and thought more carefully about the implications of overriding it. Worse, the typical scenarios where this method is implemented are those where the system has set aside some number of UI elements as being “your responsibility.” I’m not in the habit of needing to defer to the system about whether my UI elements are enabled or not, but in this case by becoming the system, I acquired a whole new set of responsibilities.

Update: Chris Liscio pointed out that the behavior of validateUserInterfaceItem: is to bounce up the responder chain until somebody says “YES” to an item. So another failure here is based no my misunderstanding of the method. It’s well documented, but I never paid close attention:

To validate a control, the application calls validateUserInterfaceItem: for each item in the responder chain, starting with the first responder. If no responder returns YES, the item is disabled.

This means my typical approach to implementing the method needs to change. I have assumed that everything would be enabled unless I said otherwise, when in fact the opposite is true. I should insist that everything I don’t recognize gets a “NO” result from the validation method, as a general rule.