Panels

Preview Functions

Globals

Table of Contents



Dopetrack Proxy

Availability  LightWave® 8.0
Component  Layout
Header  lwdopetrack.h

The Layout Dopetrack allows the user to easily manage and manipulate keyframes for the standard envelopes shared by all Layout objects (i.e, Position, Rotation and Scaling).  This Global, specifically targeted towards the Layout Tool class, allows a plug-in to take over most of the functionality of the Dopetrack in order to "expose" to the user any underlying envelopes that plug-in may have to offer.

Global Call

   LWDopetrackProxy *dtprox;
   dtprox = global( LWDOPETRACK_GLOBAL, GFUSE_TRANSIENT );

The global function returns a pointer to an LWDopetrackProxy structure.

   typedef struct st_LWDopetrackProxy
       void            (*toolRegister)(DTToolCallbacksID);
       void            (*toolRelease)(void);

       void            (*exposeEnvelopes)(LWEnvelopeID *,const char **,int *);
       void            (*refreshDisplay)(void);

       DTKeySelectID   (*querySelectedKeys)(void);
       void            (*querySelection)(LWTime *,LWTime *);

       const LWTime   *(*queryMarkers)(void);
       void            (*addMarker)(LWTime);
       void            (*remMarker)(LWTime);

       DTBakeZoneID    (*queryBakeZones)(void);
       void            (*addBakeZone)(LWTime,LWTime);
       void            (*remBakeZone)(LWTime,LWTime);

       DTOperatingMode (*queryOpMode)(void);

       void            (*displayMenu)(DTMenuCallbacksID);

       int             (*visible)(void);

       DTDrawFuncs     drawfuncs;
   } LWDopetrackProxy, *LWDopetrackProxyID;

Tool-class plug-ins register with the Dopetrack as soon as they are activated.  They must provide a pointer to a structure of type DTToolCallbacks so that the Dopetrack may interactive with them  whenever the user takes some action on their exposed Envelopes.  The DTToolCallbacks structure contains the following members:

    typedef struct _dttoolcallbacks
    {
        DTUserEvent     userEvent;
        DTAllow         allow;
        DTMouse         mouseEvent;
        DTMenuCallbacks menu;
    } DTToolCallbacks, *DTToolCallbacksID;

The DTMenuCallbacks structure contains the following function pointers for handling context menu requests on the main Dopetrack area:

    typedef struct _dtmenucallbacks
    {
        DTMenuCount     menuCount;
        DTMenuSub       menuSubMenu;
        DTMenuSep       menuSep;
        DTMenuEnable    menuEnable;
        DTMenuItem      menuItem;
        DTMenuSelect    menuSelect;
        DTMenuSelect    menuInitial;
    } DTMenuCallbacks, *DTMenuCallbacksID;

Event Support

As the user interacts with the Dopetrack, certain events will be passed back to the plug-ins.  The userEvent member of the DTToolCallbacks structure must be set to the plug-in function that will be called with these events.  It is important to note that for a plug-in to utilize the Dopetrack Proxy mechanism, this pointer must be set.  This function pointer is therefore not optional.

The function has the following prototype:

    typedef void (*DTUserEvent)(DTEventParamID);

The plug-in may control the available intrinsic functions of the Dopetrack (those that remain active when the plug-in takes control) using the allow function.  This function will receive a function type (see the table that follows), and may return a true value (1) if that action may be performed by the user, or a false value (0) if it is to be disallowed.  This function is optional, and leaving it NULL will cause the Dopetracks default actions to be performed without condition.

    typedef int (*DTAllow)(DTKeyAction);

The function types that the allow function will be one of the following:


DTACTION_SELECT


Keyframes have been selected

DTACTION_SHIFT

Keyframes have been moved (shifted).

DTACTION_SHIFT_COPY

Copy/paste operation of keyframes at a new location

DTACTION_CREATE_KEY

User request to create a new keyframe

Also optional is the mouseEvent function.  This allows the plug-in to receive mouse events (down, move and up) whenever such events are generated in the Dopetrack margins.

    typedef void         (*DTMouse)(DTMouseParamID);

