Mail Smart Folders

February 10th, 2007

Maybe this should be filed under “No Duh” but it’s taken me this long to figure out that Mail’s smart folders might actually save my life.

For the past year or so, I’ve adopted the “save everything” approach, ala gmail, except on my Mac. So I have this huge “Received Mail” archive that actually, for better or for worse, contains every single email that makes it to my inbox, including spam that SpamSieve filters.

I’m not perfect at this, but I like to strive for an empty inbox. This makes me feel less stressed. So I “delete with abandon,” knowing that a copy has been archived for future reference. Unfortunately, when I delete with abandon I sometimes lose references to mail conversations in progress. I want to go back and see exactly what somebody said a few messages back, so I’m forced to go to my mail archive and look it up.

No problem, right? Wrong. My mail archive is huge so Mail stutters and thrashes. This is because even though I only care about the message I just received a few minutes ago, it is preparing to show me the entire history of my email life.

Now for the punch line. Smart folders solves this problem! I added a “Recently Viewed” smart folder to Mail, which simply shows me any message I’ve looked at in the past 2 days. At first I tried “Recently Received” but discovered that it also showed me all of the spam I’d received. “Recently Viewed” is perfect, because it shows me stuff I’ve dredged up out of the past. It’s my Mail message working set!

I was so pleased by this that I also added a “Recently Sent” smart folder to do basically the same thing, but for messages that I’ve recently written to others. My only major complaint is that I can’t order the smart folders above my gigantic IMAP hierarchy. It’s a pain to navigate down the mailbox list to find them.

A Moveable Beast

February 9th, 2007

Implementing reordering table views is something of a first-time obstacle in Cocoa. The basic gist is you take responsibility for dragging from and to the table view in question, and thereby implicitly support drag reordering. This technique is demonstrated by Apple’s DragNDropOutlineView example.

Faced with the desire to implement such functionality in a bindings-oriented application (FlexTime), I realized it would make sense to have the dragging handled by a the NSArrayController, which is already handling so much of the behavior associated with the table view.

RSRTVArrayController implements a subclass of NSArrayController whose responsibilities are extended to provide simple row reordering via Drag-and-Drop. The basic gist is:

  1. Drop this one class into your project.
  2. Drag the header file to Interface Builder
  3. Set it as the custom class on an array controller instance
  4. Connect the dataSource and delegate outlets from the table view to the controller.
  5. Connect the oTableView outlet from the controller to the table view.

Et voila!

You can also temporarily disable reordering by calling “setDraggingEnabled:” on the controller. I do this for instance in FlexTime while the routine is running so users can’t completely confuse my world.

Update: Rick Fillion pointed out a possible day-ruining bug in the code, which will only rear it’s head if you happen to be assigning all ownership of your model objects to the array controller. Since the “move” code accomplishes the move by removing and then inserting an object from the array, it turns out that once it’s removed, it’s deallocated if nothing else in your app is retaining it! So the workaround is to be retain the object while moving it, and then release it. Thanks for reporting that, Rick. The downloadable class is now updated.

OMG BBEdit HTML Code Folding

February 8th, 2007

I’m doing some modest revision to my web site, when I notice one of those stupid HTML glitches where you’re just positive you must have forgotten to close a tag or something. Thanks to the generally forgiving nature of most web browsers, the page looks more or less OK, except that some table column is the wrong width, or there’s a rogue “>” running wild among your product copy.

I know this outs me as the world’s nerdiest sometimes-web-page-editor, but my preferred environment lately for managing my sites has been Xcode. I can easily do global search/replace, configure Safari as an executable, etc. I wrote about this weird behavior of mine specifically with regard to WordPress in a past entry.

But let’s face it. Xcode’s editor was not made for HTML editing. I like it mainly because I’m used to it. I can do my usual coding text manipulation tricks without skipping a beat. But when it comes to finding that unclosed HTML bracket? You can’t cmd-double-click a <tr> tag and expect it to automatically find the matching tag, as you can with brackets and curly braces. (Unless there’s some configuration somewhere? Do tell.)

Then it occurred to me. Wait a minute, I have a text editor on my computer that is allegedly great at editing HTML. In fact, BBEdit practically drove the entire web page editing industry during the early years. I never edited web pages back then, and since I’m trapped in Xcode most of the time, I generally only use BBEdit for comparing changes in files. But why not give it a spin?

I opened up the problematic file in BBEdit and a bright light suddenly descended upon my office. The sound of a choir of singing angels enveloped my senses. Not only does BBEdit rock at editing HTML, but they recently added support for code folding, and of course it works brilliantly and automatically on HTML.

Oh. That is just so beautiful. BBEdit’s organization of my document made it a snap to drill in and spot the errant text. But it also makes me cry because I’ve been waiting so patiently and for so long for code folding in Xcode. I know, I know. Leopard. Of course, even then I doubt it will rock the HTML editing like BBEdit.

Cocoa-Java Porting Step 1: Triage

