Solipsism Gradient

Rainer Brockerhoff’s blog

Browsing Posts in Software

This will take some explaining, and may not make much sense unless you’re a Cocoa developer. So feel free to skip this.

In early February – about a month and a half ago – someone asked an obscure question on the developer lists and, by chance, I had a code sample that did almost exactly what that guy wanted. So I sent it to him. So far so good… warm fuzzy feelings all around…

…it turned out that this gave me an idea for a new contextual menu for the Finder. So I thought, the new CM will be just that code sample with some padding, so let me start first on the application to configure stuff for the CM. Shouldn’t take more than a week or two.

As usual, I started out by designing the application icon first, which sidetracked me into a long investigation about the habits and aspect of a certain mammal. That done, I decided the user interface should consist only of a list of items. Fixed-size items each consisting of a few icons and a name. Of course, the first choice was using a NSTableView.

It turns out that the icons were too dissimilarly sized, so a table with one row per item showed too few items, and the UI looked ugly. So my next idea was to use a NSMatrix of cells, each cell with the subitems artfully arranged. This looked better, but…

I soon noticed that a NSMatrix operates with rows and columns of cells (duh!). I wanted a view that adapted itself to the window size the user wanted, but having to scroll both horizontally and vertically was ugly as soon as there were more than a dozen items. So I messed around for a couple of days with adapting the number of columns to the window width, and couldn’t get it to work reliably.

So I decided that I wanted something like iPhoto’s browser window, where there’s only a vertical scrollbar, and the view adapts instantly to width variations. So I started to write a custom view that would be inserted into a NSScrollView and do automatic layout of as many items fit into the current width.

After a week of this it was almost working… however, I was getting some strange behavior in certain conditions; when the window was resized very rapidly, or when the scroller had to appear during insertions, or, and so forth. It appeared that I wasn’t managing the communications between the NSScrollView and my own view correctly under all circumstances.

So I was trying to trace when NSTextViews communicate with their NSScrollViews, when it hit me that I could simply use a NSTextView and insert the item as specialized NSTextAttachmentCells. Bliss! This would give me all the required scrolling and resizing behavior automatically, and I would just have to write the specialized cell code and disable some of the NSTextView’s functions… it wouldn’t do to allow the user to insert actual text there, for instance.

There I went off into another two-week-long detour of writing and debugging the cell code. My cells consist of several subentities which are actually NSViews themselves – images and text – and they in turn have to be editable, selectable, and so forth. I’ll leave the saga of how I finally solved this for another post – it involves adding a subview to the NSTextView, something not normally done.

So after that I had to decide which text behaviors I wanted to leave in the NSTextView. No editing, OK. Selecting multiple items with the text cursor? Hmm… it looked interesting. The user could select several items and drag them somewhere! Perhaps for backing up the items into a Finder window – or to make a list of his items into a text windows. I happily set to work implementing routines for copying and dragging the current selection in various formats, which in turned allowed me to exercise my code under unforeseen conditions and decide how the items would be stored and saved.

Then when it was nearly finished I started to run into snags again. Suddenly I wanted to select two non-adjacent items, which NSTextView doesn’t allow. I changed my system-wide selection color (for a non-related reason) and suddenly selections in my NSTextView began to look ugly – worse, the way selections were made called the user’s attention to the fact that this was a NSTextView with much reduced functionality. This is never a good idea; it leads the user to expect certain things to work, and when they won’t work, it’s a negative experience.

So today I disabled all text selections wholesale, as well as all related functionality. This enabled me to throw out several chunks of code and someday might allow me to implement discontinuous item selections, too. So the NSTextView really just handles reflowing of my item list, and all the remaining functionality is shuffled off into my specialized NSTextAttachmentCell class.

So far so good; I expected to finally freeze my UI and get to work on the shareware related aspects – registration, trial expiration, and so forth. However, I still have to extirpate the final clue that I’m using a text view, namely the I-beam (text insertion) cursor. So far I’ve hammered away at this for the whole day without finding a solution that works all of the time.

As usual. 😥

Stay tuned…

Version tracker

No comments

No, not that VersionTracker

As regular readers might recall, my last versions of Zingg! feature an improved update query that allows me to tabulate which Mac OS X version the client is running. Preliminary results show that roughly 3 out of 4 Zingg! users are running Mac OS X 10.3 (Panther). Nearly all Panther users are running 10.3.2… but curiously, about 15% of the Jaguar users have not yet updated to 10.2.8, the last Jaguar release. These figures may be distorted by the fact that most users who check regularly for new versions will of course also upgrade to the latest Mac OS X as soon as possible.