Context Menu Support

The menu callback pointers of the DTMenuCallbacks structure (nested within the DTToolCallbacks structure) are optional, and allow the plug-in to completely replace the Dopetrack's context menu with its own functionality.  The menuCount pointer should return the count of items that your context menu will display, the menuItem pointer should return the text for a specific menu item index, and the menuSelect function will be called by the Dopetrack when a selection is made from your plug-in's context menu.  The menuCount pointer is the key to enabling context menus for your plug-in, and if you place a pointer into the menuCount value, then you must also at least define menuItem and menuSelect.  All other menu pointers are optional, and may contain NULL values.

    typedef int          (*DTMenuCount)(int);
    typedef int          (*DTMenuSub)(int,int);
    typedef int        * (*DTMenuSep)(int);
    typedef int          (*DTMenuEnable)(int,int);
    typedef const char * (*DTMenuItem)(int,int);
    typedef void         (*DTMenuSelect)(int,int);
    typedef int          (*DTMenuInitial)(void);

To support certain internal functions, a return value of zero (0) from your menuCount function will cause a -1 value to be sent to your menuSelect function.  Please code accordingly.

count = menuCount( menuID )
Returns the number of items that will appear on the menu identified by menuID.  Your main menu will always be identified by a menuID of zero (0).  Any sub-menus that you define off of that main menu will have a menuID value assigned by the menuSubMenu() function.  So, if you have no sub-menus defined, menuID will always be zero.
subMenuID = menuSubMenu( menuID, item )
Returns the menu identifier for any sub-menus that might be defined on the menu menuID.  For example, if your main context menu contained one sub-menu that branched from the third menu element, then you would return a sub-menu identifier for that sub-menu when menuID equals 0, and item equals 2.  This new menu identifier will then be used in a call to menuCount() to query the number of elements on that particular menu.
A return value of zero (0) indicates that the specified menu element is not a sub-menu.
separray = menuSep( menuID )
Elements on a menu can be separated from one another using this function.  The returned array of integer values indicates whether or not a separator should immediately follow the corresponding menu element.  A value of zero (0) means that the corresponding menu element has no following separator, while a value of one (1) indicates that a menu separator should follow that element on the menu.  The length of this array should be of the exact size returned by the menuCount() function for the indicated menuID.
enable = menuEnable( menuID, item )
Menu items can be enabled or disabled for selection using this function.  A return value of zero (0) indicates that the item on the specified menuID should be disabled, while a value of one (1) indicates that is is available for selection.
string = menuItem( menuID, item )
The Dopetrack will query the string values of menu elements using this function.  You should return a value appropriate for the indicated menuID and item offset.
menuSelect( menuID, item )
When a selection is made by the user, its owning menu identifier and offset within that menu are provided back to the plug-in.  Keep in mind that menuID may be a value previously defined for a sub-menu, and will only refer to the main menu elements when it has a value of zero (0).
select = menuInitial( )
An initial menu item selection can be specified by the plug-in using this callback.  The return value should be the zero-based index of the menu item that should be highlighted when the menu is displayed.

Exported Dopetrack Functions

toolRegister( callbacks )
Register with the Dopetrack so that it can act as a proxy for your data.  callbacks is a pointer to a structure of type DTToolCallbacks, and should be appropriately populated.
toolRelease( )
When a plug-in is finished with the Dopetrack (for example, it is dropped in favor of another tool), it should call this function as part of its cleanup code.
exposeEnvelopes( envarray, axisnames, initstates )
Envelopes (and their attendant keyframes) are exposed to the user through this function.  The envarray should be a NULL-terminated list of LWEnvelopeID pointers that are to be made available to the user on the Dopetrack.  Although any number of envelopes can be provided, you should be aware that there is a threshold -- mandated by the current Layout interface -- that will cause differences in behavior.  If you expose three or fewer envelopes, the Dopetrack will treat them as it treats its normal channel processing -- that is, each envelope will display as a separate 'tick' color on the Dopetrack interface.  However, if you expose more than three envelopes at a time, a solid white bar will be displayed for any keyframe at any time index found in the envelopes.
 
