I make a lot of diagrams for papers, and I found myself annotating shapes, then as the diagram changes I have to move the anotations around and it was difficult to get them correctly placed.
To solve the problem I created two scripts. The first Annotate creates an annotation for each selected object. It prompts for the position, and the margins.
The Reposition Annotation script only works on Annotations created with the annotation script. It allows the annotation to be repositioned with a different margin or on a different side of the object. Note you can select multiple objects, select no change, and then simply adjust the margins.
I have script versions and applications created with Applescript studio, that have a much better interface. Scripts first
To solve the problem I created two scripts. The first Annotate creates an annotation for each selected object. It prompts for the position, and the margins.
The Reposition Annotation script only works on Annotations created with the annotation script. It allows the annotation to be repositioned with a different margin or on a different side of the object. Note you can select multiple objects, select no change, and then simply adjust the margins.
I have script versions and applications created with Applescript studio, that have a much better interface. Scripts first
Code:
-- Annotate OmniGraffle objects by Dan Thomsen -- customizable parameters set label_font to "Arial Narrow" set label_font_size to 12 set annotate_color to "Steel" set annotate_thickness to 0.5 set layer_name to "Annotation Layer" set annotate_label to "annotate here" -- parameters that the user specifies global horizontal_margin global vertical_margin global horizontal_position global vertical_position global annotation_position set horizontal_margin to 20 -- default value set vertical_margin to 20 -- default value -- valid values are left, center right set horizontal_position to "left" -- default value -- valid values are top, middle, bottom set vertical_position to "top" -- default value -- constants -- these are used to create magnets on the two ends and the middle of the edge set left_edge to {{-0.5, -0.5}, {-0.5, 0}, {-0.5, 0.5}} set right_edge to {{0.5, -0.5}, {0.5, 0}, {0.5, 0.5}} set bottom_edge to {{-0.5, 0.5}, {0, 0.5}, {0.5, 0.5}} set top_edge to {{-0.5, -0.5}, {0, -0.5}, {0.5, -0.5}} -- variables set annotation_size to {0, 0} -- used to store the size of the annotated text set text_alignment to left -- default value try tell front window of application "OmniGraffle Professional" activate -- all annotations are put on their own layer -- if the annotation layer does not exist create it if (name of layers) does not contain layer_name then make new layer at beginning of layers with properties {name:layer_name, visible:true, prints:true} end if -- get the annotation layer repeat with alayer in layers if name of alayer is equal to layer_name then set annotation_layer to alayer exit repeat end if end repeat set objects to selection if (length of objects < 1) then error "no objects selected" end if if not my prompt_user() then error "user abort" end if -- the position is saved in the annotation so that the margins can latter be adjusted by running the Reposition Annotation script set annotation_tag to {annotation:true, position:annotation_position} set object_count to 0 -- we are going to draw an annotation at 0,0 just to get its actual size make new shape at beginning of graphics of annotation_layer with properties {origin:{0, 0}, draws stroke:no, fill:no fill, text:{size:label_font_size, color:annotate_color, font:label_font, alignment:text_alignment, text:(annotate_label)}, draws shadow:false, autosizing:full} set annotation_size to size of graphic 1 -- now that we have the size we don't need the object any more delete graphic 1 repeat with obj in objects set object_count to object_count + 1 set obj_size to size of obj set obj_orig to origin of obj if horizontal_position is "left" then set x_pos to (item 1 of obj_orig) - horizontal_margin - (item 1 of annotation_size) set text_alignment to right set magnet_list to right_edge set x_header_start to x_pos + (item 1 of annotation_size) else if horizontal_position = "center" then set x_pos to (item 1 of obj_orig) + ((item 1 of obj_size) - (item 1 of annotation_size)) / 2 set text_alignment to center if vertical_position = "top" then set magnet_list to bottom_edge else if vertical_position = "middle" then display alert "Invalid Placement." message "Cannot place an annotation in the center of the object." buttons {"OK"} default button 1 exit repeat -- the script else -- vertical_position ="bottom" set magnet_list to top_edge end if set x_header_start to x_pos else set x_pos to (item 1 of obj_orig) + (item 1 of obj_size) + horizontal_margin set text_alignment to left set magnet_list to left_edge set x_header_start to x_pos end if if vertical_position = "top" then set y_pos to (item 2 of obj_orig) - vertical_margin - (item 2 of annotation_size) set y_header_start to y_pos set y_header_end to y_pos + (item 2 of annotation_size) else if vertical_position = "middle" then set y_pos to (item 2 of obj_orig) + ((item 2 of obj_size) - (item 2 of annotation_size)) / 2 set y_header_start to y_pos set y_header_end to y_pos + (item 2 of annotation_size) if horizontal_position = "left" then set magnet_list to right_edge else if horizontal_position = "center" then display alert "Invalid Placement." message "Cannot place an annotation in the center of the object." buttons {"OK"} default button 1 exit repeat -- the script else -- horizontal_position = right set magnet_list to left_edge end if else -- bottom set y_pos to (item 2 of obj_orig) + (item 2 of obj_size) + vertical_margin set y_header_start to y_pos set y_header_end to y_pos + (item 2 of annotation_size) end if make new shape at beginning of graphics of annotation_layer with properties {origin:{x_pos, y_pos}, size:annotation_size, draws stroke:no, fill:no fill, magnets:magnet_list, user data:annotation_tag, text:{size:label_font_size, color:annotate_color, font:label_font, alignment:text_alignment, text:(annotate_label)}, draws shadow:false, autosizing:full} set annotated_text to graphic 1 set origin of annotated_text to {x_pos, y_pos} -- I have not been able to get connect the line directly between two objects -- I have to use the point list and then define source and destination after make new line at beginning of graphics of annotation_layer with properties {stroke color:annotate_color, thickness:annotate_thickness, point list:{x_pos, y_pos}} set connector_line to graphic 1 set source of connector_line to obj set destination of connector_line to annotated_text set head magnet of connector_line to 2 make new line at beginning of graphics of annotation_layer with properties {stroke color:annotate_color, thickness:annotate_thickness, point list:{{x_header_start, y_header_start}, {x_header_start, y_header_end}}} set header_line to graphic 1 set source of header_line to annotated_text set tail magnet of header_line to 1 set destination of header_line to annotated_text set head magnet of header_line to 3 end repeat -- Need 'as list' if there's only one item. Pesky; took ages to figure out. -- object_count is multiplied by three because the text, header line and connector line are each object set selection to a reference to graphics 1 thru (object_count * 3) as list end tell on error error_message tell front window of application "OmniGraffle Professional" activate --say error_message if not error_message is equal to "user abort" then display alert "No selection." message "This script adds an annotation to an object. Select some, then run it again." buttons {"OK"} default button 1 end if end tell end try -- Ask the user to fill in the four global variables on prompt_user() tell front window of application "OmniGraffle Professional" -- Prompt the user as to where to place annotation try set answer to choose from list {"NW", "N", "NE", "W", "E", "SW", "S", "SE"} with prompt "Placement:" if answer is false then return false set annotation_position to (item 1 of answer) set horizontal_position to my get_horizontal_position(annotation_position) set vertical_position to my get_vertical_position(annotation_position) on error error_message display alert error_message return false end try try set answer to display dialog "Enter horizontal margin" default answer "20" if answer is false then return false set horizontal_margin to text returned of answer as number set answer to display dialog "Enter vertical margin" default answer "20" if answer is false then return false set vertical_margin to text returned of answer as number on error error_message if error_message is "OmniGraffle Professional got an error: User canceled." then return false display alert error_message buttons {"OK"} default button 1 return false end try return true end tell end prompt_user on get_horizontal_position(position) if {"NW", "W", "SW"} contains position then return ("left") else if {"N", "S"} contains position then return "center" else if {"NE", "E", "SE"} contains position then return "right" else return ("left") end if end get_horizontal_position on get_vertical_position(position) if {"NW", "N", "NE"} contains position then return ("top") else if {"W", "E"} contains position then return "middle" else if {"SW", "S", "SE"} contains position then return "bottom" else return ("top") end if end get_vertical_position