Max 5 API Reference

Scripting the Patcher

Your object can use scripting capabilities of the patcher to learn things about its context, such as the patcher's name, hierarchy, or the peer objects to your object in its patcher.

You can also modify a patcher, although any actions your object takes are not undoable and may not work in the runtime version.

Knowing the Patcher

To obtain the patcher object containing your object, you can use the obex hash table. The obex (for "object extensions") is, more generally, a way to store and recall data in your object. In this case, however, we are just using it in a read-only fashion.

Note that unlike the technique discussed in previous versions of the SDK, using the obex to find the patcher works at any time, not just in the new instance routine.

    void myobject_getmypatcher(t_myobject *x)
    {
        t_object *mypatcher;

        obex_object_lookup(x, gensym("#P"), &mypatcher);
        post("my patcher is at address %lx",mypatcher);
    }

The patcher is an opaque Max object. To access data in a patcher, you'll use attributes and methods.

Patcher Name and File Path

To obtain the name of the patcher and its file path (if any), obtain attribute values as shown below.

        t_symbol *name = object_attr_getsym(patcher, gensym("name"));
        t_symbol *path = object_attr_getsym(patcher, gensym("filepath"));

These attributes may return NULL or empty symbols.

Patcher Hierarchy

To determine the patcher hierarchy above the patcher containing your object, you can use jpatcher_getparentpatcher(). A patcher whose parent is NULL is a top-level patcher. Here is a loop that prints the name of each parent patcher as you ascend the hierarchy.

        t_object *parent, *patcher;
        t_symbol *name;

        object_obex_lookup(x, gensym("#P"), &patcher);
        parent = patcher;
        do {
            parent = jpatcher_getparentpatcher(parent);
            if (parent) {
                name = object_attr_getsym(parent, gensym("name"));
                if (name)
                    post("%s",name->s_name)
            }
        } while (parent != NULL);

Getting Objects in a Patcher

To obtain the first object in a patcher, you can use jpatcher_get_firstobject(). Subsequent objects are available with jbox_get_nextobject().

If you haven't read the Anatomy of a UI Object, we'll mention that the patcher does not keep a list of non-UI objects directly. Instead it keeps a list of UI objects called boxes, and the box that holds non-UI objects is called a newobj. The "objects" you obtain with calls such as jpatcher_get_firstobject() are boxes. The jbox_get_object() routine can be used to get the pointer to the actual object, whether the box is a UI object or a newobj containing a non-UI object. In the case of UI objects such as dials and sliders, the pointer returned by jbox_get_object() will be the same as the box. But for non-UI objects, it will be different.

Here is a function that prints the class of every object (in a box) in a patcher containing an object.

    void myobject_printpeers(t_myobject *x)
    {
        t_object *patcher, *box, *obj;

        object_obex_lookup(x, gensym("#P"), &patcher);

        for (box = jpatcher_get_firstobject(patcher); box; jbox_get_nextobject(box)) {
            obj = jbox_get_object(box);
            if (obj)
                post("%s",object_classname(obj)->s_name);
            else
                post("box with NULL object");
        }
    }

Iteration Using Callbacks

As an alternative to the technique shown above, you can write a callback function for use with the patcher's iteration service. The advantage of using iteration is that you can descend into the patcher hierarchy without needing to know the details of the various objects that may contain subpatchers (patcher, poly~, bpatcher, etc.). If you want to iterate only at one level of a patcher hierarchy, you can do that too.