The axisnames array allows the plug-in to re-define and enable the axis buttons found in the lower left corner of the Layout interface.  The labels can be any string values needed by the plug-in, but should be kept to a maximum of one character for appropriate display.  Up to three axis labels may be defined   As the user interacts with these axis buttons, events are generated back to the plug-in regarding their state.
 
initstates is another array that runs parallel to the axisnames array. It indicates, by either a one (1) or a zero (0), whether or not the corresponding axis button should be initially enabled or disabled.
Please note that the pointers you provide for envarray and axisnames should be persistent in your code (i.e., declared statically).  The Dopetrack Proxy does not make a copy of the value you provide, rather it simply caches the pointer and references it as needed.
updateDisplay( )
As the plug-in makes changes to its exposed envelopes (whether through the normal course of its processing, or in response to a Dopetrack event), it should call this function to refresh the keyframe information displayed by the Dopetrack.  Display updating is not automatic; the Dopetrack does not track volatile changes to your envelopes.
selarray = querySelectedKeys( )
When an event is generated that the plug-in has allowed to occur (implicitly or explicitly), the currently selected keyframes indices can be determined using this function. selarray is a pointer to a structure of the following type:

    typedef struct _dtevent_keyselect_st
    {
        int         count;
        LWTime      *indices;
        LWTime      offset;
    } DTKeySelect, *DTKeySelectID;

Time indices selected by the user will appear in the indices array (of count length).  If a shift of keyframes has taken place, the offset (as a delta) is provided.
A NULL value will be returned if no keyframes are currently selected by the user.
markerarray = queryMarkers( )
The user can flag individual time indices by dropping "markers" on them.  While these markers are not used internally by the Dopetrack, they can be queried by a plug-in and processed in some context-specific fashion.:
The markerarray is a linear array of LWTime values, terminated by a time value of 99999.0 (defined in the lwdopetrack.h header as TIME_MARKER).  A NULL value will be returned if no markers are currently defined by the user.
addMarker( at )
A marker can be dropped onto the Dopetrack by the plug-in using the function.  The at parameter is the time index where the marker should appear.
remMarker( at )
An existing marker on the Dopetrack can be removed using this function.  The at parameter is the time index where the marker resides.
bakearray = queryBakeZones( )
The user can define ranges of frames on the Dopetrack.  The plug-in can use these ranges for whatever purpose suites its context.  This function returns a pointer to a structure of the following type:

    typedef struct _dtevent_bakezone_st
    {
        int        count;

        // parallel arrays
        int        *start_frame,*end_frame;
        LWTime     *start_time,*end_time;
    } DTBakeZone, *DTBakeZoneID;

A NULL value will be returned if no Bake Zones are currently defined by the user.
addBakeZone( start, end )
The plug-in can define its own baking zone ranges on the Dopetrack using this funciton.  start and end define the boundaries of the baking zone as time indices.
remBakeZone( start, end )
The plug-in can destroy a baking zone using this funciton.  start and end define the boundaries of the baking zone to match.
querySelection( start, end )
If a user-defined selection exists on the Dopetrack (typically from a drag-select using the right mouse button), the range of the selection can be determined using this function.  The start and end arguments are pointers to storage of type LWTime, and will be filled in with the beginning and ending time indices of the current selection.  If these values are identical, then no user-defined selection currently exists on the Dopetrack.
opmode = queryOpMode( )
The Dopetrack will operate globally on all nine channels at one time, or discretely on a per-channel basis, depending upon the mode selected by the user.  This function will return a value of type DTOperatingMode, which will be one of DTOPMODE_CHANNEL or DTOPMODE_GLOBAL.  Plug-ins are not required to adhere to this user-selected mode, as their context may or may not be conducive to such operational types.
displayMenu( menuCallbacks )
When the user clicks the right-mouse button on the Dopetrack, the Dopetrack API will automatically query the plug-in for a context menu using any menu callbacks functions defined when the plug-in registered.  However, there may be times when the plug-in will need to display a context menu for its own data in the left or right margins of the Dopetrack (see the Tool Drawing section later in this document).
This function call allows the plug-in to display an on-demand context menu.  The menuCallbacks pointer containers the menu callback functions to be used in generating and processing the context menu.  When the user is finished with the menu, the resulting menuID and selected item will be reported back to the menuSelect callback for the plug-in to process, in exactly the same fashion as when the Dopetrack demands a context menu from the plug-in.
open = visible( )
The Boolean returned by this function indicates whether or not the Dopetrack is open (and, thus, visible to the user) or closed.

