Playing with NSTimer

July 1st, 2010 — 2:51pm

It’s been a long while. Part of it is that I’ve been busy working on Hazel 3. And since I’m not interested in writing a book, I’m finding it hard to be motivated to keep posting here. Nonetheless, every so often I have to let something squirt out.

Today, we are going to talk about NSTimer. One thing you may or may not have noticed is that in certain cases where time is suspended (putting your machine to sleep) or altered (changing the clock), NSTimer has a tendency to try and compensate. For instance, say you set a timer to fire in an hour. You close the lid on your MBP and come back 30 minutes later. You’ll see that the fire date for the timer has adjusted to take into account the time it was asleep. Here’s a quickie diagram for those not quite following:

timer-timeline.png

Now, this is great if you wanted a timer to fire an hour later in the machine’s conception of time. But there are times when you want it to fire on the actual date you set on it. Typical example is setting a timer for a calendar appointment. That time is an absolute point in the user’s timeline and you don’t want that timer to shift to compensate for the machine’s timeline.
The basic fix here is when one of these time-altering events occurs, you reset the fire time on the timer to the original time that was set on it when it was created. Luckily, there are notifications for when the machine wakes from sleep as well as when the system clock is changed (this one being new in 10.6). Naturally, one would think to make an NSTimer subclass as the most straightforward way to do this. Unfortunately, it seems that it doesn’t work. Even if you get over the hurdle that NSTimer is actually abstract (like a class cluster), you have to add the timer to the run loop to schedule it. While NSRunLoop seems to accept the NSTimer subclass, it doesn’t ever fire. I don’t know if NSRunLoop is looking for the NSTimer class specifically or if it’s calling some private method that I need to override. If someone else has had any luck doing this, drop me a line.

The alternative is a category. The problem is that we need at least one ivar to store the original fire date. Normally, you can’t add ivars in a category but in this glorious post-10.6 age, we can, via associative references. It’s basically just a dictionary where you can store variables associated with an object. Sure, you could have implemented that yourself but the nice thing is that it handles memory management as well. When the object is dealloc’ed/collected, depending on the memory management you specify, it can also release/collect any associated objects.

So, NSTimer category it is. It supplies the method (among others) +scheduledTimerWithAbsoluteFireDate:target:selector:userInfo: which creates and returns an autoreleased and scheduled timer that will fire on the given date regardless of any time fluctuations/lapses. If all of a sudden the fire date has passed (like if the machine was asleep during the fire date or if the system clock was set ahead), it will fire immediately. Note that calling -setFireDate: won’t quite work in the sense that if the timer ever has to be reset from a time shift, it will be set to the date used when creating the timer, not the one that you set it to afterwards. This is an implementation limitation as I am using a category and can’t override methods (at least not without doing some method-swizzling which is not something I want to deal with). In cases where you need to change the fire date, I’d suggest just creating a new NSTimer.

I’ve also included a test harness where you can see it in action compared against a timer running in normal (non-absolute) mode. When run normally, both timers should fire at the same time but you can test things out by changing the system clock or putting your machine to sleep. You’ll see that the regular timer’s fire date will start to drift out while the “absolute” timer will stick with the time originally set.

I also went ahead and added methods to have NSTimer to use blocks instead of a target/selector combo. Note sure why this wasn’t in there already but I thought it’d be useful.

And that’s not all! Included is my NoodleGlue class. It’s a simple little class that just wraps a block (plus another block for cleanup, if you need it). It’s useful for cases where you want to set a target and selector for some object to use but don’t want to create a new class or method for it. Check out the source code for the NSTimer category to see how its used both with NSTimer (to implement the block API) as well as NSNotificationCenter. In the latter case, there is a blocks-based API but I had special memory management requirements that couldn’t be done with the existing API.

Needless to say, this extension only works on 10.6+. It’s in the latest version of NoodleKit and as a result I’ve made NoodleKit require 10.6 as well. Just build and run the TimerLab target. Fixes and suggestions welcome.

[Update 8:35pm EDT] I neglected to push the code to the github repository. Sorry about that. Everything should be there now.

7 comments » | Cocoa, Downloads, OS X, Programming

Adventures in Email Hosting

December 17th, 2009 — 12:06pm