Your iteration function is defined as follows. It will be called on every box in a patcher (and, if you specify, the patcher's subpatchers).

     long myobject_iterarator(t_myobject *x, t_object *b);

The function returns 0 if iteration should continue, or 1 if it should stop. This permits you to use an iterator as a way to search for a specific object.

Here is an example of using an iterator function:

        t_object *patcher;
        long result = 0;

        patcher = object_obex_lookup(x, gensym("#P"), &patcher);

        object_method(patcher, gensym("iterate"), myobject_iterator, (void *)x, PI_WANTBOX | PI_DEEP, &result);

The PI_WANTBOX flag tells the patcher iterator that it should pass your iterator function the box, rather than the object contained in the box. The PI_DEEP flag means that the iteration will descend, depth first, into subpatchers. The result parameter returns the last value returned by the iterator. For example, if the iterator terminates early by returning a non-zero value, it will contain that value. If the iterator function does not terminate early, result will be 0.

Assuming the iterator function receives boxes, here is an example iterator that prints out the class and scripting name (if any) of all of the objects in a patcher. Note that the scripting name is an attribute of the box, while the class we would like to know is of the object associated with the box.

    long myobject_iterator(t_myobject *x, t_object *b)
    {
        t_symbol *name = object_attr_getsym(b, gensym("varname"));
        t_symbol *cls = object_classname(jbox_get_object(b));

        if (name)
            post("%s (%s)",cls->s_name, name->s_name);
        else
            post("%s", cls->s_name);
        return 0;
    }

Creating Objects

Much of the Max user interface is implemented using patcher scripting. For example, the inspectors are patchers in which an inspector object has been created. The file browser window has four or five separate scripted objects in it. Even the debug window is a dynamically scripted patcher. We point this out just to inform you that creating objects in a patcher actually works (if you get all the details right). The xxx example object shows how to use patcher scripting to create an "editing window" similar to the ones you see when double-clicking on a table or buffer~ object.

Creating objects in a patcher generally requires the use of a Dictionary (see discussion of UI objects above), but there is a convenience function newobject_sprintf() that can be used to avoid some of the complexity.

To create an object, your task is to set some attributes. In the absence of any specific values, an object's attributes will be set to some default, but you'll probably care, at the very least, about specifying the object's location. Here is an example that creates a toggle and metro object using a combination of attribute parse syntax and sprintf. If you're interested in creating objects with newobject_sprintf(), it may help to examine a Max document to see some of the attribute name - value pairs used to specify objects.

        t_object *patcher, *toggle, *metro;

        patcher = object_obex_lookup(x, gensym("#P"), &patcher);

        toggle = newobject_sprintf(patcher, "@maxclass toggle @patching_position %.2f %.2f",
            x->togxpos, x-> togxpos);

        metro = newobject_sprintf(patcher, "@maxclass newobj @text metro 400 @patching_position %.2f %.2f",
            x->metxpos, x->metypos);

Note that to create a non-UI object, you use set the maxclass attribute to newobj and the text attribute to the contents of the object box. Attributes can be specified in any order. Using the patching_position attribute permits you to specify only the top-left corner and use the object's default size. For text objects, the default size is based on the default font for the patcher.

Finally, note that newobject_sprintf() returns a pointer to the newly created box, not the newly created object inside the box. To get the object inside the box, use jbox_get_object().

Connecting Objects

If you'd like to script the connections between two objects, you can do so via a message to the patcher. Assuming you have the patcher, toggle, and metro objects above, you'll create an array of atoms to send the message using object_method_typed().

        t_atom msg[4], rv;

        atom_setobj(msg, toggle);       // source
        atom_setlong(msg + 1, 0);       // outlet number (0 is leftmost)
        atom_setobj(msg + 2, metro);    // destination
        atom_setlong(msg + 3, 0);       // inlet number (0 is leftmost)

        object_method_typed(patcher, gensym("connect"), 4, msg, &rv);

If you want to have a hidden connection, pass an optional fifth argument that is any negative number.

Deleting Objects

To delete an object in a patcher you call object_free() on the box. As of Max 5.0.6 this will properly redraw the patcher and remove any connected patch cords.

Obtaining and Changing Patcher and Object Attributes

You can use object attribute functions to modify the appearance and behavior of objects in a patcher or the patcher itself. Note that only a few of these attributes can be modified by the user. The C level access to attributes is much more extensive.

Attributes whose type is object can be accessed via object_attr_getobj() / object_attr_setobj(). Attributes whose type is char can be accessed with object_attr_getchar() / object_attr_setchar(). Attributes whose type is long can be accessed with object_attr_getlong() / object_attr_setlong(). Attributes whose type is symbol can be accessed via object_attr_getsym() / object_attr_setsym(). For attributes that are arrays, such as colors and rectangles, use object_attr_getvalueof() / object_attr_setvalueof().

Patcher Attributes

Name

Type

Settable

Description

box

object

No

The box containing the patcher (NULL for top-level patcher)

locked

char

Yes (not in runtime)

Locked state of the patcher

presentation

char

Yes

Presentation mode of the patcher

openinpresentation

char

Yes

Will patcher open in presentation mode?

count

long

No

Number of objects in a patcher

fgcount

long

No

Number of objects in the patcher's foreground layer

bgcount

long

No

Number of objects in the patcher's background layer

numvews

long

No

Number of currently open views of the patcher

numwindowviews

long

No

Number of currently open window-based views of the patcher

firstobject

object

No

First box in the patcher

lastobject

object

No

Last box in the patcher

firstline

object

No

First patch cord in the patcher

firstview

object

No

First view object in the patcher

title

symbol

Yes

Window title

fulltitle

symbol

No

Complete title including "unlocked" etc.

name

symbol

No

Name (could be different from title)

filename

symbol

No

Filename

filepath

symbol

No

File path (platform-independent file path syntax)

fileversion

long

No

File version

noedit

char

No

Whether patcher can be unlocked

collective

object

No

Collective object, if patcher is inside a collective

cansave

char

No

Whether patcher can be saved

dirty

char

Yes (not in runtime)

Whether patcher is modified

bglocked

char

Yes

Whether background is locked

rect

double[4]

Yes

Patcher's rect (left, top, width, height)

defrect

double[4]

Yes

Patcher's default rect (used when opening the first view)

openrect

double[4]

Yes

Fixed initial window location

parentpatcher

object

No

Immediate parent patcher (NULL for toplevel patchers)

toppatcher

object

No

Topmost parent patcher (NULL for toplevel patchers)

parentclass

object

No

Class object of parent (patcher, poly~, bpatcher etc.)

bgcolor

double[4]

Yes

Locked background color (RGBA)

editing_bgcolor

double[4]

Yes

Unlocked background color (RGBA)

edit_framecolor

double[4]

Yes

Text editing frame color

locked_iocolor

double[4]

Yes

Locked inlet/outlet color

unlocked_iocolor

double[4]

Yes

Unlocked inlet/outlet color

boguscolor

double[4]

Yes

Color of uninitialized (bogus) objects

gridsize

double[2]

Yes

Editing grid size

gridonopen

char

Yes

Show grid on open

gridsnapopen

char

Yes

Snap to grid on open

imprint

char

Yes

Save default-valued object attributes

defaultfocusbox

symbol

Yes

Default focus box (varname)

enablehscroll

char

Yes

Show horizontal scrollbar

enablevscroll

char

Yes

Show vertical scrollbar

boxanimatetime

long

Yes

Box animation time

default_fontname

symbol

Yes

Default font name

default_fontface

long

Yes

Default "fake" font face (0 plain, 1, bold, 2 italic, 3 bold italic)

default_fontsize

long

Yes

Default font size in points

toolbarvisible

char

Yes

Show toolbar on open

toolbarheight

long

Yes

Height of toolbar (can use 0 for invisible)

toolbarid

symbol

Yes

Name (in maxinterface.json) of toolbar, none = empty symbol

Box Attributes

Name

Type

Settable

Description

rect

double[4]

Settable only

Changes both patching_rect and presentation_rect

presentation_rect

double[4]

Yes

Presentation mode rect

patching_rect

double[4]

Yes

Patching mode rect

position

double[2]

Settable only

Changes both patching_position and presentation_position

size

double[2]

Settable only

Changes both patching_size and presentation_size

patching_position

double[2]

Yes

Patching mode position (top, left corner)

presentation_position

d[2]

Yes

Presentation mode position

patching_size

double[2]

Yes

Patching mode size (width, height)

presentation_size

double[2]

Yes

Presentation mode size

maxclass

symbol

No

Name of Max class (newobj for non-UI objects)

object

object

No

Associated object (equivalent to jbox_get_object)

patcher

object

No

Containing patcher

hidden

char

Yes

Is box hidden on lock?

fontname

symbol

Yes

Font name (if box has font attributes or a text field)

fontface

long

Yes

"Fake" font face (if box has font attribute or a text field)

fontsize

long

Yes

Font size (if box has font attributes or a text field)

textcolor

double[4]

Yes

Text color (if box has font attributes or a text field)

hint

symbol

Yes

Associated hint

color

double[4]

Yes

Standard color attribute (may not be present in all objects)

nextobject

object

No

Next object in the patcher's list

prevobject

object

No

Previous object in the patcher's list

varname

symbol

Yes

Scripting name

id

symbol

No

Immutable object ID (stored in files)

canhilite

char

No

Does this object accept focus?

background

char

Yes

Include in background

ignoreclick

char

Yes

Ignores clicks

maxfilename

symbol

No

Filename if class is external

description

symbol

No

Description used by assistance

drawfirstin

char

No

Is leftmost inlet drawn?

growy

char

No

Can object grow with fixed aspect ratio?

growboth

char

No

Can object grow independently in width and height?

nogrow

char

No

Is object fixed size?

mousedragdelta

char

No

Does object use hidden-mouse drag tracking (number box)

textfield

object

No

Textfield object associated with this box if any

editactive

char

No

Is object the currently focused box in an unlocked patcher?

prototypename

symbol

No

Name of the prototype file used to create this object

presentation

char

Yes

Is object included in the presentation?

annotation

symbol

Yes

Text shown in clue window when mouse is over the object

numinlets

long

No

Number of inlets visible

numoutlets

long

No

Number of outlets visible

outlettype

symbol[]

No

Array of symbols with outlet types ("signal" etc.)

To access an attribute of a non-UI object, use jbox_get_object() on the box to obtain the non-UI object first.

Copyright © 2008, Cycling '74