Interaction

As mentioned before, events will be generated by user interactions, and these events will be passed on to the registered plug-in.  Events are passed to the userEvent function by a pointer to the following structure:

    typedef struct _dteventparam_st
    {
        DTEventType event;
        int         value;
        LWTime      offset;
    } DTEventParam, *DTEventParamID;

The event value will be one of the following::


DTEVENT_KEYFUNCTION


A key function has been generated by user interaction.

DTEVENT_BAKEZONECHANGE

The boundaries of a defined baking zone have been altered.

DTEVENT_EDITSTATECHANGE

The state of the axis buttons has been altered by the user.

DTEVENT_UNDO

The user requested an Undo action.

DTEVENT_REDO

The user requested a Redo action.

DTEVENT_KEYFUNCTION

A key-modification event has occurred.  The value data member of the provided DTEventParam pointer will have one of the following values: 


DTACTION_SELECT


Keyframe selections have changed (added or removed)

DTACTION_SHIFT

Keyframes have been shifted to a new time index

DTACTION_SHIFT_COPY

Copy/paste operation of keyframes at a new location

DTACTION_CREATE_KEY

User request to create a new keyframe.  The offset member of the DTEventParam argument will hold the time index where the key is to be created.

Each of these key-function types can be specifically allowed or disallowed by the plug-in using the allow function pointer. 
Upon receiving notification of any of these events, the plug-in may query the current keyframe selection or selection range settings, expose new envelope arrangements, or have the Dopetrack refresh its display after modification of the exposed envelopes.

DTEVENT_BAKEZONECHANGE

The user has altered the boundaries of one of the defined baking zones.  The value member of the DTEventParam structure will hold the index of the altered baking zone.  When this event is received, the plug-in should immediately call the queryBakeZones() Dopetrack function to update any cached internal bake zone data.

DTEVENT_EDITSTATECHANGE

The user has altered the state of one of the axis buttons that the plug-in has enabled.  The value member of the DTEventParam structure will hold a set of bit flags that indicate the state of each button. 


X


bit 1 is set if the button is enabled (1<<0)

Y

bit 2 is set if the button is enabled (1<<1)

Z

bit 3 is set if the buttno is enabled (1<<2)

Tool Drawing

The margins to the left and right of the open Dopetrack are available to plug-ins for their drawing needs.  Using a series of exported drawing functions, the plug-in can display any information in these areas that will aid the user in their interactions.

The drawfuncs member of the LWDopetrackProxy structure contains a list of drawing functions that the plug-in can be used to update either of these two margin areas.

    typedef struct _dtdrawcallbacks
    {
        int    *(*context)(int side);
        void   (*erase)(int x,int y,int w,int h);
        void   (*pixel)(int x,int y,int color);
        void   (*line)(int x1,int y1,int x2,int y2,int color);
        void   (*rectOutline)(int x,int y,int w,int h,int color);
        void   (*rectFilled)(int x,int y,int w,int h,int bcolor,int fcolor);
        void   (*border)(int x,int y,int w,int h);
        void   (*divider)(int x,int y,int w);
        void   (*text)(int x,int y,int color,const char *text);
        void   (*text_box)(int x,int y,int w,int h,const char *text);
        void   (*button)(int x,int y,int w,int h,const char *text);
        void   (*flush)(void);
    } DTDrawFuncs, *DTDrawFuncsID;

