AppleScript to clear the row styles in OO 5?

I’m having trouble figuring out how to write AppleScript to clear the style of a row in an OO 5 document. I thought something like the following would do it,

tell application "OmniOutliner"
        tell front document
                repeat with theRow in selected rows
                        set rowStyles to get style of theRow
                        repeat with theStyle in rowStyles
                                remove style theStyle of theRow
                        end repeat
                end repeat
        end tell
end tell

but this produces an error,

error "OmniOutliner got an error: Can’t make style
(item 1 of style of child 4 of child 15 of document 
id \"cmtakl8ic3V\") of item 1 of every selected row
of document 1 into type list of specifier or specifier."
number -1700 from style (item 1 of style of child 4 of
child 15 of document id "cmtakl8ic3V") of item 1
of every selected row of document 1

and I’m kind of stuck there.

I bet someone among you has already done this in AppleScript – what is the correct incantation?

@StrawberriePie

After experimentation, I think the problem is that a row always has a style; you can’t remove it. But you can set the ‘attributes’ of a row’s style to their default values. And you need to remove any ‘named style’ from the row’s style.

The script below succeeds in resetting most of the selected rows’ appearance to their defaults. It doesn’t change text color, though.

SG

tell application "OmniOutliner"
	tell front document
		repeat with aRow in selected rows
			tell aRow's style
				repeat with anAttr in its attributes
					try
						set anAttr's value to anAttr's default value
					end try
				end repeat -- attributes
				try
					remove named styles from its named styles
				end try
			end tell -- style
		end repeat -- rows
	end tell
end tell

One other approach (quite fast with longer documents) is to:

  1. Reset all the attributes in the style of the first row, creating a vanilla or ‘factory-settings’ style,
  2. then batch-set the style of all rows in the document to that vanilla style:

(If you also wanted to remove formats from particular text ranges, you would need to step through the attribute runs of topic cell rich texts)

on run
    tell application "OmniOutliner"
        if (count of documents) > 0 then
            tell front document
                
                -- VANILLA STYLE FOR ROW 1
                script setAttrib
                    on |λ|(attrib, v)
                        set value of attrib to v
                    end |λ|
                end script
                
                tell style of front row
                    my zipWith(setAttrib, ¬
                        attributes, default value of attributes)
                end tell
                
                -- ALL ROWS RESET TO VANILLA
                set style of rows to (style of front row)
            end tell
        end if
    end tell
end run

-- GENERIC FUNCTIONS ----------------------------------------------

-- min :: Ord a => a -> a -> a
on min(x, y)
    if y < x then
        y
    else
        x
    end if
end min

-- Lift 2nd class handler function into 1st class script wrapper 
-- mReturn :: Handler -> Script
on mReturn(f)
    if class of f is script then
        f
    else
        script
            property |λ| : f
        end script
    end if
end mReturn

-- zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
on zipWith(f, xs, ys)
    set lng to min(length of xs, length of ys)
    set lst to {}
    tell mReturn(f)
        repeat with i from 1 to lng
            set end of lst to |λ|(item i of xs, item i of ys)
        end repeat
        return lst
    end tell
end zipWith

Thanks draft8.

So basic AppleScript, slower than your advanced AppleScript but much faster than the original, could be something like this:

SG

tell application "OmniOutliner"'s front document
	set selRow1 to selected rows's first item
	repeat with anAttr in selRow1's style's attributes
		try
			set anAttr's value to anAttr's default value
		end try
	end repeat
	try
		remove named styles from selRow1's style's named styles
	end try
	set selected rows's style to selRow1's style -- batch reset
end tell

Looks good.

(You may also, I think, be able to get a little extra speed with a couple of batch reads at the start – fewer AppleEvents – less traffic, that is, across the slowish automation interface)

tell application "OmniOutliner"'s front document
    set selRow1 to selected rows's first item
    set oStyle to style of selRow1
    
    set vs to default value of attributes of oStyle
    set intAttribs to length of vs
    set attribs to attributes of oStyle as list
    
    repeat with i from 1 to intAttribs
        set value of item i of attribs to item i of vs
    end repeat
    
    try
        remove named styles from selRow1's style's named styles
    end try
    
    set selected rows's style to selRow1's style -- batch reset
end tell

@draft8

Yes, thanks. Batch reads and sets are the way to go … when I can get the darn things to work. Better in AppleScript than in JXA, it seems.

SG

In JXA, the batch reads are fine, but the batch writes are not supported

Clearing special formatting attributes from particular attribute runs within texts might, of course, look something like this:

tell application "OmniOutliner"
    tell front document
        set vs to topic of its rows
        set lng to length of vs
        repeat with i from 1 to lng
            set topic of row i to item i of vs
        end repeat
    end tell
end tell

Or, packaging up the clearing of row styles with the clearing of character run formats:

on run
    tell application "OmniOutliner"
        if (count of documents) > 0 then
            tell front document
                
                -- VANILLA STYLE FOR ROW 1
                script setAttrib
                    on |λ|(attrib, v)
                        set value of attrib to v
                    end |λ|
                end script
                
                tell style of front row
                    my zipWith(setAttrib, ¬
                        attributes, default value of attributes)
                end tell
                
                -- ALL ROWS RESET TO VANILLA STYLE
                set style of rows to (style of front row)
                
                -- CHARACTER FORMATS CLEARED
                script clearChars
                    on |λ|(oRow, strText)
                        set topic of oRow to strText
                    end |λ|
                end script
                
                my zipWith(clearChars, rows as list, topic of rows)
            end tell
        end if
    end tell
end run

-- GENERIC FUNCTIONS ----------------------------------------------

-- min :: Ord a => a -> a -> a
on min(x, y)
    if y < x then
        y
    else
        x
    end if
end min

-- Lift 2nd class handler function into 1st class script wrapper 
-- mReturn :: Handler -> Script
on mReturn(f)
    if class of f is script then
        f
    else
        script
            property |λ| : f
        end script
    end if
end mReturn

-- zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
on zipWith(f, xs, ys)
    set lng to min(length of xs, length of ys)
    set lst to {}
    tell mReturn(f)
        repeat with i from 1 to lng
            set end of lst to |λ|(item i of xs, item i of ys)
        end repeat
        return lst
    end tell
end zipWith

Many thanks to everyone who replied and provided code! This is so excellent.

In OmniOutliner 5.2 (not available publicly yet), I’ve added a couple new scripting capabilities that should make some of this easier. Once test builds are available, the attribute class will have a new has local value property, and will respond to a new clear command to remove any local value (leaving it to cascade from from any level style or inherit from an applied named style).

For example, if you have a row, you could do:

clear attributes of its style

or to remove styles from text spans in the row’s topic cell:

clear attributes of every style of its topic
1 Like

@tjw That sounds great – thanks!