AppleScript and the class struggle

After a Twitter conversation with ComplexPoint, I have a more elegant solution to the circle counting script I wrote about yesterday.

Both of the problems I had in writing the script are related to AppleScript’s approach to classes and variable typing.

The OmniGraffle dictionary says the fill color property of a shape is of class color, not list, which is presumably why lines like

applescript:
if c is redFill then

didn’t work (recall that variable c is an item in a list of fill colors) and I had to do a coercion,

applescript:
if c as list is redFill then

to get the Boolean expression to ever return true. If AppleScript used duck typing, it would recognize that a color walks like a list and talks like a list and wouldn’t need the coercion.

This morning I wondered if I could get coercion to work the other way. If instead of defining the colors at the top of the script the way I did originally,

applescript:
set redFill to {64614, 0, 1766}
set greenFill to {5246, 39932, 635}
set blueFill to {0, 0, 65416}

I could define them through coercion,

applescript:
set redFill to {64614, 0, 1766} as color
set greenFill to {5246, 39932, 635} as color
set blueFill to {0, 0, 65416} as color

then maybe I could reduce the rest of the script to the short and elegant

applescript:
tell application "OmniGraffle"
  tell layer "Annotations" of canvas 1 of document 1
    set reds to every shape whose name is "Circle" and fill color is redFill
    set greens to every shape whose name is "Circle" and fill color is greenFill
    set blues to every shape whose name is "Circle" and fill color is blueFill
  end tell
end tell

Unfortunately, this didn’t work. It produced an error on the first line:

Can't make {64614, 0, 1766} into type color.

Update May 9, 2019 9:36 AM
ComplexPoint tells me that

applescript:
set redFill to {64614, 0, 1766} as RGB color

doesn’t produce an error, and he’s right. But the rest of the script doesn’t work. The lists generated by lines like

applescript:
set reds to every shape whose name is "Circle" and fill color is redFill

are empty.

But another approach did lead to an elegant solution. OmniGraffle has a way of assigning data to objects that is completely under the control of the user and there’s no class warfare. If you go to the Info pane headed by the gear icon, you’ll see an Object Data section.

OmniGraffle object data inspector

You can type whatever you want into the notes field (the multi-line text area) and be able to access it in AppleScript through the notes property. So I selected all of my red circles (by clicking on one of them and using Edit‣Select‣Similar Objects) and entering “red” into the notes field, as above. After doing the same for the green and blue circles, this short script did everything I wanted.

applescript:
1:  tell application "OmniGraffle"
2:    tell layer "Annotations" of canvas 1 of document 1
3:      set reds to every shape whose name is "Circle" and notes is "red"
4:      set greens to every shape whose name is "Circle" and notes is "green"
5:      set blues to every shape whose name is "Circle" and notes is "blue"
6:    end tell
7:  end tell

No need for counting variables, no need to loop through lists with repeat. This is the kind of script I was hoping to write yesterday.

To see how many circles of each type are in the drawing, I look at the inspector pane in Script Debugger:

New circle counting results in Script Debugger

If I had known this before I started drawing circles, I could have avoided the “select similar objects trick.” Instead, I’d have added the appropriate note to the first circle of each color, and the notes would have propagated every time I duplicated a circle. That’s what I’ll be doing in the future.