dim[2] context( side )
Before performing any drawing, the plug-in should make a call to this function to select the margin context to receive the drawing action.  A value of zero (0) chooses the left margin, and a value of one (1) chooses the right margin.  An integer pointer is returned that contains the width and height dimensions of the selected margin.
erase( x, y, w, h )
Erases the specified area of the drawing context to the appropriate background color/image.
pixel( x, y, color )
Draws a single pixel in the specified color at the indicated location.
line( startx, starty, endx, endy, color )
Draws a line segment in the specified color between the start and end coordinates.
recOutline( x, y, w, h, color )
Draws a rectangular box using the specified line color.
recFilled( x, y, w, h, bordercolor, fillcolor )
Draws a rectangular box using the specified line bordercolor for the outline, and the interior using the specified fillcolor.
border( x, y, w, h )
Draws a 3D-look rectangular border.
divider( x, y, w )
Draws a 3D-look line divider.
text( x, y, color, text )
Draws provided text string at the indicated location with the specified color.
text_box( x, y, w, h, text )
Draws provided text string within the specified bounding box.  This function is intended for use with RichText strings.
button( x, y, w, h, color )
Draws a button in the current LightWave interface style.  x and y are the column and row position of the upper left corner of the button.  w and h are the width and height, respectively.  You can subsequently use the text() or text_box() functions to draw a label upon the button face.
flush( )
Causes an immediate re-draw of the Dopetrack, along with its left and right margin areas.

Mouse Events

In addition to drawing capabilities, plug-ins can register for events in these areas, creating a two-way interface mechanism, effectively providing these two margin areas to the plug-in for their own, context-specific interface needs.  In order to receive mouse events in these areas, the plug-in needs to fill in the pointer for the mouseEvent function when it registers with the Dopetrack.

Three event types are provided to the plug-in's event callback, defined by the following constants:

    typedef enum
    {
        DTMOUSE_DOWN,
        DTMOUSE_MOVE,
        DTMOUSE_UP
    } DTMouseEvent;

When an event occurs, the callback function will be passed a pointer to a DTMouseParam structure.

    typedef struct _dtevent_mouse_st
    {
        DTMouseEvent event;
        int context;
        int x,y;
        int w,h;
        int button;
    } DTMouseParam, *DTMouseParamID;

event
Contains one of DTMOUSE_DOWN, DTMOUSE_MOVE, DTMOUSE_UP.
context
Will hold a zero (0) if the event took place in the left margin of the Dopetrack, or one (1) if it occurred in the right margin.
x,y
The location of the event.  The values are relative to the boundaries of the margin area.
w,h
Indicates the dimensions of the margin area where the event occurred.
button
Indicates which mouse button was pressed.  Will hold zero (0) for left, one (1) for right, and (2) for the middle button.

Examples

The following code fragments provide a better understanding of the use of the Dopetrack Proxy mechanism.

Registration

    static DTToolCallbacks dtcallbacks = { _dtUserEvent,
                                           _dtAllow,
                                           _dtMenuCount,
                                           NULL,           // no sub-menus defined
                                           _dtMenuSep,
                                           NULL,           // menu items always enabled
                                           _dtMenuItem,
                                           _dtMenuSelect };
    ....
    dtprox = (LWDopetrackProxyID)(*global)(LWDOPETRACK_GLOBAL,GFUSE_TRANSIENT);
    if(dtprox)
        (*dtprox->toolRegister)(&dtcallbacks);

Envelope Exposure

    static LWEnvelopeID expose[4];
    static const char * channel_names[] = {"L","C","M",NULL};
    static int          channel_states[] = {1,0,1};
    ...
    dtprox = (LWDopetrackProxyID)(*global)(LWDOPETRACK_GLOBAL,GFUSE_TRANSIENT);
    if(dtprox)
        (*dtprox->exposeEnvelopes)(expose,channel_names,channel_states);

