== Action Editor ==

Now it is possible to 'protect' action channels and constraint channels.
* When a channel is 'protected', the only operation possible on keyframes
is selection. All other operations are not able to be performed.
* The padlock to the right of each channel's name toggles the protection
status of that channel. You can only alter the protection status of one
channel at a time.

Todos:
* Menus still pop up when trying to do stuff to keyframes even if
all the keyframes selected are from protected channels.
* Shapekey channels shown in action editor should also get locks
This commit is contained in:
Joshua Leung 2006-12-06 02:37:32 +00:00
parent 4eb3644286
commit 6c138fc6a9
6 changed files with 259 additions and 120 deletions

@ -44,5 +44,7 @@ void draw_ipo_channel(struct gla2DDrawInfo *di, struct Ipo *ipo, int flags, floa
void draw_action_channel(struct gla2DDrawInfo *di, struct bAction *act, int flags, float ypos);
void draw_object_channel(struct gla2DDrawInfo *di, struct Object *ob, int flags, float ypos);
int count_action_levels (bAction *act);
#endif /* BDR_DRAWACTION_H */

@ -46,7 +46,7 @@
#define CHANNELHEIGHT 16
#define CHANNELSKIP 2
#define NAMEWIDTH 128
#define NAMEWIDTH 144
#define SLIDERWIDTH 125
#define ACTWIDTH (G.saction->actwidth)
@ -119,7 +119,6 @@ struct bActionChannel* get_hilighted_action_channel(struct bAction* action);
struct bAction *add_empty_action(int blocktype);
void winqreadactionspace(struct ScrArea *sa, void *spacedata, struct BWinEvent *evt);
struct bAction *bake_action_with_client (struct bAction *act, struct Object *arm, float tolerance);
/* contextual get action */
struct bAction *ob_get_action(struct Object *ob);
@ -127,6 +126,7 @@ struct bAction *ob_get_action(struct Object *ob);
void remake_action_ipos(struct bAction *act);
/* this needs review badly! (ton) */
struct bAction *bake_action_with_client (struct bAction *act, struct Object *arm, float tolerance);
void world2bonespace(float boneSpaceMat[][4], float worldSpace[][4], float restPos[][4], float armPos[][4]);
#endif

@ -122,6 +122,7 @@ typedef struct SpaceAction {
#define ACHAN_SELECTED 0x00000001
#define ACHAN_HILIGHTED 0x00000002
#define ACHAN_HIDDEN 0x00000004
#define ACHAN_PROTECTED 0x00000008
#define ACHAN_MOVED 0x80000000
/* SpaceAction flag */

@ -249,7 +249,8 @@ typedef struct bRigidBodyJointConstraint{
/* bConstraintChannel.flag */
#define CONSTRAINT_CHANNEL_SELECT 0x01
#define CONSTRAINT_CHANNEL_SELECT 0x01
#define CONSTRAINT_CHANNEL_PROTECTED 0x02
/* bRotateLikeConstraint.flag */
#define ROTLIKE_X 0x01

@ -92,7 +92,6 @@
/* local functions ----------------------------------------------------- */
int count_action_levels(bAction *act);
static ListBase *ipo_to_keylist(Ipo *ipo, int flags);
static ListBase *action_to_keylist(bAction *act, int flags);
static ListBase *ob_to_keylist(Object *ob, int flags);
@ -227,9 +226,19 @@ static void draw_action_channel_names(bAction *act)
for (chan=act->chanbase.first; chan; chan=chan->next){
if((chan->flag & ACHAN_HIDDEN)==0) {
glEnable(GL_BLEND);
/* draw backing strip behind action channel name */
BIF_ThemeColorShade(TH_HEADER, 20);
glRectf(x, y-CHANNELHEIGHT/2, (float)NAMEWIDTH, y+CHANNELHEIGHT/2);
/* draw 'lock' indicating whether channel is protected */
if (chan->flag & ACHAN_PROTECTED)
BIF_icon_draw(NAMEWIDTH-16, y-CHANNELHEIGHT/2, ICON_LOCKED);
else
BIF_icon_draw(NAMEWIDTH-16, y-CHANNELHEIGHT/2, ICON_UNLOCKED);
/* draw name of action channel */
if (chan->flag & ACHAN_SELECTED)
BIF_ThemeColor(TH_TEXT_HI);
else
@ -239,17 +248,24 @@ static void draw_action_channel_names(bAction *act)
y-=CHANNELHEIGHT+CHANNELSKIP;
/* Draw constraint channels */
for (conchan=chan->constraintChannels.first;
conchan; conchan=conchan->next){
for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next) {
/* draw 'lock' to indicate if constraint channel is protected */
if (conchan->flag & CONSTRAINT_CHANNEL_PROTECTED)
BIF_icon_draw(NAMEWIDTH-16, y-CHANNELHEIGHT/2, ICON_LOCKED);
else
BIF_icon_draw(NAMEWIDTH-16, y-CHANNELHEIGHT/2, ICON_UNLOCKED);
/* draw name of constraint channel */
if (conchan->flag & CONSTRAINT_CHANNEL_SELECT)
BIF_ThemeColor(TH_TEXT_HI);
else
BIF_ThemeColor(TH_TEXT);
glRasterPos2f(x+32, y-4);
BMF_DrawString(G.font, conchan->name);
y-=CHANNELHEIGHT+CHANNELSKIP;
}
glDisable(GL_BLEND);
}
}
}

@ -88,20 +88,20 @@
#include "BSE_time.h"
#include "BSE_trans_types.h"
#include "BDR_drawaction.h"
#include "BDR_editobject.h"
#include "mydevice.h"
#include "blendef.h"
#include "nla.h"
extern int count_action_levels (bAction *act);
void top_sel_action();
void up_sel_action();
void bottom_sel_action();
void down_sel_action();
/* Useful macros ----------------------------------------------- */
#define BEZSELECTED(bezt) (((bezt)->f1 & 1) || ((bezt)->f2 & 1) || ((bezt)->f3 & 1))
#define VISIBLE_ACHAN(achan) ((achan->flag & ACHAN_HIDDEN)==0)
#define EDITABLE_ACHAN(achan) (((achan->flag & ACHAN_HIDDEN)==0) && ((achan->flag & ACHAN_PROTECTED)==0))
#define EDITABLE_CONCHAN(conchan) ((conchan->flag & CONSTRAINT_CHANNEL_PROTECTED)==0)
/* Local Function prototypes, are forward needed */
static void hilight_channel (bAction *act, bActionChannel *chan, short hilight);
@ -220,7 +220,7 @@ void duplicate_meshchannel_keys(Key *key)
void duplicate_actionchannel_keys(void)
{
bAction *act;
bActionChannel *chan;
bActionChannel *achan;
bConstraintChannel *conchan;
act=G.saction->action;
@ -228,11 +228,15 @@ void duplicate_actionchannel_keys(void)
return;
/* Find selected items */
for (chan = act->chanbase.first; chan; chan=chan->next){
if((chan->flag & ACHAN_HIDDEN)==0) {
duplicate_ipo_keys(chan->ipo);
for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
duplicate_ipo_keys(conchan->ipo);
for (achan = act->chanbase.first; achan; achan= achan->next){
if(EDITABLE_ACHAN(achan))
duplicate_ipo_keys(achan->ipo);
if (VISIBLE_ACHAN(achan)) {
for (conchan=achan->constraintChannels.first; conchan; conchan=conchan->next) {
if (EDITABLE_CONCHAN(conchan))
duplicate_ipo_keys(conchan->ipo);
}
}
}
@ -961,8 +965,8 @@ void transform_actionchannel_keys(int mode, int dummy)
bAction *act;
TransVert *tv;
Object *ob= OBACT;
bActionChannel *achan;
bConstraintChannel *conchan;
bActionChannel *chan;
float deltax, startx;
float minx, maxx, cenf[2];
float sval[2], cval[2], lastcval[2]={0,0};
@ -979,12 +983,15 @@ void transform_actionchannel_keys(int mode, int dummy)
if(act==NULL) return;
/* Ensure that partial selections result in beztriple selections */
for (chan=act->chanbase.first; chan; chan=chan->next){
if((chan->flag & ACHAN_HIDDEN)==0) {
tvtot+=fullselect_ipo_keys(chan->ipo);
for (achan=act->chanbase.first; achan; achan= achan->next){
if (EDITABLE_ACHAN(achan))
tvtot+=fullselect_ipo_keys(achan->ipo);
for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
tvtot+=fullselect_ipo_keys(conchan->ipo);
if (VISIBLE_ACHAN(achan)) {
for (conchan=achan->constraintChannels.first; conchan; conchan=conchan->next) {
if (EDITABLE_CONCHAN(conchan))
tvtot+=fullselect_ipo_keys(conchan->ipo);
}
}
}
@ -997,12 +1004,15 @@ void transform_actionchannel_keys(int mode, int dummy)
tv = MEM_callocN (sizeof(TransVert) * tvtot, "transVert");
tvtot=0;
for (chan=act->chanbase.first; chan; chan=chan->next){
if((chan->flag & ACHAN_HIDDEN)==0) {
/* Add the actionchannel */
tvtot = add_trans_ipo_keys(chan->ipo, tv, tvtot);
for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
tvtot = add_trans_ipo_keys(conchan->ipo, tv, tvtot);
for (achan=act->chanbase.first; achan; achan= achan->next){
if(EDITABLE_ACHAN(achan))
tvtot = add_trans_ipo_keys(achan->ipo, tv, tvtot);
if (VISIBLE_ACHAN(achan)) {
for (conchan=achan->constraintChannels.first; conchan; conchan=conchan->next) {
if (EDITABLE_CONCHAN(conchan))
tvtot = add_trans_ipo_keys(conchan->ipo, tv, tvtot);
}
}
}
@ -1681,6 +1691,77 @@ static void mouse_actionchannels(bAction *act, short *mval,
allqueue (REDRAWBUTSALL, 0);
}
/* turn on/off protect option for action channel */
static void mouse_actionchannels_protect (bAction *act, short *mval)
{
bActionChannel *achan;
bConstraintChannel *conchan;
float x,y;
int clickmin, clickmax;
int wsize, lock;
/* wsize is the greatest possible height (in pixels) that would be
* needed to draw all of the action channels and constraint
* channels.
*/
wsize = count_action_levels(act)*(CHANNELHEIGHT+CHANNELSKIP);
wsize += CHANNELHEIGHT/2;
areamouseco_to_ipoco(G.v2d, mval, &x, &y);
clickmin = (int) ((wsize - y) / (CHANNELHEIGHT+CHANNELSKIP));
clickmax = clickmin;
if (clickmax < 0)
return;
/* clickmin and clickmax now coorespond to indices into
* the collection of channels and constraint channels.
* What we need to do is turn locks on/off for all action
* channels and constraint channels between these indices.
* This is done by traversing the channels and constraint
* channels, for each item decrementing clickmin and clickmax.
* When clickmin is less than zero we start locking stuff,
* until clickmax is less than zero or we run out of channels
* and constraint channels.
*/
for (achan = act->chanbase.first; achan; achan= achan->next){
if((achan->flag & ACHAN_HIDDEN)==0) {
if (clickmax < 0) break;
/* assume locking this action channel */
if ( clickmin <= 0) {
/* invert the channel's protect property */
lock = (achan->flag & ACHAN_PROTECTED);
if (lock)
achan->flag &= ~ACHAN_PROTECTED;
else
achan->flag |= ACHAN_PROTECTED;
}
--clickmin;
--clickmax;
/* Check for click in a constraint channel */
for (conchan=achan->constraintChannels.first; conchan; conchan=conchan->next){
if (clickmax < 0) break;
if ( clickmin <= 0) {
/* invert the channel's protect property */
lock = (conchan->flag & CONSTRAINT_CHANNEL_PROTECTED);
if (lock)
conchan->flag &= ~CONSTRAINT_CHANNEL_PROTECTED;
else
conchan->flag |= CONSTRAINT_CHANNEL_PROTECTED;
}
--clickmin;
--clickmax;
}
}
}
allqueue (REDRAWACTION, 0);
}
void delete_meshchannel_keys(Key *key)
{
if (!okee("Erase selected keys"))
@ -1695,7 +1776,7 @@ void delete_meshchannel_keys(Key *key)
void delete_actionchannel_keys(void)
{
bAction *act;
bActionChannel *chan;
bActionChannel *achan;
bConstraintChannel *conchan;
act = G.saction->action;
@ -1705,19 +1786,22 @@ void delete_actionchannel_keys(void)
if (!okee("Erase selected keys"))
return;
for (chan = act->chanbase.first; chan; chan=chan->next){
if((chan->flag & ACHAN_HIDDEN)==0) {
for (achan = act->chanbase.first; achan; achan= achan->next){
if(EDITABLE_ACHAN(achan)) {
/* Check action channel keys*/
delete_ipo_keys(chan->ipo);
delete_ipo_keys(achan->ipo);
}
if (VISIBLE_ACHAN(achan)) {
/* Delete constraint channel keys */
for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
delete_ipo_keys(conchan->ipo);
for (conchan=achan->constraintChannels.first; conchan; conchan=conchan->next) {
if (EDITABLE_CONCHAN(conchan))
delete_ipo_keys(conchan->ipo);
}
}
}
remake_action_ipos (act);
remake_action_ipos(act);
BIF_undo_push("Delete Action keys");
allspace(REMAKEIPO, 0);
allqueue(REDRAWACTION, 0);
@ -1822,7 +1906,7 @@ void clean_shapekeys(Key *key)
void clean_actionchannels(bAction *act)
{
bActionChannel *chan;
bActionChannel *achan;
bConstraintChannel *conchan;
Ipo *ipo;
@ -1830,7 +1914,6 @@ void clean_actionchannels(bAction *act)
int ok;
/* don't proceed any further if no action or user refuses */
if (!act) return;
if (G.scene->toolsettings->clean_thresh==0)
@ -1841,23 +1924,27 @@ void clean_actionchannels(bAction *act)
if (!ok) return;
/* clean selected channels only */
for (chan= act->chanbase.first; chan; chan= chan->next) {
if((chan->flag & ACHAN_HIDDEN)==0) {
for (achan= act->chanbase.first; achan; achan= achan->next) {
if(EDITABLE_ACHAN(achan)) {
/* clean if action channel if selected */
if (chan->flag & ACHAN_SELECTED) {
ipo= chan->ipo;
if (achan->flag & ACHAN_SELECTED) {
ipo= achan->ipo;
if (ipo) {
for (icu= ipo->curve.first; icu; icu= icu->next)
clean_ipo_curve(icu);
}
}
}
if (VISIBLE_ACHAN(achan)) {
/* clean action channel's constraint channels */
for (conchan= chan->constraintChannels.first; conchan; conchan=conchan->next) {
ipo= conchan->ipo;
if (ipo) {
for (icu= ipo->curve.first; icu; icu= icu->next)
clean_ipo_curve(icu);
for (conchan= achan->constraintChannels.first; conchan; conchan=conchan->next) {
if (EDITABLE_CONCHAN(conchan)) {
ipo= conchan->ipo;
if (ipo) {
for (icu= ipo->curve.first; icu; icu= icu->next)
clean_ipo_curve(icu);
}
}
}
}
@ -1883,7 +1970,8 @@ void sethandles_meshchannel_keys(int code, Key *key)
void sethandles_actionchannel_keys(int code)
{
bAction *act;
bActionChannel *chan;
bActionChannel *achan;
bConstraintChannel *conchan;
/* Get the selected action, exit if none are selected
*/
@ -1894,14 +1982,21 @@ void sethandles_actionchannel_keys(int code)
/* Loop through the channels and set the beziers
* of the selected keys based on the integer code
*/
for (chan = act->chanbase.first; chan; chan=chan->next){
if((chan->flag & ACHAN_HIDDEN)==0)
sethandles_ipo_keys(chan->ipo, code);
for (achan = act->chanbase.first; achan; achan= achan->next){
if(EDITABLE_ACHAN(achan))
sethandles_ipo_keys(achan->ipo, code);
if (VISIBLE_ACHAN(achan)) {
for (conchan= achan->constraintChannels.first; conchan; conchan= conchan->next) {
if (EDITABLE_CONCHAN(conchan))
sethandles_ipo_keys(conchan->ipo, code);
}
}
}
/* Clean up and redraw stuff
*/
remake_action_ipos (act);
remake_action_ipos(act);
BIF_undo_push("Set handles Action channel");
allspace(REMAKEIPO, 0);
allqueue(REDRAWACTION, 0);
@ -1913,7 +2008,7 @@ void set_ipotype_actionchannels(int ipotype)
{
bAction *act;
bActionChannel *chan;
bActionChannel *achan;
bConstraintChannel *conchan;
short event;
@ -1940,26 +2035,30 @@ void set_ipotype_actionchannels(int ipotype)
* the type for each Ipo curve in the channel Ipo (based on
* the value from the popup).
*/
for (chan = act->chanbase.first; chan; chan=chan->next){
if((chan->flag & ACHAN_HIDDEN)==0) {
if (chan->flag & ACHAN_SELECTED){
if (chan->ipo)
setipotype_ipo(chan->ipo, ipotype);
for (achan = act->chanbase.first; achan; achan= achan->next){
if(EDITABLE_ACHAN(achan)) {
if (achan->flag & ACHAN_SELECTED){
if (achan->ipo)
setipotype_ipo(achan->ipo, ipotype);
}
}
if (VISIBLE_ACHAN(achan)) {
/* constraint channels */
for (conchan=chan->constraintChannels.first; conchan; conchan= conchan->next) {
if (conchan->flag & CONSTRAINT_CHANNEL_SELECT) {
if (conchan->ipo)
setipotype_ipo(conchan->ipo, ipotype);
for (conchan=achan->constraintChannels.first; conchan; conchan= conchan->next) {
if (EDITABLE_CONCHAN(conchan)) {
if (conchan->flag & CONSTRAINT_CHANNEL_SELECT) {
if (conchan->ipo)
setipotype_ipo(conchan->ipo, ipotype);
}
}
}
}
}
/* Clean up and redraw stuff
*/
remake_action_ipos (act);
remake_action_ipos(act);
BIF_undo_push("Set Ipo type Action channel");
allspace(REMAKEIPO, 0);
allqueue(REDRAWACTION, 0);
@ -1970,7 +2069,7 @@ void set_ipotype_actionchannels(int ipotype)
void set_extendtype_actionchannels(int extendtype)
{
bAction *act;
bActionChannel *chan;
bActionChannel *achan;
bConstraintChannel *conchan;
short event;
@ -1998,43 +2097,48 @@ void set_extendtype_actionchannels(int extendtype)
* the type for each Ipo curve in the channel Ipo (based on
* the value from the popup).
*/
for (chan = act->chanbase.first; chan; chan=chan->next){
if((chan->flag & ACHAN_HIDDEN)==0) {
if (chan->flag & ACHAN_SELECTED) {
if (chan->ipo) {
for (achan = act->chanbase.first; achan; achan= achan->next){
if (EDITABLE_ACHAN(achan)) {
if (achan->flag & ACHAN_SELECTED) {
if (achan->ipo) {
switch (extendtype) {
case SET_EXTEND_CONSTANT:
setexprap_ipoloop(chan->ipo, IPO_HORIZ);
setexprap_ipoloop(achan->ipo, IPO_HORIZ);
break;
case SET_EXTEND_EXTRAPOLATION:
setexprap_ipoloop(chan->ipo, IPO_DIR);
setexprap_ipoloop(achan->ipo, IPO_DIR);
break;
case SET_EXTEND_CYCLIC:
setexprap_ipoloop(chan->ipo, IPO_CYCL);
setexprap_ipoloop(achan->ipo, IPO_CYCL);
break;
case SET_EXTEND_CYCLICEXTRAPOLATION:
setexprap_ipoloop(chan->ipo, IPO_CYCLX);
setexprap_ipoloop(achan->ipo, IPO_CYCLX);
break;
}
}
}
}
if (VISIBLE_ACHAN(achan)) {
/* constraint channels */
for (conchan=chan->constraintChannels.first; conchan; conchan= conchan->next) {
if (conchan->flag & CONSTRAINT_CHANNEL_SELECT) {
if (conchan->ipo) {
switch (extendtype) {
case SET_EXTEND_CONSTANT:
setexprap_ipoloop(conchan->ipo, IPO_HORIZ);
break;
case SET_EXTEND_EXTRAPOLATION:
setexprap_ipoloop(conchan->ipo, IPO_DIR);
break;
case SET_EXTEND_CYCLIC:
setexprap_ipoloop(conchan->ipo, IPO_CYCL);
break;
case SET_EXTEND_CYCLICEXTRAPOLATION:
setexprap_ipoloop(conchan->ipo, IPO_CYCLX);
break;
for (conchan=achan->constraintChannels.first; conchan; conchan= conchan->next) {
if (EDITABLE_CONCHAN(conchan)) {
if (conchan->flag & CONSTRAINT_CHANNEL_SELECT) {
if (conchan->ipo) {
switch (extendtype) {
case SET_EXTEND_CONSTANT:
setexprap_ipoloop(conchan->ipo, IPO_HORIZ);
break;
case SET_EXTEND_EXTRAPOLATION:
setexprap_ipoloop(conchan->ipo, IPO_DIR);
break;
case SET_EXTEND_CYCLIC:
setexprap_ipoloop(conchan->ipo, IPO_CYCL);
break;
case SET_EXTEND_CYCLICEXTRAPOLATION:
setexprap_ipoloop(conchan->ipo, IPO_CYCLX);
break;
}
}
}
}
@ -2044,7 +2148,7 @@ void set_extendtype_actionchannels(int extendtype)
/* Clean up and redraw stuff
*/
remake_action_ipos (act);
remake_action_ipos(act);
BIF_undo_push("Set Ipo type Action channel");
allspace(REMAKEIPO, 0);
allqueue(REDRAWACTION, 0);
@ -2055,19 +2159,22 @@ void set_extendtype_actionchannels(int extendtype)
static void set_snap_actionchannels(bAction *act, short snaptype)
{
/* snapping function for action channels*/
bActionChannel *chan;
bActionChannel *achan;
bConstraintChannel *conchan;
/* Loop through the channels */
for (chan = act->chanbase.first; chan; chan=chan->next){
if((chan->flag & ACHAN_HIDDEN)==0) {
if (chan->ipo) {
snap_ipo_keys(chan->ipo, snaptype);
}
for (achan = act->chanbase.first; achan; achan= achan->next){
if(EDITABLE_ACHAN(achan)) {
if (achan->ipo)
snap_ipo_keys(achan->ipo, snaptype);
}
if (VISIBLE_ACHAN(achan)) {
/* constraint channels */
for (conchan=chan->constraintChannels.first; conchan; conchan= conchan->next) {
if (conchan->ipo) {
snap_ipo_keys(conchan->ipo, snaptype);
for (conchan=achan->constraintChannels.first; conchan; conchan= conchan->next) {
if (EDITABLE_CONCHAN(conchan)) {
if (conchan->ipo)
snap_ipo_keys(conchan->ipo, snaptype);
}
}
}
@ -2130,19 +2237,22 @@ void snap_keys_to_frame(int snap_mode)
static void mirror_actionchannels(bAction *act, short mirror_mode)
{
/* mirror function for action channels */
bActionChannel *chan;
bActionChannel *achan;
bConstraintChannel *conchan;
/* Loop through the channels */
for (chan = act->chanbase.first; chan; chan=chan->next){
if((chan->flag & ACHAN_HIDDEN)==0) {
if (chan->ipo) {
mirror_ipo_keys(chan->ipo, mirror_mode);
}
for (achan = act->chanbase.first; achan; achan= achan->next){
if(EDITABLE_ACHAN(achan)) {
if (achan->ipo)
mirror_ipo_keys(achan->ipo, mirror_mode);
}
if (VISIBLE_ACHAN(achan)) {
/* constraint channels */
for (conchan=chan->constraintChannels.first; conchan; conchan= conchan->next) {
if (conchan->ipo) {
mirror_ipo_keys(conchan->ipo, mirror_mode);
for (conchan=achan->constraintChannels.first; conchan; conchan= conchan->next) {
if (EDITABLE_CONCHAN(conchan)) {
if (conchan->ipo)
mirror_ipo_keys(conchan->ipo, mirror_mode);
}
}
}
@ -2870,12 +2980,21 @@ void winqreadactionspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
*/
if (mval[0]<NAMEWIDTH) {
if(act) {
if(G.qual & LR_SHIFTKEY)
mouse_actionchannels(act, mval, NULL, SELECT_INVERT);
else
mouse_actionchannels(act, mval, NULL, SELECT_REPLACE);
BIF_undo_push("Select Action");
if (mval[0] < (NAMEWIDTH-16)) {
/* mouse is over action channels */
if(G.qual & LR_SHIFTKEY)
mouse_actionchannels(act, mval, NULL, SELECT_INVERT);
else
mouse_actionchannels(act, mval, NULL, SELECT_REPLACE);
BIF_undo_push("Select Action");
}
else {
/* mouse is over channel locks */
mouse_actionchannels_protect(act, mval);
BIF_undo_push("Protect Channel");
}
}
else numbuts_action();
}