As I’ve mentioned before, Noodlesoft’s online operations are happily hosted on Slicehost. A while back, I migrated my site and databases from DreamHost to Slicehost and haven’t looked back. Ok, well, not exactly. I didn’t migrate my email hosting over. There were several reasons for this:

  • Setting up and maintaining email software with all the features you want is a bit of a pain.
  • I wanted redundant MX servers. Getting another slice to do that is a bit costly.
  • I didn’t want email service to go down with my web site.

For the time being I left email on DreamHost. It’s a hard habit to kick. They provide tons of bandwidth and disk space for cheap. Of course, given enough time, DreamHost will disappoint you and disappoint they did. They changed the MX servers on me without notifying me. Since I have my DNS hosted elsewhere, I need to be warned ahead of time so I can do the proper DNS change. Not only that, there didn’t seem to be an overlap so mail going to the old MX was being bounced. Not acceptable. It was at this point I decided to get all my business operations off of DreamHost. It only drilled the point home when DreamHost would sporadically fail to resolve some mail aliases for a while afterwards.

What was I looking for in an email hosting provider? Several things:

  1. Reliability. This is where DreamHost seems to falter time and time again. With DreamHost, not only do things seem to go wrong more often, but their handling of the situation is unprofessional.
  2. Easy migration. This seems to be a bit more commonplace now. Providers now have a way where you can point their server at yours and have it suck out all the mail and folders. Even better if you can do it incrementally after the initial run to catch the extra emails that end up going to your old MX during the DNS transition.
  3. Support. I expect support to be accessible, responsive, competent and diligent. This is one of those times where I want to pay. With free, you have no leverage to get them to fix a problem in a timely fashion. This is my business here and I can’t afford to have unresponsive and unhelpful support if the service goes down.
  4. Features. This will be different for everyone but for me, I just need basic things like SSL for inbound (IMAP) and outbound (SMTP) and server-side rules/filtering.
  5. Reasonable resource limits. Some providers limit your bandwidth (both inbound and outbound) and all put limits on disk storage. The limits should be high enough that I don’t notice them.

Google Apps is an obvious choice but frankly, I don’t like how Gmail does IMAP. In addition, I was very unimpressed with Google’s support with Google Checkout and expected the same type of thing with their other services. See point 3 above.

I decided to try FuseMail. I heard some good things about them and pre-sales support was responsive so I signed up for a trial. Unfortunately, it didn’t work out. There were some odd problems here and there but ultimately it was because of unspoken usage limits. They flagged my account when I tried to do a full IMAP sync to my desktop mail client. Their claim was that I was using too many connections and suggested I should set Apple Mail to not download all messages. They violated point 5 in my list so I cancelled my account. Also, while their support was responsive, they weren’t actually always helpful. They seemed to be oriented towards getting a response out quickly instead of actually solving my problem.

I considered FastMail. Poking around on their site, it seemed to be a good fit for features but it felt like they were trying very hard to discourage you from contacting them. Even if you have a ticket system in place, not everyone that needs to talk to you is already a customer. I was turned off by their support pages and put them on the backburner.

I then poked around the Slicehost forums seeing what other people may be using. I discovered that Slicehost has a special deal with Rackspace (who own Slicehost now). I went to their site and was able to have actually useful conversations with sales and support people. I started my trial.

The migration seemed to go smoothly, albeit a bit slower than Fusemail’s migration tool. After the migration was done, I found a problem: all the dates on my messages were set to the migration date and not the actual original date received. After contacting support, I learned that their tool was resetting the IMAP INTERNALDATE which Apple Mail uses as “Date Received” (most other clients use the sent date in the mail header). After some tests with them, they finally fixed the problem internally. They performed a special migration for me and then I pointed my DNS at Rackspace’s servers. It’s unclear to me if/when this fix will get into the main migration tool; if you are considering signing up with them, I suggest contacting their support and asking first.

The verdict: I’m quite satisfied. After the setup hump, things seem to be running smoothly plus the Rackspace servers seem noticeably faster than DreamHost. A recent DreamHost-wide outage made me feel like I jumped ship just in time. I’ll probably keep my DreamHost account around in the short term for some random personal projects (which are expendable) but I’m glad that no part of my business is hosted with them anymore.

• • •