February 8th, 2007

When I chose to purchase an existing Java-based Cocoa application, I knew I was taking on some risk. Mac OS X still has a technology within it called the Cocoa-Java bridge, which makes it relatively easy for Java classes to participate in the Cocoa runtime, automatically translating objects like arrays and strings to their language-specific counterparts, and allowing for messaging between objects of the two languages. In short, it makes it possible for Java developers to write Cocoa apps just like an Objective-C developer would, only using Java for all of the custom classes in their code.

Unfortunately, Apple has all but abandoned the technology. The implications for developers are that Cocoa-Java applications cannot easily adopt new APIs, are mysteriously buggy, and are not easily maintained with the latest versions of Xcode. In short, they are a mine-field of opportunities for disappointment and despair.

Nonetheless, I decided to buy a Cocoa-Java application, after looking through the existing sources and making best and worst-case scenario predictions. The previous owner had already disclosed some nuanced issues with running on Intel Macs, so I knew there would be some work before I could ship it in good faith. The amount of work would range from “a few hacks to get it working on Intel as Cocoa-Java” to “a complete rewrite in Objective C.” I planned to port to Objective C eventually, but I hoped to be able to put the product on the market soon with just a few tweaks to make it robust on Intel. Then I could port to Objective C at my leisure while counting all the money.

Now that I’m 95% done with the process of getting the application into shape, I’m ready to start writing about it. I hope that by explaining the process of evaluating and overcoming obstacles in the process of doing this port, it will establish a reference for others who are stuck doing the same kind of work. I expect there will be many Cocoa-Java parts in the months to come, as support for the legacy technology continues to diminish. Hopefully some of the wisdom I have earned will come in handy for others.

Evaluate The Source Base

The first thing you’ll want to do in approaching any port is to look closely at the existing source files. You don’t need to completely understand what everything does, you just need to see it and take away an impression. At this stage the style and organization of the content should pose as much interest to you as the raw technical functionality. For example, is the object hierarchy conducive to a typical Cocoa design? Is the code well-commented? Does the code exploit language specific features being that will be difficult to replicate in the target language?

In my case the code is well-written, well-commented, and since it’s Cocoa-Java, it already conforms more or less to a conventional Cocoa way of doing things. Lucky me! Nice purchase, Daniel. The only part of the project that doesn’t directly translate to Objective-C is the notion in Java of a class that “extends Thread.” There is no Objective-C class representation of a “thread,” so this would obviously take some modest reorganization. But the use of threads was not extensive, so I did not get scard off.

It doesn’t hurt that Java and C are such close siblings, any amount of porting that does need to be done will retain much of its syntactic appearance. In particular, when porting Cocoa-Java to Cocoa-ObjC, probably 80% of the work will fall into two broad categories of concern:

  1. Convert syntax().like().this() to [[syntax like] this].
  2. Pay attention to memory management.

The syntax point is a simplification, but really a lot of it could be ported by trained monkeys, or better yet, regular expressions. Anyway, I’ll talk more about this in a later segment, but suffice to say that the work of straight porting from Java classes to Objective-C classes can be tedious, but is eminently doable.

Build And Run

Realistically, this step was probably the first thing you did in evaluating the sources. You’ve got the Xcode project staring right back at you, how can you resist at least trying? The great news is that my product built and run flawlessly, from the minute I unpacked the source archives and installed them on my Mac. The bad news goes back to those aforementioned “subtle Intel problems.” Specifically? Crash on print. Crash on window zoom. Ouch.

The crashes in either case point to the Cocoa-Java bridge. An infuriatingly cryptic backtrace essentially lets me know that Cocoa-Java was trying to print some log message (perhaps explaining why it was about to crash?) when it bit the dust:


#0  0x90a53387 in objc_msgSend ()
#1  0xbfffe250 in ?? ()
#2  0x908103a2 in _CFStringAppendFormatAndArgumentsAux ()
#3  0x9080ec8c in _CFStringCreateWithFormatAndArgumentsAux ()
#4  0x925e2a5d in -[NSPlaceholderString initWithFormat:locale:arguments:] ()
#5  0x92604670 in -[NSString initWithFormat:arguments:] ()
#6  0x9672d308 in _BRIDGELog ()
#7  0x9673305d in _BRIDGEMethodImpStructReturn ()
#8  0x93519c6e in -[NSWindow _standardFrame] ()
#9  0x93732d7c in -[NSWindow zoom:] ()
#10 0x9335cd88 in -[NSApplication sendAction:to:from:] ()
#11 0x9335cce1 in -[NSControl sendAction:to:] ()
#12 0x9335ee91 in -[NSCell _sendActionFrom:] ()
#13 0x9335e94c in -[NSButtonCell performClick:] ()
#14 0x97723a6b in Java_com_apple_cocoa_application_NSWindow_performZoom ()

