## SwingOSC – Java-based GUI classes

last mod: 01-Aug-08 sciss

known issues / todo
File->Close | Doesn't work for some reason with internal frames
OS X Screen Menu | Modifications to the menu root are not reflected in the menu "prototype" visible when no window is open

# JSCMenuRoot, JSCMenuGroup, JSCMenuItem, JSCMenuCheckItem, JSCMenuSeparator

Menu bar management. The root handle of a menu bar is represented by `JSCMenuRoot`. Currently, only one global, application wide menu root is supported, but future versions might provide a menu root per window.

To create a menu, an instance of `JSCMenuGroup` is created with the menu root as its parent argument. The group is then populated with items by instantiating the `JSCMenuItem` class with the group as the parent argument. To create submenus, groups can be nested. To enhance visual clarity, separators can be inserted through the `JSCMenuSeparator` class.

Note that on Mac OS X the menu bar appears as the screen title bar. However it is only visible if at least one SwingOSC window is opened.

## JSCMenuRoot

    	JSCMenuRoot.new( <server> )

- `server` – the SwingOSC server to use. If omitted or `nil`, the `SwingOSC.default` server is used.

Note that only one instance per server can be created, if you try to instantiate the root again, the same object will be returned.

        r = JSCMenuRoot.new;

## JSCMenuGroup

    	JSCMenuGroup.new( <parent>, <name>, <index> )

- `parent` – an instance of `JSCMenuRoot` to attach the group to the top level, or another `JSCMenuGroup` to create a submenu.
- `name` – a String that is used to label the menu node.
- `index` – an Integer that specifies the position of the node in the parent group. A value of `0` will place the node at the very left or top of the parent group. A value of `1` will place it after the first node in the parent group. A value of `nil` will place the node at the end of the parent group, so usually you will just omit this argument.

Example:

    (
        r = JSCMenuRoot.new;
        a = JSCMenuGroup( r, "Utilities" );   // add to the end of the root
        b = JSCMenuGroup( r, "Demos" );       // add to the end of the root
        c = JSCMenuGroup( a, "Server" );      // a submenu of "Utilities"

        // depending on the OS, the window bar not visible
        // unless there is at least one window
        if( JSCWindow.allWindows.size == 0, { JSCWindow( "Test" ).front });
    )

## JSCMenuItem

    	JSCMenuGroup.new( <parent>, <name>, <index> )

The arguments have the same meaning as with `JSCMenuGroup`. An item cannot have submenus. Instead is represents an action to be performed upon selection. Similar to a JSCView, you can register a Function to be invoked when the item is selected, using the setter method `action_( <func> )`.

Example:

    (
        // we want this item to appear before the "Server"
        // submenu, hence we use an index of 0
        d = JSCMenuItem( a, "Browse UGens", 0 );
        d.action = { arg item; UGen.browse };
        e = JSCMenuItem( a, "Inspect Server", 1 );
        e.action = { arg item; Server.default.inspect };
        // now inside the "Server" submenu
        f = JSCMenuItem( c, "Query All Nodes" ).action_({ Server.default.queryAllNodes });
        // and one for the "Demo" menu
        h = JSCMenuItem( b, "Analog Bubbles" ).action_({
            {
    	       CombN.ar( SinOsc.ar( LFSaw.kr( 0.4, 0, 24, LFSaw.kr([ 8, 7.23 ], 0, 3, 80 )).midicps, 0, 0.04 ),
                          0.2, 0.2, 4 );
            }.play;
        });
    )

### name_

Some of the properties of the nodes can be modified. For example, a groups or item's name, using the `name_( <newName> )` setter method:

        a.name = "Tools";

### enabled_

Also groups and items can be disabled so they appear greyed out and cannot be selected. The corresponding setter method is `enabled_( <bool> )`. In the following example, an observer is set up that enables and disables the scsynth based menu nodes depending on whether scsynth is running or not:

    (
        UpdateListener.newFor( Server.default, { arg upd, s;
            c.enabled = s.serverRunning;
            h.enabled = s.serverRunning;
        }, \serverRunning ).update( Server.default, \serverRunning );
    )

### shortcut_

For quicker access, keyboard shortcuts can be assigned, using the `shortcut_( <description> )` setter method. The argument is a human readable string following the format `<modifiers>* <key>`, where:

- `modifiers := shift | meta | meta2 | control | ctrl | alt | altGraph`
- `key :=` key code name

The key code name consists either of a normal signal alphanumerical character, e.g. `"Q"`, `"W"`, `"E"`, `"R"`, `"T"`, etc., or a any of the `VK_...` as defined in java.awt.KeyEvent, e.g. `"BACK_SLASH"`, `"DOWN"`, `"ESCAPE"`, etc.

Important: To facilitate cross-platform operation, the meaning of `"meta"` and `"meta2"` depends on the platform: On OS X, `"meta"` corresponds to the Command-Key, on Windows and Linux this maps to the Control-Key, so it always corresponds to the commonly used menu shortcut modifier. On the other hand, `"meta2"` maps to the Control-Key on OS X, and on Windows and Linux is realized as a combination of Control and Alt. Hence you can use `"meta2"` as a secondary menu modifier that will not conflict with the primary modifier. If possible, avoid the use of `"control"` and `"alt"`, since they may interfere with the other modifiers.

Example:

        d.shortcut = "meta U";
        e.shortcut = "alt shift I";
        h.shortcut = "meta2 B";

### Removing Nodes

Nodes can be removed from the menu bar by calling the `remove` method:

        h.remove;

## JSCMenuCheckItem

The `JSCMenuCheckItem` extends the functionality of `JSCMenuItem` by having a checkmark icon that gets toggled on and off. The state of the checkmark can be queried using the `selected` method, or programmatically set using `selected_( <bool> )`.

In the following example, the checkmark reflects whether the Moto-Rev synth is actually playing:

    (
        var synth;
        h = JSCMenuCheckItem( b, "Moto Rev" )
            .shortcut_( "meta2 M" )
            .enabled_( Server.default.serverRunning )
            .action_({ arg item;
                synth.release; synth = nil;
                if( item.selected, {
                    synth = {
                        RLPF.ar( LFPulse.ar( SinOsc.kr( 0.2, 0, 10, 21 ), 0.1 ), 100, 0.1 ).clip2( 0.4 );
                    }.play.register;
                    UpdateListener.newFor( synth, { arg upd, n;
                        if( synth == n, { synth = nil; item.selected = false });
                    }, \n_end );
                });
            });
    )

## JSCMenuSeparator

Finally, separator lines can be inserted for increased visual clarity. The constructor is `JSCMenuSeparator( <parent>, <index> )`. Here we insert a separator before the "Server" submenu:

        JSCMenuSeparator( a, 2 );