And before I go, I thought I’d share a procedure for those looking to migrate mail servers. Unfortunately, some of these points I came up with after a screw up or two on my part but now you get to benefit from my mistakes:

  1. If you are hosting DNS, turn down the TTL on your MX records. The TTL (time to live) is an indicator as to how long clients can cache this record. You want to turn it down so that when you switch to the new server, the cache won’t be stale for as long.
  2. On your new server, set up all accounts.
  3. On your new server, set up all aliases. Make sure they point to a valid address.
  4. On your new server, if the option is available, set up a catch-all address. This address will receive emails that are addressed to non-existent mailboxes/aliases. If possible, route it to a folder or account that is not used for anything else. Even if you don’t want this on normally, turn it on now until you are sure that all mailboxes and aliases are set up properly.
  5. Test locally. Check with your provider but some should shunt local mail without going to an external MX server (so you can do this before switching DNS). Make sure none of your aliases bounce.
  6. Do the migration. Depending on the size of your mailboxes, this may take some time (letting it run overnight is a good idea). Make sure to verify afterwards. Remember to check those dates!
  7. Update your MX records to point to the new server(s).
  8. When you feel comfortable that things are working ok on the new server, do a final migration to pick up any messages that arrived during the overlap period.
  9. Cancel old account.
  10. Pour yourself a scotch.

Note that steps #1-9 are optional.

Update (Dec. 18, 2009): Oh the irony. A day after I posted this, Rackspace had an outage. One the email hosting side, it seemed to only affect clients connecting to access their accounts. Their mail servers were still able to accept messages during this time which was the more important thing for me. Of course, I’d prefer stuff like this not to happen and time will tell how often it does happen. In the end, it was handled well and support was very quick to answer my follow-up questions after things settled down.

Comment » | Noodlesoft, System Administration

Yet Another Way to Mimic the Artwork Column in Cocoa

October 20th, 2009 — 8:14pm

I was just turned on to a post from Jesper of Waffle Software entitled How to Mimic the Artwork Column in Cocoa. There he approaches the problem of implementing cells in an NSTableView that can span multiple rows. This spurred on Jacob Xiao at Like Thought to tackle the problem in a different manner. I suggest reading both of the above to get a sense of the problem and the possible approaches.

With NSTableView still on my mind from my last diversion, this got me thinking about the problem and I decided to try my own hand at it. I preferred Jacob’s approach as I think it should be all contained within NSTableView if at all possible. I liked the notion of “slicing” up the cell instead of using another view. Playing with it, I came up with a generalized solution. Here are some of the interesting aspects:

  • It can support any NSCell subclass. I do this by using a special wrapper cell. It draws the “full” version of the cell then draws out the slices for each row into the tableview.
  • It determines the spans by finding contiguous rows that have the same object value for the column in question.
  • The above two points are exploited to do some caching such that the “full” cell is only drawn once and the parts composited in place as needed.
  • If the first column is set to span rows, it will only draw the grid for the last row in a span.
  • You can designate multiple columns to do this. In the example project, if you click on the “Album” column, it will toggle between span and non-span mode. The “Artwork” column doesn’t do this because it looks cruddy in non-span mode. I haven’t tested it (see update below) but theoretically, the spans do not have to line up between different columns.

To use it, for any columns you want to have this row-spanning behavior, set the table column class to NoodleRowSpanningTableColumn. Also, it might help to get rid of any intercell spacing in your tableview (at least in the vertical direction). There are probably a few bugs. I do know that reordering columns may cause some grid drawing funniness but for the most part, it seems to work pretty well. Let me know if you find otherwise.

You can check the code for other implementation tidbits. I’ve included a self-contained project below but I’ll eventually polish it a bit and add it to my NoodleKit repository where it can join the rest of my goodies.

Many thanks for Jesper and Jacob for the inspiration and laying down the groundwork for this.

Update (Oct. 21, 2009): I observed odd behavior in that the program would get into a re-drawing frenzy eating up a bit of CPU. This was non-blocking in that I was still able to manipulate the UI (which is why I didn’t notice it at first). I finally figured out that it was the -drawGridInClipRect implementation. I was drawing the grid selectively by changing the grid mask and then calling super on subregions. Unfortunately, changing the grid mask queued up a redraw which started a loop (albeit one that didn’t tie up the event loop). Seeing as I needed to fix the grid-drawing logic anyways, I reworked the whole thing.

The new grid code should now be able to handle multiple spanning columns rearranged in whatever way you want. Here’s contrived example of having multiple spanning columns that don’t necessarily line up:

row-spanning-table.png

I also made some other fixes and improvements so it’s worth re-downloading the project even if you already did so before (I bumped this version to 0.37).

As for integration with the rest of NoodleKit, I still need to decide whether I want to merge this with my sticky row tableview to make some über-tableview. Problem is is that I can’t think of how the two features would interact so maybe it’s best to keep them separate. Thoughts on this are welcome.

