| Fraser Speirs ( @ 2005-01-07 09:48:00 |
Incremental GTD App Hacking
Merlin and I talked a fair bit recently about what we want to see in a mythical perfect application for Getting Things Done.
I had spent some time in Interface Builder trying to make something work, but found it really hard. I had some ideas for particular features I wanted, but I couldn't really bring it together into a coherent whole.
Here's what I came up with originally:

I wanted better support for the GTD-specific objects in the workflow - next actions and projects. So I put together the following UI for projects:
The things I wanted from this UI were the following:
- A single list of all projects ongoing
- Del.icio.us style tags to support GTD context "work", "home", "calls" etc.
- Links to local files related to project ("project support materials" in GTD terms) (not shown in this screenshot, but was in another rev.)
- Links to the Address Book cards of related people
Another thing I was curious about was critiquing. Years ago, I used to use a tool called ArgoUML. This was a UML design tool with one feature that I don't think I've seen in many apps before or since. It would look at the model you had designed and tell you if you were doing stupid things. For example "This class has no instance variables" or "This class has massive fan-out, consider refactoring".
GTD is a pretty free-form workflow, so it's hard to come up with more than one or two hard rules that you would want to enforce in an application. In fact, the only one that I can think of right now is "every project must have a next action". Anyway, I thought it would be interesting to explore the notion of critiquing your workflow.
Anyway, I really didn't want to write an application for this if I didn't have to. I've recently been getting into OmniOutliner 3, and the final version was just released yesterday. I started wondering to myself if I could implement the features I wanted in this mythical GTD app within OmniOutliner. Here's how I went about it.
Firstly, here's what the outline looks like (click for larger annotated shot):

1. Use OmniOutliner Professional
The drawer in OO3 Pro acts as the source list in my UI above.
2. Use standard prefixes to mark the type of rows
In OO, I start the text of a row with [NA] if it's a next action. This makes it really easy to pull out all the next actions either with a search for "[NA]" within Outliner or by Applescript. If you're disciplined about this, you can find all your next actions with one line of code:
I wrote a simple Applescript that will pull out that list of [NA] rows and populate a new document with them:
You could also script the addition of the [NA] prefix to selected rows, or write a "Make new Next Action" script which would prompt the user for the text and insert it with [NA] prepended.
This general trick also lets you implement tagging. I use the following general approach:
- In principle every row can be tagged, not just projects or next actions
- To be considered "tagged" a row has a child row whose topic is "Tags" and that child itself has one or more children whose topic starts with "[Tag]" (even my grammar is starting to sound like Applescript!)
Again, it makes a lot of sense to manage this with Applescript. Here's a script called "Tag selected rows":
What this does is, for every selected row, creates a "Tags" child if one doesn't exist and then add a child to that with the user-provided tag name. As before, you can generate a list of all your tags with a search or a line of Applescript:
And here's the corollary to the above "Tag Selected Rows" script. It's called "Select Tagged Rows":
The smarts in there is that, given that we know that a tagged row has a "Tags" child and that row has n children named "[Tag] *", whenever you find a row beginning "[Tag]" you just ask for the "parent of parent of" the row, and you have the row that we consider "tagged". Neat.
3. Use styles to make row types distinct
I like to use distinct styles to visually attract me to certain things. For example, next actions are a bright red colour. If you create named styles in OO3, you can nicely use the prefix-tagging approach above to style particular types of rows.
For example:
4. Drag Address Book cards into OO for "Relevant people"
Whilst you can open Address Book and drag stuff in, it would be nice of OO had a bit more support for this. If Omni incorporated an Address Picker into OO, that would be one less Exposé dance to do.
Also, OO doesn't really allow you do "do" much with an Address Card embedded in an outline. All you can do is really "open" it back into Address Book. It would be great if OO gave you similar options to the little menus in Address Book when you select a person record in that app: 'IM this person', 'Email this person'...etc.
5. Alias files into OO for "Project Support Materials"
This last one is pretty obvious. You can embed links to disk files in an OO outline, which gives you ready-made Project Support Materials lists.
Downloads
You can download these AppleScripts and my example Outline at http://www.mycamera.org.uk/mirror/o ogtd.dmg
Merlin and I talked a fair bit recently about what we want to see in a mythical perfect application for Getting Things Done.
I had spent some time in Interface Builder trying to make something work, but found it really hard. I had some ideas for particular features I wanted, but I couldn't really bring it together into a coherent whole.
Here's what I came up with originally:

I wanted better support for the GTD-specific objects in the workflow - next actions and projects. So I put together the following UI for projects:
The things I wanted from this UI were the following:
- A single list of all projects ongoing
- Del.icio.us style tags to support GTD context "work", "home", "calls" etc.
- Links to local files related to project ("project support materials" in GTD terms) (not shown in this screenshot, but was in another rev.)
- Links to the Address Book cards of related people
Another thing I was curious about was critiquing. Years ago, I used to use a tool called ArgoUML. This was a UML design tool with one feature that I don't think I've seen in many apps before or since. It would look at the model you had designed and tell you if you were doing stupid things. For example "This class has no instance variables" or "This class has massive fan-out, consider refactoring".
GTD is a pretty free-form workflow, so it's hard to come up with more than one or two hard rules that you would want to enforce in an application. In fact, the only one that I can think of right now is "every project must have a next action". Anyway, I thought it would be interesting to explore the notion of critiquing your workflow.
Anyway, I really didn't want to write an application for this if I didn't have to. I've recently been getting into OmniOutliner 3, and the final version was just released yesterday. I started wondering to myself if I could implement the features I wanted in this mythical GTD app within OmniOutliner. Here's how I went about it.
Firstly, here's what the outline looks like (click for larger annotated shot):

1. Use OmniOutliner Professional
The drawer in OO3 Pro acts as the source list in my UI above.
2. Use standard prefixes to mark the type of rows
In OO, I start the text of a row with [NA] if it's a next action. This makes it really easy to pull out all the next actions either with a search for "[NA]" within Outliner or by Applescript. If you're disciplined about this, you can find all your next actions with one line of code:
set nextActions to every row of front document whose topic starts with "[NA]"I wrote a simple Applescript that will pull out that list of [NA] rows and populate a new document with them:
tell application "OmniOutliner Professional"
set sourceDocument to front document
set myDoc to make new document
set nextActions to every row of sourceDocument ¬
whose topic starts with "[NA]"
repeat with aRow in nextActions
set props to {topic:(topic of aRow)}
set theNewRow to make new row ¬
with properties props ¬
at end of children of myDoc
end repeat
end tell
You could also script the addition of the [NA] prefix to selected rows, or write a "Make new Next Action" script which would prompt the user for the text and insert it with [NA] prepended.
This general trick also lets you implement tagging. I use the following general approach:
- In principle every row can be tagged, not just projects or next actions
- To be considered "tagged" a row has a child row whose topic is "Tags" and that child itself has one or more children whose topic starts with "[Tag]" (even my grammar is starting to sound like Applescript!)
Again, it makes a lot of sense to manage this with Applescript. Here's a script called "Tag selected rows":
tell application "OmniOutliner Professional"
set tagName to text returned of ¬
(display dialog "Tag selected rows with" ¬
default answer "newTag" buttons "OK" ¬
default button "OK")
set theText to "[Tag] " & tagName
set selectedRows to selected rows of front document
repeat with myparent in selectedRows
set childlist to (children of myparent whose topic is "Tags")
if (count of childlist) = 0 then
set tagChild to make new row ¬
with properties {topic:"Tags"} ¬
at end of children of myparent
else
set tagChild to first item of childlist
end if
make new row with properties {topic:theText} ¬
at end of children of tagChild
end repeat
end tell
What this does is, for every selected row, creates a "Tags" child if one doesn't exist and then add a child to that with the user-provided tag name. As before, you can generate a list of all your tags with a search or a line of Applescript:
set tags to topic of every row of front document whose topic starts with "[Tag]"And here's the corollary to the above "Tag Selected Rows" script. It's called "Select Tagged Rows":
tell application "OmniOutliner Professional"
set sourceDocument to front document
set tag to "[Tag] " & text returned of ¬
(display dialog "Select which tag?" ¬
default answer "tag" buttons "OK" default button "OK")
set tagRows to every row of sourceDocument whose topic is tag
set taggedRows to {}
repeat with aTagRow in tagRows
set taggedRow to parent of parent of aTagRow
set taggedRows to taggedRows & {taggedRow}
end repeat
select taggedRows
end tell
The smarts in there is that, given that we know that a tagged row has a "Tags" child and that row has n children named "[Tag] *", whenever you find a row beginning "[Tag]" you just ask for the "parent of parent of" the row, and you have the row that we consider "tagged". Neat.
3. Use styles to make row types distinct
I like to use distinct styles to visually attract me to certain things. For example, next actions are a bright red colour. If you create named styles in OO3, you can nicely use the prefix-tagging approach above to style particular types of rows.
For example:
tell application "OmniOutliner Professional"
set theStyle to first named style of front document ¬
whose name is "Next Actions"
set theRows to every row of front document ¬
whose topic starts with "[NA]"
repeat with r in theRows
set style of r to theStyle
end repeat
end tell
4. Drag Address Book cards into OO for "Relevant people"
Whilst you can open Address Book and drag stuff in, it would be nice of OO had a bit more support for this. If Omni incorporated an Address Picker into OO, that would be one less Exposé dance to do.
Also, OO doesn't really allow you do "do" much with an Address Card embedded in an outline. All you can do is really "open" it back into Address Book. It would be great if OO gave you similar options to the little menus in Address Book when you select a person record in that app: 'IM this person', 'Email this person'...etc.
5. Alias files into OO for "Project Support Materials"
This last one is pretty obvious. You can embed links to disk files in an OO outline, which gives you ready-made Project Support Materials lists.
Downloads
You can download these AppleScripts and my example Outline at http://www.mycamera.org.uk/mirror/o