### 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 `static`ally). 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; } } }`