Update (Oct. 23, 2009): I’ve just updated the project to version 0.68. There was a crasher when you would click on a non-span row and then move into a spanning cell. Also, the span cells were highlighting when you clicked on a non-span row. Both of these have been fixed. Thanks for Jesper for finding the crasher and Jacob for helping with the fix.

Update (Oct. 25, 2009): I’ve consolidated this functionality along with my previous sticky row header code into a single NoodleTableView class. You can find it in the NoodleKit repository. Future updates to this will be posted there from here on out.

Download NoodleRowSpanningTableViewTest.zip
[This project is no longer being updated here. You can get the latest in the NoodleKit repo]

2 comments » | Cocoa, Downloads, OS X, Programming, Software, User Interface

NoodleKit

September 29th, 2009 — 2:44pm

I’ve finally gotten around to consolidating most of the bits of code I’ve posted on this blog over the years and put it in a repository. While the real NoodleKit is a much more extensive and cohesive toolkit that I use internally, this one will serve as a place where I put the odd scraps that I decide to open source. I guess I could’ve called it NoodleScraps but it doesn’t quite sound as nice.

If you’ve been following my blog for a while then there’s not much new here, codewise. There are a tweaks here and there plus the stuff should be 64-bit ready so it might be worth re-downloading just for that. It will be the place where I put future code so you should keep an eye on it.

The project is structured to build a framework but also has an examples directory (with corresponding targets) to demonstrate the use of the different classes. The current notion is to keep things compatible with 10.5. And, as usual, all code is released under the MIT license.

NoodleKit (hosted at github)

Enjoy.

Comment » | Cocoa, Downloads, OS X, Programming, Quartz, User Interface, Xcode

“Sticky” section headers in NSTableView

September 25th, 2009 — 9:25am

Once again, I find myself with some cool code with nowhere to put it. And, yet once again, you get the reap the benefits.

UITableView on the iPhone has the neat feature where section headers stick at the top after the actual row for it has scrolled off. For instance, in Contacts, if you scroll down, the row for section “A” stays visible so that you know what section you’re in if the section is too large for the screen.

Unfortunately, someone asked how something like this could be done on desktop Cocoa. I say “unfortunately” because I was within earshot and it resulted in me embarking on a journey peppered with annoying run-ins with NSTableView’s idiosyncrasies. Nonetheless, I persevered and came up with something hopefully someone will find useful. As was the case before, I don’t have a use for it myself at the moment, but I feel it would be a shame to let the code go to waste.

I am presenting NoodleStickyRowTableView. I call the section headers “sticky rows” as one can specify any row to stick up top for whatever reason, though by default, it does it for group rows.

The bad stuff:

NSTableView makes it very difficult to cache a row visually. -drawRow:clipRect: should be able to do it but the problem is that it’s too smart for it’s own good. If the row is not visible, it will not draw anything. Add on top of that the impossibility of getting it to draw the special background the group rows use. I tried all sorts of techniques but none were general or reliable enough. My email on the cocoa-dev list was, not surprisingly, ignored. As a result, in the default implementation, I use my own look for the rows and then use a fade-in transition since the sticky row will look different from the actual one. You can see it in the video below where it has a ghost-like effect.

The good stuff:

The good news is that I managed to work it into an NSTableView category. You can think of it as an abstract category if you wish. In your subclass, at the very minimum, override -drawRect: and call -drawStickyRowHeader after calling super. From that one call you will get all the functionality.

Why do it this way? Well, ease of use for one. It makes it so that you can integrate it into your own NSTableView subclass without having to copy and paste large swaths of code or put it all sorts of odd hooks. Secondly, the changes get inherited by NSOutlineView so the functionality is available there and in its subclasses as well.

While I could use the new associated objects functionality in Snow Leopard, I opted for Leopard-compatiblity. This resulted in me coming up with a couple tricks to get around the lack of ivars, including the rarely used -viewWithTag: method. Check the “read me” file and code for details.

There are two different transitions. The default is the fade-in transition as shown here:


<a href="http://www.noodlesoft.com/blog/wp-content/uploads/2009/09/Ghost-Transition.mov">Download movie</a>

As mentioned before, since I couldn’t get the group rows to draw properly and consistently, I use a more generic look for the row. The transition makes it less jarring but it is a compromise of sorts. Not bad when you consider that you get all this by calling just one extra method in your -drawRect:.