Searching the web for help on this failure was pretty fruitless. I downloaded some other applications that I knew to be Cocoa-Java based, and they didn’t crash on zoom. So what’s the deal? What is my involvement in this problem. Through hard work and luck, I narrowed both crashing problems down to a pretty simple thesis:

“Cocoa-Java will crash on Intel if Cocoa asks Java to return an NSRect.”

It might be overstating the case, but those are the two crashing cases in my application. I suspect there is a byte swapping problem or something. If you return an NSRect, you will crash. Sad story, but fixable. The two method calls in question for me are NSWindowController’s “windowWillUseStandardFrame:defaultFrame:” and NSView’s “rectForPage:”.

Keep It Running

I went to the auto parts store a few months back, around the beginning of autumn. My car had been running hot, and on the verge of overheating all through the summer. I brainstormed with the clerk about what might be wrong, and we finally agreed that it is probably a problem with my thermostat, but could be a problem with the radiator itself. Fine, it’s settled. I’ll replace the thermostat first. But even this involves draining the radiator, perhaps removing it, and a lot of other messy stuff. The shop clerk looked outside at the chilling weather and asked me a few pointed questions: “How old is this car?” “Do you drive it a lot?”, etc. After hearing my responses of “pretty old,” and “not too much,” he gave his pragmatic verdict: “It’s not gonna overheat in wintah, and who knows if it will even run in the spring?”

The point being, it’s not always the right time to do the right thing. I’m confident that that my Cocoa-Java NSRect crashes will disappear if I port the application to Objective C. But can I get away with patching the rough spots today? Moreover, won’t my customers (existing customers of this application) benefit from having the Intel problem solved sooner, rather than later? I decided to attack the problem of crashing NSRect across the bridge.

My first thought was to try to gauge exactly why it crashes. If it’s truly a byte-swapping issue, I could detect the fact that I’m running on Intel, and perhaps massage the data to suit the bridge. The major downside to an approach like this is you’re now at the mercy of Apple leaving the bug in place. In the unlikely event that Apple shows the bridge some love, and fixes the crash, where does that leave me? Now I’m byte-swapping myself into the same crash some ways down the road.

The solution I came up with is simple albeit somewhat of a hack. It’s the duct tape around the muffler, if you will. If you think about the crashing problem with returning an NSRect, an obvious solution would be for Java to not return an NSRect. To work around this problem, I modified the affected Java classes to return NSString instead of NSRect:


public String javaSafeStandardFrame(theWindow, defaultFrame)
public String javaSafeRectForPage(pageNumber)

Now the program doesn’t crash. Of course, it doesn’t zoom or print correctly, either. The problem of course is that Cocoa doesn’t know to call these “javaSafe” versions of the methods, and even if it did, it wouldn’t know to expect a string in return.

This is where the hacking comes in to play. The fact that I’m working with a Cocoa-Java project doesn’t mean I can’t also have Objective-C classes in my project. In fact, at runtime the majority of classes in memory are Objective-C, coming from Apple’s frameworks. What’s to keep me from selectively implementing some classes of my own to help soothe this problem? Using a custom class and the powerful technique of class posing, I’m able to effectively patch out standard versions of the above methods, and replace them with code that calls through to Java, gets a string representation of the rect, and converts it to NSRect for the benefit of Cocoa. Since the NSRect is now returned from Objective C, there’s no more crash. In my main.m source file:


[RectSafeWindowController poseAsClass:[NSWindowController class]];

And in my custom NSWindowController implementation:


- (NSRect)windowWillUseStandardFrame:(NSWindow *)theWindow defaultFrame:(NSRect)newFrame
{
  // Your java class instead of "JavaController"
  Class myJavaClass = NSClassFromString(@"JavaController")
  if ([self isKindOfClass:myJavaClass])
  {
    NSString* goodRectString = [self javaSafeStandardFrame:theWindow :newFrame];
    return NSRectFromString(goodRectString);
  }
  else if ([super respondsToSelector:@selector(windowWillUseStandardFrame:defaultFrame:)])
  {
    return [super windowWillUseStandardFrame:theWindow defaultFrame:newFrame];
  }
  else
  {
    // Just return the default
    return newFrame;
  }
}

A similar approach was used in the NSView override of rectForPage.

Take Stock

Now that my application builds and runs without crashing, it’s time to ship it, right? Possibly, but in this case I chose not to. Although the application has a mature feature set and benefits from years of evolutionary design, it has become a little bit dated in some ways that I grew increasingly uncomfortable with.

Now that the application is running crash free, I can breathe a sigh of relief. I could ship this thing, but I choose not to. I want to be prudent about what I change for the “1.0 relaunch,” but there are some nuances of the appearance and performance that need tweaking. And to comfortably tweak those things, I’m going to have to selectively port some of the Java classes to Objective C. But after all that hard work getting the application to “shippable,” I want to be careful not to backslide too much. Tune in next time for a detailed look at migrating Java classes to Objective C without disrupting the overall functionality of the application.

Continue reading – Step 2: Life Support.