setDoubleAction with Editable Cells in Leopard

December 19, 2007

If you set an NSTableView’s double action in Tiger or earlier by something like:

	[tasksTable setTarget:self];
	[myTable setDoubleAction:@selector(doDoubleClick:)];

doDoubleClick would only get fired for clicks on blank cells, headers, or uneditable cells. The documentation still reflects this. In Leopard, this is no longer the case: doDoubleClick gets called no matter what. If you want the old behavoir - that is, if you want the cells to receive the edit action, you must do it manually. Clickedrow is -1 for headers and empty cells. Thus, the code would look something like:

- (IBAction)doDoubleClick:(id)sender {
	if([sender clickedRow] == -1){
		//your special action goes here
	}
	else {
		[myTable editColumn:[sender clickedColumn]
				row:[sender clickedRow]
				withEvent:nil select:YES];
	}
}

Hope this helps somebody scratching their head with the same issue!


TaskView Application And Working with CalCalendarStore

December 18, 2007
picture-11.jpg

I’ve been really excited about some of the new features in Leopard and the Calendar Server is a big one. To try it out, I’m writing a simple GTD-inspired task manager that interacts with the calendar server so that events are pulled and pushed dynamically to and from the server. The idea here is an app that takes just the data from iCal’s todos and puts it into a view that is more GTD friendly. The benefits are that you never have to sync and all of your information is always in iCal (thus, it works great with iPhones, Blackberries, etc). On the other hand, you are limited to storing information that iCal tasks already have (see my implementation for Projects below).

I’m using Calendars for Contexts so any calendar with begining with an @ symbol will be picked up and treated as a context. The @Inbox calendar gets a special icon, and in the future I’ll be letting you set icons for different calendars and perhaps customize the Context tolken (ie ‘@’). If you really want custom icons now, open the resource bundle. You’ll see a file named @Inbox.png. If you drop another image in there - say named @Work.png - then your @Work calendar will get the image.

The eventual plan is to make the tasks viewable as projects in an hierachal (outline) view with clever delimiters in the task names to save state in. Thus, a project for mail a package might look something like this when you are viewing it in ical

1! Mail Package
1* Get Stamps
1* Find a box
1* Go to the post office

This later feature for project is not yet implemented, and I’d love some feedback if you have a better idea of how I might structure the delimter syntax for Projects. The goals would be that it is clear and obvious, and that it displays in the right order when tasks are sorted by name in iCal (I’m not sure what most phones use as a sort ordering, but i’d be good to obey that too!).
In the meantime, I thought I’d go along a post the app and the code, which might be helpful to people trying to understand the basics of interacting with the calendar server. Keep in mind that this code is UNFINISHED, has not been well tested, and MANIPULATES ITEMS IN THE CALENDAR SERVER. It should go without saying that you need to backup your iCal files if you want to try this app. Also, it requires OS 10.5, whence the Calendar server was introduced.
[Download the App]
[Download the Source Code]

How interacting with CalCalendarStore works:
About all that Tasks Controller class does is set a sort descriptor for the tasks and do the initial grab of the calendars from the server, telling each resulting context to initialize itself using the calendar. The Task and Project controllers are currently unimplemented. The Context class is where most of the magic happens.
In Context’s initializer, you’ll notice two things. First, we add the context class as an observer of changes to its ‘theTasks’ array. Because we are using Cocoa Bindings to handle adding and removing of tasks in the UI, we need to observe changes to the array so that when they happen, we can push them to the Calendar server. More on that below.

[self addObserver:self forKeyPath:@"theTasks"
			  options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld)
			  context:NULL];

Additionally, we want to observe changes to the tasks themselves, which is why when we add them to theTasks, we also call addTaskAsObserver on them, which adds observers of the tasks’s ‘iscompleted’, ‘title’, and ‘datestamp’ properties.

-(void) addTaskAsObserver:(CalTask *)task {
	[task addObserver:self forKeyPath:@"isCompleted"
			  options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld)
			  context:NULL];
	[task addObserver:self forKeyPath:@"title"
			  options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld)
			  context:NULL];
	[task addObserver:self forKeyPath:@"dateStamp"
			  options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld)
			  context:NULL];
}

Going the other way, we just register with the notification center to receive updates when the external calendar store changes. When that happens, we call the method updateTasks.


[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateTasks :)
												 name:CalTasksChangedExternallyNotification object:[CalCalendarStore defaultCalendarStore]];

The first thing to note in the updateTasks method is that it removes itself as an observer of changes to theTasks at the begining of the block and adds itself again at the end. This is to prevent an inifinite loop of notifications that would happen if theTasks was trying to save back to the server the updates we are about to do to it.
We look at the three types of possible updates: added tasks, deleted tasks, and removing tasks. This code is based on the example code in Simple Calendar, which is a good resource for learning about the Calendar server, although the code seems a bit dated. For example, our code for adding task objects when there are new tasks on the calendar server (note that the addTask method below calls addTaskAsObserver on the new task!):

 NSArray *insertedTasks = [[notification userInfo] valueForKey:CalInsertedRecordsKey];
    if (insertedTasks){
		for(NSString *uid in insertedTasks) {
			CalTask *task = [[CalCalendarStore defaultCalendarStore] taskWithUID:uid];
			if([task.calendar.title isEqualToString:self.title]) {
				[self addTask:task];
			}
		}
    }