But what if you want something more like the iPhone’s version? In your subclass, you can override -stickyRowHeaderTransition to return no transition and then do your own drawing of the rows so that they look the same between the regular and sticky versions. The NoodleIPhoneTableView included in the project shows how this is done with the result being something on par with UITableView:


<a href="http://www.noodlesoft.com/blog/wp-content/uploads/2009/09/iPhone-Transition1.mov" title="iPhone Transition.mov">Download movie</a>

I am releasing this under MIT license as usual. Use it however you want as long as you give me credit somewhere. Please send in any feedback, suggestions, bugs, etc but keep in mind that this is not something I use in my projects so I probably will not be actively maintaining it beyond a certain point. I’ve made a point of making the the API easy to use and to extend so have fun with it.

Download NoodleStickyRowTableViewTest.zip (version 0.18639)

Update (Sep. 29, 2009): I have included this class in my NoodleKit repository so you should check there for future updates.

4 comments » | Cocoa, Downloads, OS X, Programming, User Interface

Hazel 2.3 is out

July 13th, 2009 — 1:16pm

Seems like forever since I’ve done a release. There have been odd setbacks here and there but Hazel 2.3 is out at last.

For those that share your machine with others, you’ll find that the App Sweep feature now allows everyone on a machine to have their support files thrown away when someone throws away an app. Everyone that wants to participate has to enable the feature (look in the “Trash” pane). I would have made this not an option but I had visions of people complaining about others being able to see what apps they are throwing away. So, now there’s a checkbox which personally I’d rather not have.

The feature I’m most excited about is, of course, the one that most of my users probably won’t care about or notice. But, this being a developer blog, I’m sure some of you would be interested. In version 2.2, I added an embedded script editor. The AppleScript editor had syntax highlighting because, well, Apple gives that to you. In 2.3, I’ve added syntax highlighting to the regular shell script editor. Of course, it’s not just for shell scripts. Just set the interpreter you want and if it’s one that Hazel knows about (currently, it’s bash, Perl, Python, Ruby and awk), then Hazel will color your code appropriately. Give it a spin and let me know what you think.

Oh, and I didn’t add any UI to configure the highlight colors. If you really hate the color scheme it uses, you can take your XCode color theme and copy/symbolically link it as ~/Library/Application Support/Hazel/Hazel.xccolortheme. I thought about making Hazel just use whatever is set in XCode but I got the feeling that people weren’t using XCode to edit their non-Objective-C stuff. I may change my mind on this in a future release. Feedback welcome.

And of course, there are a bunch of other new features, improvements, and fixes (and probably bugs).

As for the roadmap, there will definitely be another (free) update of some sort before 3.0. Snow Leopard is coming out in a couple months and I need to make Hazel compatible. If you are running Snow Leopard and would like to test, definitely drop me a line.

Comment » | Hazel, Noodlesoft, Software

Fun with KVC

June 30th, 2009 — 10:33am

[Warning: this article is for entertainment purposes only. Any harm you do to your code or any offense you incur on the sensibilities of other programmers is your responsibility.]

Some time ago, Guy English posted a great and twisted article on key value coding (KVC). He comes up with a nice hack to do stuff with KVC that was never intended. Of course, there are some neat ways you can use KVC without indulging in the craziness that Guy English delves into (and I think he’d be the first to admit that it’s not meant for primetime). The main point remains, which is that using KVC with NSArrays is a great way to avoid coding loops over their contents. You have an array of “Person” objects and want an array of their names (accessible via a -name method)? Just do…

newArray = [array valueForKey:@"name"];

Now, the keys do not necessarily have to be properties of the object. There’s nothing stopping you from calling other methods on your objects that return other objects, provided they don’t require any arguments. For instance, if starting out with an array of strings…

newArray = [array valueForKey:@"lowercaseString"]

…will generate a new array of lowercase versions of the original strings. Actually, the methods don’t even have to return objects. Want to convert an array of numbers in string form to numbers? Try the following:


array = [NSArray arrayWithObjects:@"1", @"2", @"3", @"4", nil];

newArray = [array valueForKey:@"intValue"];

This gives you an array of NSNumbers. KVC auto-boxes scalar types as needed. It’s a great way to do conversions/transformations of arrays of objects in one fell swoop.

Suppose you want to do a deep(er) copy of an array of objects (not only copy the array, but each of its objects as well):

newArray = [array valueForKey:@"copy"];