There are no figures for 10.1.x users, as there’s no way to distinguish them from users of XRay, which doesn’t yet have the new version-tracking code. In any event, other surveys have found that they number under 10% (some say 5%) of active Mac OS X users. I don’t plan to support 10.1.x for any new version of my products, and indeed no longer have a Mac that runs it available for testing.

Starting with Jaguar, Mac OS X has facilities for weak-linking system calls and for easily checking if certain calls are available or not. This makes writing software that runs on both older and newer systems much easier. Hopefully Panther adoption rates will be such that I’ll soon be able to release Panther-only software.

I actually posted Nudge 1.01 some days ago, but there were only minute internal changes… and a new installer, which still looks much like a folder alias. If 1.0 is working for you, no need to download 1.01.

The new installer is much more intelligent, automatically routing a CM to the right folder, and creating it if necessary. If you’re suspicious about installers, double-click the installer and it will open the right folder for you, so you can move the CM manually.

This installer (or variations thereof) will be gradually adopted by my other products, and I plan to publish the source and make it available to other developers…

Positive feedback on Nudge is mounting. There’ve been over 3000 downloads so far in 3 days, beating XRay‘s 2800 and Zingg!‘s 2000+ downloads over the whole month!

c.k. over at 3650 and a 12-inch links to Nudge and suggests:

Apple should send him a rather large donation for providing a solution to one of their major Finder bugs…

Not that I would mind… 😆

I’m sure that the Apple folks are working hard at this. However, from the rumor sites, it seems they’re stretched rather thin at the moment, working on Mac OS X 10.3.3. Also, Nudge is more of a stop-gap solution; I certainly wouldn’t want it made permanent.

The links and referrals stemming from John Gruber’s article are becoming too numerous to list. This shows, once again, how word of mouth is important for Mac developers. I also found a flattering side-effect at Ryan Wilcox’s h4ck3r+=boi:

Brent Simmons has created a Mac Software Business mailing list on Yahoo Groups.

The description: “This group is for small, independent Macintosh developers who want to talk with other developers about the business of Mac development. Questions on pricing, packaging, advertising, e-commerce providers, and so on are on-topic. Note that this list isn’t a vehicle for promotion: announcements and press releases are off-topic.”

The members of the group include some heavy hitters in the Mac Software industry, in addition to Brent: Rainer Brockerhoff, Michael Tsai, along with a cast of additional others.

Brent’s mailing list seems to have great potential; if you’re a shareware/indieware developer, I recommend it highly.

A couple of weeks ago I noticed some complaints out on the net about the Mac OS X 10.3 (Panther) Finder not updating its windows. Later on, I myself noticed that it happens sometimes.

The thing is, today the Finder usually waits passively for notification that one of its items has been changed. And not all applications post that notification. Yes, it polls certain folders at strategic times, but apparently not in a way that covers all eventualities.

In Panther, parity with FreeBSD 5 introduced the so-called kqueue mechanism (PDF file), but unfortunately it’s still experimental, and the Finder doesn’t use it. I suppose 10.4 will implement that…

…in the meantime, I wrote a little Contextual Menu called “Nudge” and sent it to some people who complained about that problem. Mosty, there were no replies. Since it worked for me, I let it rest until today, when I noticed a MacFixit article about this very same problem. So, I took a few hours off to recompile “Nudge”, have a half-hearted stab at designing an icon for it, and publish it. So here it is (VersionTracker listing).

Preliminary reports indicate it works in most situations. Icon donations are accepted – I tried to draw an elbow (or fist) whacking a folder, but results were unsatisfactory icon_lol.gif.

Slava of Unsanity posted part I of a good article about being successful in what he calls “Indieware” (formerly known as shareware) – reasonably-priced software developed by small companies, or individual developers such as myself.

First thing to ask yourself is how useful your software would be? Would you use it? Some people I know are making software they don’t use personally and try to sell that (OK, “day job” work doesn’t count, I am talking about indieware here and in the rest of the article). My vague point is that the product will not sell good enough if you do not use it yourself daily, or see no real use for it, or are not inspired enough to use it. Call me superstitious, but how you feel about your software creation is more or less how users will feel about it, except they will have less love and tender feelings than you do towards it.

Some younger developers I directed to the article found this point too self-evident, but it’s not!

Erik J. Barzeski also agrees it’s not self-evident:

