## SwingOSC – Java-based GUI classes

last mod: 27-jan-08 sciss

# SwingOSC

A `SwingOSC` object is the client-side representation of a java GUI server. A lot of the method names are closely modelled after the Server class (the client representation of scsynth), so it"s fairly easy to use.

SwingOSC is also the name of the java / OSC based GUI library. For an overview of GUI objects using SwingOSC, please read the help file SwingGUI.

## The default server

There is always a default Server, which is stored in the class variable `default`. Any GUI components created without a target will be created on the default server. At startup, a server representation referring to the local machine is created and stored both in the class variables `local` and `default`. When you boot SwingOSC manually from a terminal (see below), please note that this default server assumes SwingOSC to listen at TCP port 57111 and to send an initial ping message to sclang (127.0.0.1:57120). If the initial ping is not send, you will have to call `initTree` manually.

Note that in this document we will assume that the global variable `g` is assigned to the default server, by executing the following line:

         g = SwingOSC.default;   // !

## Booting and Quitting the Server from within SuperCollider

Method signatures:

            <aServer>.boot( <startAliveThread = true> )
            <aServer>.quit

Examples:

            g.boot;
            // ...
            g.quit;

Note: this assumes that the file `"SwingOSC.jar"` has been copied into the main SuperCollider folder. If you want to use a different install location, set the class variable `program` appropriately:

            SwingOSC.program = "<completePathToAndIncludingSwingOSC.jar>";

Here's for example, what my `"~/scwork/startup.rtf"` reads:

            SwingOptions.default.javaOptions = SwingOptions.default.javaOptions + "-Xdock:icon=/Users/rutz/Documents/workspace/SwingOSC/application.icns";
            SwingOSC.program                 = "/Users/rutz/Documents/workspace/SwingOSC/build/SwingOSC.jar";
            g                                = SwingOSC.default;

If you want to use a specific `java` program, you can set the class field `java`, e.g. to force SwingOSC to boot with a Java 1.4 version on Mac OS X:

            SwingOSC.java = "/System/Library/Frameworks/JavaVM.framework/Versions/1.4.2/Commands/java";

You can register a custom function to be executed when the server has been booted. For example:

Method signature:

        <aServer>.doWhenBooted( <func>, <timeout = 20> )

Example:

    (
        g.doWhenBooted({
            JSCWindow.viewPalette;
        });
        g.boot;
    )

Differences to the audio `Server`: `doWhenBooted`'s second argument here is timeout in seconds.

Alternatively you can combine the two methods above:

Method signature:

        <aServer>.waitForBoot( <func>, <timeout = 20> )

Example:

    (
        g.waitForBoot({
            JSCWindow.viewPalette;
        });
        g.boot;
    )

On Mac OS 10.3.9 (Panther), there is a known issue with `unixCmd`s blocking each other, so to boot SwingOSC and `scsynth` successively, you may need to call `g.unblockPipe` after booting SwingOSC.

## Booting the server from a terminal

You can boot a SwingOSC server from within sclang with the boot method (see above). However sometimes you may wish to boot it from a terminal or by double clicking an executable shell script in the Finder. Please refer to the Document `"readme.html"` in the main SwingOSC folder to learn about launching the server from a terminal. You usually launch the server by the following command:

    	cd <mainSwingOSCFolder>
      	java -jar build/SwingOSC.jar -t 57111 -i -L

Make sure to use TCP port 57111 and restrict OSC communication to the local machine (`-L` option) when possible.

If you prefer to use UDP transport, use `-u` instead of `-t`.

To quit the server press `Ctrl-C` in the terminal.

## Creating another server

You might want to create another server instance because

- the GUI needs to be split over different environments
- the GUI server is running on a remote machine
- several clients are sharing the same server

To create a new server:

    	<aServer> = SwingOSC.new( <name>, <addr>, <options>, <clientID> );

The `name` must be a unique Symbol (no two servers with the same name may exist). The `addr` is a NetAddr object describing the server's socket (IP and port). The `options` argument is an instance of SwingOptions. If several clients are sharing the same server, use a different `clientID` Integer (default: `0`) for each, so their objectID-allocators won't interfere.

Important: If you don't specify `options`, default options will be created. `loopBack` is turned on (`true`) if the supplied `addr` argument uses the loopback-IP `"127.0.0.1"`, otherwise it is off. Inversely, if you don't specify an `addr`, an appropriate address will be created: if the `option`'s `loopBack` flag is `true`, the address will be `NetAddr( "127.0.0.1", 57111 )`. If `loopBack` is `false`, the local computer's IP address will be determined (only working on Unix systems). Therefore, to use a server without loopback as default:

    	g = SwingOSC.new( \test, options: SwingOptions.new.loopBack_( false ));
    	SwingOSC.default = g;
    	g.boot;

Do not try to modify the `loopBack` settings an already existing server.

Here is another example:

    (
        f = SwingOSC.new( \secondSwing, NetAddr( "localhost", 44444 ));
        f.options.protocol = \udp;  // if you don't want tcp
        f.waitForBoot({
            var win;

            win = JSCWindow( server: f );
            win.onClose = { f.quit; };
            JSCStaticText( win.view, Rect( 40, 40, 100, 30 )).font_( JFont( "Helvetica", 36 )).string_( "Kieka" );
            win.front;
        });
        // use this on MacOS 10.3.9 Panther if booting blocks:
        // g.unblockPipe;
    )