There is the issue, though, that the copies in the array now have a retain count of 1 and will be leaked unless extra care is taken. But no need write a loop to autorelease the objects. Instead, we can use keypaths to create chains:

newArray = [array valueForKeyPath:@"copy.autorelease"];

Retain it now and release it later or just let it get autoreleased. Either way, everything cleans up nicely.

And if you have a bunch of NSMutableCopying-conformant, immutable objects and want to convert them to their mutable counterparts:

newArray = [array valueForKeyPath:@"mutableCopy.autorelease"];

That gives us an array of autoreleased mutable copies.

Sure, not super mind-blowing but hopefully something above may save you some keystrokes. If you have your own KVC tricks, post them here in the comments.

[Update 2009-06-30]

This all started out with an conversation I had with Guy about wacky KVC stuff. The original title of this post was “KVC Abuse” but after a few edits, it got lost. I added the warning at the top but I’ll make it clear here: this is an abuse of KVC. This whole post is an indulgence.

I avoided a whole discussion of implementing HOM (Higher Order Messaging) type functionality in Objective-C but here’s a method you can use in an NSArray category:

- (NSArray *)map:(SEL)selector
{
	NSMutableArray		*result;
	
	result = [NSMutableArray arrayWithCapacity:[self count]];
	for (id object in self)
	{
		[result addObject:[object performSelector:selector]];
	}
	return result;
}


You don’t get the auto-boxing or the keypath stuff but the end result is still succinct and convenient for the basic case:


newArray = [array map:@selector(lowercaseString)];

Of course, my own version that I use has a more verbose method name (-transformedObjectsUsingSelector:) but no matter how you slice it, it will generate less bile from other developers.

6 comments » | Cocoa, OS X, Programming

413 days

June 19th, 2009 — 1:42pm

No, not the number of days since I last posted, though yes, it’s been a while. I just happened to be looking at my server stats and noticed that I had an uptime of 413 days. I guess this post would have been more timely and poetic at the 1 year mark but I have to say that I’m pretty impressed with Slicehost (warning: it’s affiliate link so if you sign up using that link, I get some credit). The reboot of my slice way back when was when I upgraded to a bigger slice.

I’m sure other people on other providers can post similar numbers but seeing as I had come from DreamHost, I find it pretty amazing. And yes, that is an affiliate link as well as I still use them for other things – I can be a whore at times, too.

Looking back over the past 413 days, I only recall contacting Slicehost support once, and that was for an administrative issue. I do remember some network problems once but those were resolved within minutes. By the time I asked around in the IRC channel about it, it was fixed. For the most part, I almost never think of Slicehost. The fact that I can take them for granted says something about their reliability.

How have the other services and tools I’ve been using on my site fared during this time?

PotionStore has been great. And now that it has an in-app Cocoa store component, it’s even better. I’ve currently integrated it into the latest Hazel beta (forum account required) if you want to see it in action. Just keep in mind that while the app is beta, it is connected to the live store so all sales are real.

Between the two main transaction processors I use, PayPal has been far better than Google Checkout. Very few issues with the former (knock on wood). Unfortunately, when there has been an issue with Google Checkout, I’ve had to hunt to find a way to even contact them and then the email support has been pretty crummy. On the flip side, I can find PayPal’s phone number quickly and their support people seem very knowledgeable and when the call is over, the issue is resolved. Fortunately, Google Checkout accounts for a small number of sales.

For server monitoring, I have been using Montastic. At least, I thought I was. Recently I checked my account only to notice that it wasn’t really monitoring. After unwedging it, it seemed to not like my store certificate, bugging me with alerts regularly. It also seemed to be sending MIME mail of some sort which end up as MMS on my phone. They’ve got pics of my server on fire or something? Annoying and potentially expensive. I’ve disabled it so suggestions for an alternate server monitoring service are welcome.

I could end this post with “Here’s to another 413 days” but I know I have to do a server upgrade at some point which will break my streak. Nonetheless, it’s good to know that downtime occurs on my terms and not my provider’s.

4 comments » | System Administration

Understanding Flipped Coordinate Systems

February 2nd, 2009 — 12:25pm

Flipped coordinate systems always seem to confuse people, myself included. After working things out with pen and paper or handpuppets, I always feel like they’re not quite correct so I thought I’d resolve things here. If anything, I can refer back to it whenever I forget this stuff (which happens every couple years). First a refresher in case you need it: Cocoa Drawing Guide: Flipped Coordinate Systems.