Successful indieware developers “get it.” They’re Mac users to the core. Mac users are picky. They have high standards. Mac users care about the whole experience – is your site great, icon cool, and application dock-aware? “Where’s my damn AppleScript support!” they’ll ask. Do your keyboard shortcuts meet their expectations? Is your toolbar pretty? Do you even have a toolbar?

If you’ve been using a Mac for five years or more, you “get” this already. You’re a picky sonofabitch too, and you despise crap applications,especially if they’re your own. Indieware developers spend an inordinate amount of time thinking about the above issues and more during development – great UI doesn’t just fall from the sky. Slava says that new developers should not “?be ashamed to spend a week or more in the planning stage.” I say they should be ashamed (and will be shamed) if they only spend a week. Planning never ends. Mac users expect nothing less.

Couldn’t have said it better myself.

Slava also links to The Do’s and Dont’s of Shareware and Software Strategies for Emerging Developers, two other must-read articles. Now to wait for part II…

Re: One more…

No comments

Well, the details caught up with me very fast. By late afternoon, one user e-mailed me a crash report for Zingg! 1.4. After dinner, another user e-mailed me with a simple way to reproduce the crash. 30 minutes later it was fixed, so 1.4.1 has just been published.

This sort of thing is very embarrassing… neither I nor my handful of beta-testers thought of doing the particular choreography that causes the crash. Namely, one has to:

  • set either DropStuff or Disk Copy to “always/override”;
  • set the “Show” popup to either “No comment” or “Styled name”;
  • open the contextual menu and the Zingg! submenu;
  • without releasing the mouse, go outside the submenu to close it;
  • still without releasing the mouse, go back and reopen the submenu. Boom, as Steve Jobs is fond of saying.

Actually, any application which has exactly 8 or 9 letters and which starts with any hexadecimal digit would cause the crash, but only DropStuff (and, for Jaguar users, Disk Copy) fall within these parameters. At least of the applications I have here.

OK. Why can such a weird combination of circumstances trigger a crash? The answer lies within the way Contextual Menu Plugins work. Briefly, the plugin is a loadable bundle with a few precisely defined entry points. When the user control-clicks (or right-clicks) on some item(s), the “Examine Context” entry point is called with a list of AppleEvent records – one for each item. Such a record resolves to a file path or URL. So I have to loop over the list and by a somewhat complex logic return another list containing parameters for the items I wish to have inserted into the Finder’s contextual menu.

These parameters unfortunately are somewhat restricted; I can pass the menu item’s text, a so-called command-ID, and a few attributes for the item. Later on, if the user selects one of my menu items, the “Handle Selection” entry point is called, this time with that item’s command-ID, and the same list of records that was passed to the first call I described. At this point, I decode the command-ID to get back which application I should open, and I re-decode and encode the record list to pass the list of items to be opened to that application.

All very well, but how to attach application icons to each menu item? This demands more complicated hackery. First, I had to register yet another entry point: a “Menu Event Handler”. Unfortunately, this gets called for the main contextual menu and for every submenu – not just for my own little submenu! So first I had to detect my own submenu among the others; not easy when contents are wildly variable. I finally hit upon the trick of setting an invisible first item with a special encoded title.

Also, for some weird reason, at the point this handler gets called the command-IDs haven’t yet been attached to the menu items – so there’s no way of telling directly to which application each item corresponds. So I simply encoded that information into the menu item texts I return from the “Examine Context” entry point, instead of putting in the actual application name. When the handler sees such an encoded text, it decodes it to obtain the application path, and from that gets both the name and the icon, and puts them into the menu item.

What I conveniently forgot is that the handler gets called every time the submenu is opened – not just when it’s built. So the second time around, the handler dutifully tries to re-decode the menu items again – only now they already contain the actual application names! And for certain names, the decoding will actually proceed with invalid data and crash later on. So, a simple first-time flag and it was fixed. For some reason, in my tests I never played around with the mouse, opening and closing the submenu…

One more…

No comments

After a few days of concentrated effort, Zingg! 1.4 is out the door.

There were several things left out from the recent 1.3 release and I felt it worth the effort to learn how to do them. Putting icons into the contextual menu and a better user interface for the configurator’s application list were the two most troublesome implementations. I’ll write up some details later in the week.

Photos licensed by Creative Commons license. Unless otherwise noted, content © 2002-2024 by Rainer Brockerhoff. Iravan child theme by Rainer Brockerhoff, based on Arjuna-X, a WordPress Theme by SRS Solutions. jQuery UI based on Aristo.