The other big chunk of code is implementing observeValueForKeyPath, which is called when the things we observed above change (ie the user makes a change in the UI to theTasks or its tasks). Again, we need to avoid an infite loop, so it temporarily halts change notifications from the server that we are about to save to. The object that is changed and thus received by observeValueForKeyPath can be of two types: CalTask or Context - the former if the user changed a task’s properties, and the latter if they added or removed a task to theTask. We check to see which one is the case and then do the appropriate thing. For example, if the object is a CalTask, do something like:

if ([[CalCalendarStore defaultCalendarStore] saveTask:[object copy] error:&taskSavingError] == NO){
			NSAlert *alertPanel = [NSAlert alertWithError:taskSavingError];
			(void) [alertPanel runModal];
		}

The other case is a little trickier, but it is self-explanitory and I won’t post it here.

All in all, its not too hard to interact with the Calendar Server, and I expect there will be a lot of really great apps that result from this awesome new feature in Leopard. Please post comments, questions, bug reports or ideas!

Update: A little bug fix in the code and above explination. dueDate saving was turned off and when I turned it on I was getting infinite calls of observeValueForKeyPath. Why? It turns out that saveTask does not make a copy of the object getting passed, so when the dueDate was getting validated by iCal it was causing the original object to get updated, which then got revalidated by the bindings NSDateFormatter, ad infinitum! The new code properly saves dueDates, and saves a copy of the task instead of the original to prevent the original from being edited by iCal. I’ve updated by example code above to reflect this, and posted new versions of the code and app.


Code Posting in WordPress (with Marsedit), Recap

December 12, 2007

Picture 1.jpg
So I had a brief issue with a previous post after migrating to wordpress - it converted all my quotes to fancy quotes and thus made the code unpasteworthy. My first reaction was to hard code all the quotes and that did work, but a little more research reveal that wordpress.com now has built-in source code displaying (including syntax highlighting) via the sourcecode tag. Using Marsedit, you can define a macro that does it all for you, as follows:
Opening Tag:

[sourcecode language='#asktext Enter Language:(cpp,csharp,css,delphi,java,jscript,php,python,ruby,sql,vb,xml)#']

Closing Tag:

[/sourcecode]

Here’s how some Ruby code might look:

def extended_euclid(a,b)
  x,y,r,m = a, b, a % b, Matrix.identity(2)
  while r > 0
    q = (x/y).to_i
    x,y,r,m = y, r, y % r, m * Matrix[[0,1],[1,-1 * q]]
  end
  return [y,m]
end

Fifty Percent Grey

December 9, 2007

Short (3 minute) Movie. Check it out.


New Home @ WordPress!

December 5, 2007

I’ve finally gotten arround to moving to wordpress, which I should have done a long time ago, and instead did when I should be working. If you are reading this via RSS, please update your feed to the new one at wcrawford.org. Now on to the CSS…


In Portland?

November 27, 2007


audio, visual, textual, phenomenal.
performance, participation, collaboration.

A plug for an art opening, music and dinner event happening this Saturday, Dec. 1 at 5 oclock. It’s at the store on 37th and Cora. Several special friends and guests will be peforming, presenting or shown there including:

  • Avy Mallik
  • Benjamen Blake
  • Benjamen Lingo
  • Ethan Rafal
  • Eva Cruz
  • Jason Powell
  • Jem
  • Lars Ysla
  • Lila roo Duncombe-Lieber
  • Lotus Grenier
  • Mariza Ruvalcaba
  • Mickey Murch
  • Norah Hoover
  • Rosanna Nafziger
  • Selecta Folian
  • Stephanie Hinshaw
  • (me)

If you’re around and interested, please do come.


Does anyone else find this weird?

November 21, 2007

Taken in Preview.app. I find the space between the horizontal and vertical scrollers mildy disconcerting.


Preview.app now supports auto-reloading!

November 6, 2007

Nice new feature in Leopard I haven’t seen mentioned anywhere: Preview.app now supports auto-reloading for PDF’s. This is great for anyone that uses LaTex, and it just might make TextMate-Latex Bundle-Preview.app the ultimate Texing enviornment.


Still no leopard

October 28, 2007

While Apple has finally shared the GM build with its premier and select members as of yesterday evening (days after some consumers received it and the build leaked to the internets, and a week after journalists), student members will be waiting about a week to receive it in the mail. I wonder why they couldn’t allow us to download it now? I’ve been itching to install for months, and while student membership is a great deal, it sucks that its members - who are among the most enthusiastic users - have to wait the longest. Ah well. Anyone care to tell me if my software still works? On a side note, the PDX launch party was litterally swarmed with iphone-toting Mac nerds. Kind of a scary sight, but heart-warming all the same.


Hotel Chevalier

September 27, 2007

The prequel to Wes Anderson’s upcoming The Darjeeling Limited.
Free via the iTunes Music Store

Via Daring Fireball.