The main thing to keep in mind is that while a flipped coordinate system is basically translating the origin to the top left and flipping things vertically, semantically it doesn’t mean that everything is upside-down. Images, text and other elements are supposed to render right-side-up. The flipped coordinates should affect where those elements are placed, not how they are rendered. Being flipped is a higher level notion and is separate from the
CTM. While setting something as flipped will flip the CTM, modifying the CTM without setting the graphics context as flipped results in everything being entirely upside-down, which is different. Many of the Cocoa classes and functions take flipped coordinate systems into account and will draw right-side-up for you but it gets tricky with images.

There are two sets of methods in NSImage to render it in a graphics context: the -composite... methods and the -draw... methods. I’ve put together an interactive example app to help illustrate the differences. It might help you to download it now so you can follow along. The figures included in this article are from that app

The -composite... methods actually seem to work in the sense that the image is always right-side-up. The problem is that it draws “upward” from the draw point regardless of the coordinate system. The result is you have to calculate the compositing point at the upper left corner of the image instead of lower left. This is the result of these methods not taking the CTM into account in terms of scaling and rotation. That also means that if you scale your view up, the images won’t scale with it. While you can use this to your advantage in some cases (like rendering resize handles or labels which you want to stay the same size regardless of zoom), it’s usually not the right way to do it.

composite-scaled.png

View scaled at 2x. Notice how the composited version does not scale.

The -draw... methods on the other hand follow the CTM properly. That also means that if the view is flipped, so is the image, so you need to adjust accordingly. So while these methods obey the CTM, they do not take into account the flipped flag of the context which would be the cue to draw things right-side-up.

draw-flipped.png

The draw... routines follow the CTM, but not the flipped flag.

Now, to complicate things even further, NSImage’s themselves can be flipped. The reason for this isn’t to make the images upside-down. It’s there to provide a flipped coordinate system for whenever you draw into it (i.e. lockFocus on it, draw, then unlockFocus). It’s useful for when you want to tell a flipped view to draw into an NSImage, for instance. It’s basically like a flipped view; you don’t expect a flipped view to draw upside down, no matter which view you set as its superview. A subtlety to be aware of is that flipping an image loaded from a bitmap does not make too much conceptual sense (you are changing the coordinate system after the content has already been “drawn”) but it does have the practical effect of flipping the image vertically, which seems to be an implementation detail. Yes, flipping the image will work to “correct” orientation problems in most cases but, depending on where your image gets its data (for instance if it has an NSCustomImageRep like the example app – see below) and whatever implementation-specific details lurk in NSImage, you may end up with undesired or inconsistent results.

As mentioned above, I’ve put together a little interactive example app (Leopard-only) to show how the different methods behave. In addition, I’ve written methods in an NSImage category (-drawAdjusted...) which will render the image correctly regardless of the flipped status of the coordinate system it draws into. As suggested in Apple’s docs, it does a transform reversing the flip, draws the image, then reverts the coordinate system back.

The image itself is drawn by code, not loaded from a bitmap. The reason for this is that I also wanted to illustrate using flipped images. It draws an arrow and some text to indicate the proper orientation. When flipped, the drawing code is exactly the same in that no coordinates are recalculated. The text content is changed to compensate for the new orientation. Notice how the text renders right side up no matter the flip state of the image; an indication that the NSString drawing methods are “flip-aware.” Also, it shows how to check the graphics context to get its flipped status so you can make your own drawing routines flip-aware as well.

Unfortunately, not everyone draws flipped images correctly. One place in particular is NSView’s/NSWindow’s -dragImage:at:offset:event:pasteboard:source:slideBack: method which will draw flipped images upside-down. Since you can’t control how the image is drawn, you can instead draw your flipped image into another non-flipped image and pass that in. I’ve added a method to the NSImage category to do this and you can check out the result in the example app (you can drag the image out of the views though only the last one has the corrected version).

And what if you actually want to draw everything upside-down, you irrepressible nut? Well, apply your own transforms using NSAffineTransform or CGAffineTransform. Just remember to concat the transform and not set it (a good general rule when using affine transforms). As long as you don’t tell any classes that you’re flipped, it should work out.