## Sending Messages to the Server and Receiving Replies

Method signatures:

        // send a single message
        <aServer>.sendMsg( <arg1>, [ <arg2>  ... ])
        // send a single message as array
        <aServer>.listSendMsg( <arrayOfArgs> )
        // send a bundle
        <aServer>.sendBundle( <time>, <arrayOfArgs1>, [ <arrayOfArgs2>  ... ])
        // send an array of messages as a bundle
        <aServer>.listSendBundle( <time>, <arrayOfArraysOfArgs> )

Examples:

         g.sendMsg( '/print', '[', '/local', \OS_Name, '[', '/method', 'java.lang.System', \getProperty, 'os.name', ']', ']');
         g.listSendMsg([ '/print', '[', '/local', \OS_Version, '[', '/method', 'java.lang.System', \getProperty, 'os.version', ']', ']' ]);

Note: bundle times should always be set to `nil` for now, since they are not used by SwingOSC. this behaviour may change in the future, so your code is safe if you use `nil`.

To receive replies, you can set up either a normal `OSCresponderNode` or an `OSCpathResponder`. If, for some reason, the address argument doesn't work, use `nil` instead. Example:

    (
        OSCpathResponder( g.addr, [ '/info', \confirm ], { arg time, resp, msg;
            var choice, rgb;

            resp.remove;
            choice = msg[ 2 ];
            ("User's choice : " ++ [ "Ok", nil, "Cancel" ].at( choice )).postln;
            if( choice == 0, {
                OSCresponderNode( g.addr, '/set', { arg time, resp, msg;
                    if( msg[ 1 ].asSymbol === \color, {
                        resp.remove;
                        msg.postln;
                        rgb = msg[ 3 ];
                        ("New colour : " ++ Color.new255( (rgb >> 16 & 0xFF), (rgb >> 8) & 0xFF, rgb & 0xFF )).postln;
                    });
                }).add;
                g.sendBundle( nil,
                    [ '/local', \color, '[', '/method', \colorChooser, \getColor, ']' ],
                    [ '/get', \color, \RGB ]);
            });
        }).add;
        // note : showConfirmDialog is modal and will block OSC processing in SwingOSC
        // until the dialog is closed. this is just an example anyway
        g.sendBundle( nil,
            [ '/local', \colorChooser, '[', '/new', 'javax.swing.JColorChooser', ']' ],
            [ '/query', \confirm, '[', '/method', 'javax.swing.JOptionPane', \showConfirmDialog,
                '[', '/ref', \null, ']', '[', '/ref', \colorChooser, ']', "Select a colour", 2, ']' ]);
    )

The above example can be simplified by using the `sendMsgSync` method. This must be called inside a `Routine` as for example created by `fork`:

Method signature:

            // send a single message and wait for a success or failure reply
            <aServer>.sendMsgSync( <msgArray>, <successMsgArray>, <failureMsgArray>, <timeout = 4>, <condition> )

Example:

    (
    fork {
        var choice, rgb, msg;

        g.sendMsg( '/local', \colorChooser, '[', '/new', 'javax.swing.JColorChooser', ']' );
        msg = g.sendMsgSync([ '/query', \confirm, '[', '/method', 'javax.swing.JOptionPane', \showConfirmDialog,
                                '[', '/ref', \null, ']', '[', '/ref', \colorChooser, ']', "Select a colour", 2, ']' ],
            [ '/info', \confirm ], timeout: 60 );
        if( msg.notNil, {
            choice = msg[ 2 ];
            ("User's choice : " ++ [ "Ok", nil, "Cancel" ].at( choice )).postln;
            if( choice == 0, {
                g.sendMsg( '/local', \color, '[', '/method', \colorChooser, \getColor, ']' );
                msg = g.sendMsgSync([ '/get', \color, \RGB ], [ '/set', \color, \RGB ]);
                if( msg.notNil, {
                    rgb = msg[ 3 ];
                    ("New colour : " ++ Color.new255( (rgb >> 16 & 0xFF), (rgb >> 8) & 0xFF, rgb & 0xFF )).postln;
                }, {
                    "Timeout!".warn;
                });
            });
        }, {
            "This takes way too long, i'm giving up".postln;
        });
    };
    )

Note: a `timeout` of `nil` means no timeout (waits forever).

## Adding custom Java classes to the class path

Method signatures:

            <aServer>.addClasses( <url1>, [ <url2> ... ])
            <aServer>.removeClasses( <url1>, [ <url2> ... ])
            <aServer>.updateClasses( <url1>, [ <url2> ... ])

Example: see `"DynamicClasses.html"` in examples for folder.

URLs can point to single class files, jar archives or folders containing class files. Make sure to use proper `file://...` URLs and not plain path names for files!

The `updateClasses` method can be used to dynamically develop java classes. It is a shorthand for removing and re-adding the URLs in order to use modified recompiled versions of java classes.

## Class methods

Method signatures:

    	SwingOSC.local;     // reference to default local server
    	SwingOSC.default;   // reference to default local server; this will be used in GUI elements when no explicit server is specified
    	SwingOSC.quitAll;   // quit all registered servers ; UNTESTED
    	SwingOSC.killAll;   // query the system for any SwingOSC server apps and hard quit them ; UNTESTED