Support Functions

    void _dtUserEvent(DTEventParamID event)
    {
        DTKeySelectID selected_keys;
        LWDopetrackProxyID dtprox;

        dtprox = (LWDopetrackProxyID)(*GLOBAL)(LWDOPETRACK_GLOBAL,GFUSE_TRANSIENT);
        if(!dtprox) return;
        switch(event->event)
        {
            case DTEVENT_KEYFUNCTION:
                // the user has performed some action on the exposed keys

                // regardless of the action, get the selected key times

                selected_keys = (*dtprox->querySelectedKeys)();
                if(!selected_keys) break; // nothing selected

                switch(event->value)
                {
                    case DTACTION_SELECT:
                    break;

                    case DTACTION_SHIFT:
                    break;

                    case DTACTION_SHIFT_COPY:
                    break;

                    case DTACTION_CREATE_KEY:
                    break;
                }

                break;

            case DTEVENT_EDITSTATECHANGE:
                // the state of an axis button has changed. event->value is:
                // 'X' if(1<<0) (axis is enabled)
                // 'Y' if(1<<1) (axis is enabled)
                // 'Z' if(1<<2) (axis is enabled)

                break;

            case DTEVENT_UNDO:
                // undo my last action

                break;

            case DTEVENT_REDO:
                // redo my last action

                break;

            default:
                // any other actions are context explicit, and
                // come only from any menu the plug-in may chose
                // to provide (see the menu functions below...)
                break;
        }
    }

    int _dtAllow(DTKeyAction action)
    {
        // this function controls the intrinsic actions available
        // to anybody using the Dopetrack -- keyframe selection, movement,
        // and value copying. the tool plug-in can allow or disallow
        // any of these specific functions.

        // any functionality beyond this is strictly defined by the
        // tool plug-in, and exposed to the user via the context menu
        // mechanism.

        switch(action)
        {
            case DTACTION_SELECT:
                // allow the user to select exposed keyframes?
                break;

            case DTACTION_SHIFT:
                // allow the user to move selected keyframes to a new location?
                break;

            case DTACTION_SHIFT_COPY:
                // allow the user to copy values from selected keyframes
                // to create new ones at a new location?
                break;

            case DTACTION_CREATE_KEY:
                // allow the user to create a new keyframe?
                break;

            default:
                return(0); // in case new options are added
        }

        return(0);
    }

    int _dtMenuCount(int menuID)
    {
        LWTime  start,end;
        LWDopetrackProxyID dtprox;

        // how many menu items will be displayed?

        if(menuID == 0)   // main context menu
        {
            dtprox = (LWDopetrackProxyID)(*GLOBAL)(LWDOPETRACK_GLOBAL,GFUSE_TRANSIENT);
            if(!dtprox) return(0);

            (*dtprox->querySelection)(&start,&end);

            if(start == end)  // no selection exists
                return(3);

            return(2);        // "Moe" is only available with a selection
        }

        return(0);   // no sub-menus defined, so nothing more to check
    }

    const char * _dtMenuItem(int menuID,int num)
    {
        static const char *menu_items[] = {"Larry","Curly","Moe"};

        if(menuID == 0)
            return(menu_items[num]);

        return(NULL);   // no sub-menus defined
    }

    int * _dtMenuSep(int menuID)
    {
        static int *menu_seps[] = {
                                   0,   //
                                   1,   // separator following "Curly" on menu
                                   0 }; //

        if(menuID == 0)   // main context menu
            return(menu_seps);

        return(NULL);     // no sub-menus defined
    }

    void _dtMenuSelect(int menuID,int num)
    {
        LWDopetrackProxyID dtprox;

        // the user selected one of my menu items. perform
        // the processing.

        dtprox = (LWDopetrackProxyID)(*GLOBAL)(LWDOPETRACK_GLOBAL,GFUSE_TRANSIENT);
        if(!dtprox) return;

        if(menuID == 0)
        {
            switch(num)
            {
                case 0: // Larry
                    break;
                case 1: // Curley
                    break;
                case 2: // Moe
                    break;
            }
        }
    }