Hopefully this didn’t make things even more confusing (and also, hopefully, my interpretation of all this is correct). If you are still lost then just follow these rules:

  1. Do not set images as flipped unless you know what you are doing.
  2. Use the -drawAdjusted... methods in my category (or similar technique) to do all your image drawing.
  3. If you didn’t listen to rule #1 and you have a flipped image and it is showing up upside-down even when following rule #2, then use the -unflippedImage method in my category to get an unflipped version of the image and use that instead.
  4. Never go in against a Sicilian when death is on the line.

That should handle most cases you run into. And trust me on rule #4.

6 comments » | Cocoa, Downloads, OS X, Programming, Quartz

The Invisible Interface: Stealing Prefs

January 27th, 2009 — 1:52pm

In this installment of The Invisible Interface, we are going to look at stealing preferences. What is stealing preferences? Simply enough, it’s using the preferences of some other app instead of having your own for a particular feature. The point of this is to avoid having to provide a separate interface for settings when the user has already made their choices known somewhere else.

The key here is finding cases where your functionality is more centrally used somewhere else. I’m going to use Hazel as an example but hopefully these will illustrate the point well enough for you to look for where you can apply it yourself.

Spring-Loaded Folders

For those that don’t know, Finder has a feature called spring-loaded folders. What this does is when you drag a file over a folder, after a delay, it will flash and then open that folder so you can drill deeper. It allows you to navigate the folder tree without having to let go of the file you are dragging.

Hazel implements spring-loaded folders as well. It works when you want to move/copy rules in between folders. Hovering the dragged rule over a folder in the list on the left will cause the view to switch to the rules for the hovered-over folder allowing you to drag back into the rule list and place the rule where you want it. [On a side note, implementing this uncovered a bug in NSTableView (at least on Tiger; have not checked with Leopard) resulting in me doing my own implementation of NSTableView's drag and drop.]

Under Finder’s “General” tab, you’ll see a checkbox and slider to configure these settings. Your first thought may be to provide a similar UI in your app. But why? Does the user really care about tweaking this for each app it appears in? It seems like whatever setting works in Finder will be fine wherever else it is used so why not just use Finder’s preference?

Commonly Used Folders

In Hazel, when you specify a destination folder for some actions (like move and copy), there is a pop-up of folders. You’ll notice that there’s a list of common folders at the end of the pop-up menu. If you look a bit closer, you may notice that these are the same folders in the sidebar of your Finder windows. Those folders are common destinations for files so it’s a good list for Hazel to use. By grabbing that list from Finder, Hazel avoids any sort of extra maintenance/interface for managing that list.

AppleScript Editor

In 2.2, Hazel introduced inline editing of scripts. You are provided with a mini-AppleScript editor right in Hazel’s rule interface. Now, there are potentially different things you can tweak to make the editor suit your needs, such as line wrapping, tab widths and whether to use the script assistant. But if you poke around Hazel’s UI, you’ll see that there’s no interface to set these. That’s because Hazel steals these preferences from Script Editor. If someone is serious enough about editing AppleScript that they care about these settings, there’s a good chance they have Script Editor installed and already set these preferences. By using its preferences, there is a consistency of user experience between the two editors.

• • •

Of course, you can’t do this everywhere. It’s best suited when the functionality is primarily used elsewhere and you are echoing it in your own app. The apps Apple ships with the system are an easy mark since you can usually rely on them being installed and they tend to be the places where common functionality is defined. Overall, the result is a less tweaky and cluttered interface and a more seamless experience with the rest of the system.

Doing the Heist

You can grab other apps’ preferences using either CoreFoundation or Cocoa. With Cocoa, NSUserDefaults is your go-to guy. -persistentDomainForName: does what you want. Give it a bundle ID and in return, you get a dictionary of preferences. What would’ve made more sense is something like +userDefaultsForName: which would return an NSUserDefaults instance, but hey, it’s not like Apple is hiring me to do API design. With CoreFoundation, you can use CFPreferencesCopyAppValue() to pick individual preferences. Again, a bundle ID is needed.

And I can’t leave without placating the more pedantic among you that have to point out the potential dangers of doing this. Therefore, I must note that there is some risk in doing this as most apps do not document their preference settings and they can change at any time. Having default values of your own for these settings should minimize the risk, at least buying you time until you can re-work things to use the new schema. That said, if this is for some critical/primary functionality in your app, it might behoove you to have your own settings for it. As they say, invest only what you can afford to lose, or, to milk the stealing metaphor, don’t do the crime if you can’t do the time. And while we’re at it, just say “no” to drugs, kids.

Comment » | Cocoa, Hazel, OS X, Programming, User Interface

Back to top