Report abuse

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
### Motivation

There have been several specific hints on this site which use UI scripting
to perform tasks that applications' AppleScript dictionaries don't otherwise
allow.  Unfortunately, UI scripting is a complicated pain to get right.  In
particular, the code Apple uses as a [demonstration on apple.com][demo] is
very long, and not particularly intuitive:

[demo]: http://www.apple.com/applescript/uiscripting/02.html

    tell application "Finder"
        activate
    end tell
    
    tell application "System Events"
        tell process "Finder"
            tell menu bar 1
                tell menu bar item "View"
                    tell menu "View"
                        tell menu item "Arrange By"
                            tell menu "Arrange By"
                                click menu item "Size"
                            end tell
                        end tell
                    end tell
                end tell
            end tell
        end tell
    end tell

To that end, I created a helper function, which I call `menu_click`, which
can be added to any script to allow the above code to instead be written as:

    tell application "Finder" to activate
    menu_click({"Finder", "View", "Arrange By", "Size"})

I find these two lines much clearer and easier to understand than the 19
lines of code that Apple uses for the same purpose.  To be fair, even
without my function, this code could be written:

    tell application "Finder" to activate
    tell application "System Events"
        click menu item "Size" of ((process Finder)'s (menu bar 1)'s ¬
            (menu bar item "View")'s (menu "View")'s ¬
            (menu item "Arrange By")'s (menu "Arrange By"))
    end tell

I still like my version better ;)

To be even fairer, Apple tries to help out with their own
[helper functions][help].  These are useful, but they are overly complicated
to use, because a separate function is needed for each level of menu
hierarchy the script author wants to use.  i.e. the `do_menu` handler
can execute the *File → New* command, but not the
*Edit → Insert → Line Break* command, which requires the more complicated
`do_submenu` handler.  For more deeply nested menu options, we'd eventually
need a `do_subsubsubmenu` handler, if we continued in Apple's footsteps.

[help]: http://www.apple.com/applescript/uiscripting/03.html

Thus, the `menu_click` handler was born.  Just add the following code to
your script, and then call `menu_click` with a list containing the
application name, followed by the menu command.  Just so we're clear, I'll
repeat myself.  Copy and paste the "Code" section that immediately follows
this sentence into your script verbatim (it can be anywhere: top or bottom
makes no difference).  Then anywhere in the rest of your script, you can
call the helper function.

### Code

    -- `menu_click`, by Jacob Rus, September 2006
    -- 
    -- Accepts a list of form: `{"Finder", "View", "Arrange By", "Date"}`
    -- Execute the specified menu item.  In this case, assuming the Finder 
    -- is the active application, arranging the frontmost folder by date.
    
    on menu_click(mList)
        local appName, topMenu, r
        
        -- Validate our input
        if mList's length < 3 then error "Menu list is not long enough"
        
        -- Set these variables for clarity and brevity later on
        set {appName, topMenu} to (items 1 through 2 of mList)
        set r to (items 3 through (mList's length) of mList)
        
        -- This overly-long line calls the menu_recurse function with
        -- two arguments: r, and a reference to the top-level menu
        tell app "System Events" to my menu_click_recurse(r, ((process appName)'s ¬
            (menu bar 1)'s (menu bar item topMenu)'s (menu topMenu)))
    end menu_click
    
    on menu_click_recurse(mList, parentObject)
        local f, r
        
        -- `f` = first item, `r` = rest of items
        set f to item 1 of mList
        if mList's length > 1 then set r to (items 2 through (mList's length) of mList)
        
        -- either actually click the menu item, or recurse again
        tell app "System Events"
            if mList's length is 1 then
                click parentObject's menu item f
            else
                my menu_click_recurse(r, (parentObject's (menu item f)'s (menu f)))
            end if
        end tell
    end menu_click_recurse

### Example:

Here is a (very simple) example demonstrating the use of these functions.  To
get the example to work, make sure you copy both the helper functions above
and the example code following into the same script.

    -- This example script turns on the "iTunes Visualizer" visualizer, full screen
    tell app "iTunes" to activate
    menu_click({"iTunes", "View", "Visualizer", "iTunes Visualizer"})
    menu_click({"iTunes", "View", "Turn On Visualizer"})
    menu_click({"iTunes", "View", "Full Screen"})

* * *

Happy menu-item selecting.  Remember to [turn on UI scripting][ui-scripting]
before you run these scripts, or they won't work.

[ui-scripting]: http://www.apple.com/applescript/uiscripting/01.html