
- border select initiated in the channel names border selects the channels and constraint channels. - right click or border select initiated in the horizontal scroll causes blender to select all keys for the selected frames. - right click or border select in the vertical scroll causes blender to select all keys for the channel or constraint channels that are to the left of the selection.
1855 lines
42 KiB
C
1855 lines
42 KiB
C
/**
|
|
* $Id$
|
|
*
|
|
* ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version. The Blender
|
|
* Foundation also sells licenses for use in proprietary software under
|
|
* the Blender License. See http://www.blender.org/BL/ for information
|
|
* about this.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
|
|
* All rights reserved.
|
|
*
|
|
* The Original Code is: all of this file.
|
|
*
|
|
* Contributor(s): none yet.
|
|
*
|
|
* ***** END GPL/BL DUAL LICENSE BLOCK *****
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <math.h>
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#ifdef WIN32
|
|
#include "BLI_winstuff.h"
|
|
#endif
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "PIL_time.h"
|
|
|
|
#include "BLI_blenlib.h"
|
|
#include "BLI_arithb.h"
|
|
|
|
#include "DNA_action_types.h"
|
|
#include "DNA_armature_types.h"
|
|
#include "DNA_curve_types.h"
|
|
#include "DNA_ipo_types.h"
|
|
#include "DNA_object_types.h"
|
|
#include "DNA_scene_types.h"
|
|
#include "DNA_screen_types.h"
|
|
#include "DNA_userdef_types.h"
|
|
#include "DNA_constraint_types.h"
|
|
|
|
#include "BKE_armature.h"
|
|
#include "BKE_constraint.h"
|
|
#include "BKE_displist.h"
|
|
#include "BKE_curve.h"
|
|
#include "BKE_ipo.h"
|
|
#include "BKE_global.h"
|
|
#include "BKE_library.h"
|
|
#include "BKE_main.h"
|
|
#include "BKE_action.h"
|
|
|
|
#include "BIF_gl.h"
|
|
#include "BIF_mywindow.h"
|
|
#include "BIF_toolbox.h"
|
|
#include "BIF_screen.h"
|
|
#include "BIF_space.h"
|
|
#include "BIF_buttons.h"
|
|
#include "BIF_interface.h"
|
|
#include "BIF_editview.h"
|
|
#include "BIF_poseobject.h"
|
|
#include "BIF_editarmature.h"
|
|
|
|
#include "BSE_edit.h"
|
|
#include "BSE_drawipo.h"
|
|
#include "BSE_headerbuttons.h"
|
|
#include "BSE_editipo.h"
|
|
#include "BSE_editaction.h"
|
|
#include "BSE_trans_types.h"
|
|
#include "BSE_editaction_types.h"
|
|
|
|
#include "BDR_editobject.h"
|
|
|
|
#include "blendertimer.h"
|
|
|
|
#include "interface.h"
|
|
#include "mydevice.h"
|
|
#include "blendef.h"
|
|
#include "nla.h"
|
|
|
|
static bPose *g_posebuf=NULL;
|
|
extern int count_action_levels (bAction *act);
|
|
|
|
#define BEZSELECTED(bezt) (((bezt)->f1 & 1) || ((bezt)->f2 & 1) || ((bezt)->f3 & 1))
|
|
|
|
/* Local Function prototypes */
|
|
|
|
static void insertactionkey(bAction *act, bActionChannel *achan, bPoseChannel *chan, int adrcode, short makecurve, float time);
|
|
static void flip_name (char *name);
|
|
static void mouse_actionchannels(bAction *act, short *mval,
|
|
short *mvalo, int selectmode);
|
|
static void borderselect_action(void);
|
|
static void mouse_action(int selectmode);
|
|
static bActionChannel *get_nearest_actionchannel_key (float *index, short *sel, bConstraintChannel **conchan);
|
|
static void delete_actionchannels(void);
|
|
static void delete_actionchannel_keys(void);
|
|
static void duplicate_actionchannel_keys(void);
|
|
static void transform_actionchannel_keys(char mode);
|
|
static void select_poseelement_by_name (char *name, int select);
|
|
static void hilight_channel (bAction *act, bActionChannel *chan, short hilight);
|
|
static void set_action_key_time (bAction *act, bPoseChannel *chan, int adrcode, short makecurve, float time);
|
|
|
|
/* Implementation */
|
|
|
|
static void select_poseelement_by_name (char *name, int select)
|
|
{
|
|
/* Synchs selection of channels with selection of object elements in posemode */
|
|
|
|
Object *ob;
|
|
|
|
ob = G.obpose;
|
|
if (!ob)
|
|
return;
|
|
|
|
switch (ob->type){
|
|
case OB_ARMATURE:
|
|
select_bone_by_name ((bArmature*)ob->data, name, select);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
#ifdef __NLA_BAKE
|
|
bAction* bake_action_with_client (bAction *act, Object *armob, float tolerance)
|
|
{
|
|
bAction *result=NULL;
|
|
bActionChannel *achan;
|
|
float actlen;
|
|
int curframe;
|
|
char newname[64];
|
|
bArmature *arm;
|
|
Bone *bone;
|
|
float oldframe;
|
|
bAction *temp;
|
|
bPoseChannel *pchan;
|
|
|
|
if (!act)
|
|
return NULL;
|
|
|
|
arm = get_armature(armob);
|
|
|
|
|
|
if (G.obedit){
|
|
error ("Not in editmode");
|
|
return NULL;
|
|
}
|
|
|
|
if (!arm){
|
|
error ("Must have an armature selected");
|
|
return NULL;
|
|
}
|
|
/* Get a new action */
|
|
result = add_empty_action();
|
|
|
|
/* Assign the new action a unique name */
|
|
sprintf (newname, "%s.BAKED", act->id.name+2);
|
|
rename_id(&result->id, newname);
|
|
|
|
actlen = calc_action_end(act);
|
|
|
|
oldframe = G.scene->r.cfra;
|
|
|
|
temp = armob->action;
|
|
armob->action = act;
|
|
|
|
for (curframe=1; curframe<ceil(actlen+1); curframe++){
|
|
|
|
/* Apply the old action */
|
|
|
|
G.scene->r.cfra = curframe;
|
|
|
|
/* Apply the object ipo */
|
|
get_pose_from_action(&armob->pose, act, curframe);
|
|
apply_pose_armature(arm, armob->pose, 1);
|
|
clear_object_constraint_status(armob);
|
|
where_is_armature_time(armob, curframe);
|
|
|
|
/* For each channel: set avail keys with current values */
|
|
for (pchan=armob->pose->chanbase.first; pchan; pchan=pchan->next){
|
|
|
|
/* Copy the constraints from the armature (if any) */
|
|
|
|
bone = get_named_bone(arm, pchan->name);
|
|
if (bone){
|
|
|
|
Mat4ToQuat(pchan->obmat, pchan->quat);
|
|
Mat4ToSize(pchan->obmat, pchan->size);
|
|
VECCOPY(pchan->loc, pchan->obmat[3]);
|
|
|
|
/* Apply to keys */
|
|
set_action_key_time (result, pchan, AC_QUAT_X, 1, curframe);
|
|
set_action_key_time (result, pchan, AC_QUAT_Y, 1, curframe);
|
|
set_action_key_time (result, pchan, AC_QUAT_Z, 1, curframe);
|
|
set_action_key_time (result, pchan, AC_QUAT_W, 1, curframe);
|
|
set_action_key_time (result, pchan, AC_LOC_X, 1, curframe);
|
|
set_action_key_time (result, pchan, AC_LOC_Y, 1, curframe);
|
|
set_action_key_time (result, pchan, AC_LOC_Z, 1, curframe);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Make another pass to ensure all keyframes are set to linear interpolation mode */
|
|
for (achan = result->chanbase.first; achan; achan=achan->next){
|
|
IpoCurve* icu;
|
|
for (icu = achan->ipo->curve.first; icu; icu=icu->next){
|
|
icu->ipo= IPO_LIN;
|
|
}
|
|
}
|
|
|
|
notice ("Made new action \"%s\"", newname);
|
|
G.scene->r.cfra = oldframe;
|
|
armob->action = temp;
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
void select_actionchannel_by_name (bAction *act, char *name, int select)
|
|
{
|
|
bActionChannel *chan;
|
|
|
|
if (!act)
|
|
return;
|
|
|
|
for (chan = act->chanbase.first; chan; chan=chan->next){
|
|
if (!strcmp (chan->name, name)){
|
|
act->achan = chan;
|
|
if (select){
|
|
chan->flag |= ACHAN_SELECTED;
|
|
hilight_channel (act, chan, 1);
|
|
}
|
|
else{
|
|
chan->flag &= ~ACHAN_SELECTED;
|
|
hilight_channel (act, chan, 0);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void remake_action_ipos(bAction *act)
|
|
{
|
|
bActionChannel *chan;
|
|
bConstraintChannel *conchan;
|
|
IpoCurve *icu;
|
|
|
|
for (chan= act->chanbase.first; chan; chan=chan->next){
|
|
if (chan->ipo){
|
|
for (icu = chan->ipo->curve.first; icu; icu=icu->next){
|
|
sort_time_ipocurve(icu);
|
|
testhandles_ipocurve(icu);
|
|
}
|
|
}
|
|
for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
|
|
if (conchan->ipo){
|
|
for (icu = conchan->ipo->curve.first; icu; icu=icu->next){
|
|
sort_time_ipocurve(icu);
|
|
testhandles_ipocurve(icu);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void duplicate_actionchannel_keys(void)
|
|
{
|
|
bAction *act;
|
|
bActionChannel *chan;
|
|
bConstraintChannel *conchan;
|
|
|
|
act=G.saction->action;
|
|
if (!act)
|
|
return;
|
|
|
|
/* Find selected items */
|
|
for (chan = act->chanbase.first; chan; chan=chan->next){
|
|
duplicate_ipo_keys(chan->ipo);
|
|
for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
|
|
duplicate_ipo_keys(conchan->ipo);
|
|
}
|
|
|
|
transform_actionchannel_keys ('g');
|
|
}
|
|
|
|
static bActionChannel *get_nearest_actionchannel_key (float *index, short *sel, bConstraintChannel **rchan){
|
|
bAction *act;
|
|
bActionChannel *chan;
|
|
IpoCurve *icu;
|
|
bActionChannel *firstchan=NULL;
|
|
bConstraintChannel *conchan, *firstconchan=NULL;
|
|
int foundsel=0;
|
|
float firstvert=-1, foundx=-1;
|
|
int i;
|
|
short mval[2];
|
|
float ymin, ymax;
|
|
rctf rectf;
|
|
*index=0;
|
|
|
|
*rchan=NULL;
|
|
act=G.saction->action; /* We presume that we are only called during a valid action */
|
|
|
|
getmouseco_areawin (mval);
|
|
|
|
mval[0]-=7;
|
|
areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin);
|
|
|
|
mval[0]+=14;
|
|
areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax);
|
|
|
|
ymax = count_action_levels (act) * (CHANNELHEIGHT + CHANNELSKIP);
|
|
|
|
*sel=0;
|
|
|
|
for (chan=act->chanbase.first; chan; chan=chan->next){
|
|
|
|
/* Check action channel */
|
|
ymin=ymax-(CHANNELHEIGHT+CHANNELSKIP);
|
|
if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))){
|
|
for (icu=chan->ipo->curve.first; icu; icu=icu->next){
|
|
for (i=0; i<icu->totvert; i++){
|
|
if (icu->bezt[i].vec[1][0] > rectf.xmin && icu->bezt[i].vec[1][0] <= rectf.xmax ){
|
|
if (!firstchan){
|
|
firstchan=chan;
|
|
firstvert=icu->bezt[i].vec[1][0];
|
|
*sel = icu->bezt[i].f2 & 1;
|
|
}
|
|
|
|
if (icu->bezt[i].f2 & 1){
|
|
if (!foundsel){
|
|
foundsel=1;
|
|
foundx = icu->bezt[i].vec[1][0];
|
|
}
|
|
}
|
|
else if (foundsel && icu->bezt[i].vec[1][0] != foundx){
|
|
*index=icu->bezt[i].vec[1][0];
|
|
*sel = 0;
|
|
return chan;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ymax=ymin;
|
|
|
|
/* Check constraint channels */
|
|
for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
|
|
ymin=ymax-(CHANNELHEIGHT+CHANNELSKIP);
|
|
if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))){
|
|
for (icu=conchan->ipo->curve.first; icu; icu=icu->next){
|
|
for (i=0; i<icu->totvert; i++){
|
|
if (icu->bezt[i].vec[1][0] > rectf.xmin && icu->bezt[i].vec[1][0] <= rectf.xmax ){
|
|
if (!firstchan){
|
|
firstchan=chan;
|
|
firstconchan=conchan;
|
|
firstvert=icu->bezt[i].vec[1][0];
|
|
*sel = icu->bezt[i].f2 & 1;
|
|
}
|
|
|
|
if (icu->bezt[i].f2 & 1){
|
|
if (!foundsel){
|
|
foundsel=1;
|
|
foundx = icu->bezt[i].vec[1][0];
|
|
}
|
|
}
|
|
else if (foundsel && icu->bezt[i].vec[1][0] != foundx){
|
|
*index=icu->bezt[i].vec[1][0];
|
|
*sel = 0;
|
|
*rchan = conchan;
|
|
return chan;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ymax=ymin;
|
|
}
|
|
}
|
|
|
|
*rchan = firstconchan;
|
|
*index=firstvert;
|
|
return firstchan;
|
|
}
|
|
|
|
static void mouse_action(int selectmode)
|
|
{
|
|
bAction *act;
|
|
short sel;
|
|
float selx;
|
|
bActionChannel *chan;
|
|
bConstraintChannel *conchan;
|
|
short mval[2];
|
|
|
|
act=G.saction->action;
|
|
if (!act)
|
|
return;
|
|
|
|
getmouseco_areawin (mval);
|
|
|
|
chan=get_nearest_actionchannel_key(&selx, &sel, &conchan);
|
|
|
|
if (chan){
|
|
if (selectmode == SELECT_REPLACE) {
|
|
if (sel == 0)
|
|
selectmode = SELECT_ADD;
|
|
else
|
|
selectmode = SELECT_SUBTRACT;
|
|
deselect_actionchannel_keys(act, 0);
|
|
deselect_actionchannels(act, 0);
|
|
act->achan = chan;
|
|
chan->flag |= ACHAN_SELECTED;
|
|
hilight_channel (act, chan, 1);
|
|
}
|
|
|
|
if (conchan)
|
|
select_ipo_key(conchan->ipo, selx, selectmode);
|
|
else
|
|
select_ipo_key(chan->ipo, selx, selectmode);
|
|
|
|
allqueue(REDRAWIPO, 0);
|
|
allqueue(REDRAWVIEW3D, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWNLA, 0);
|
|
|
|
}
|
|
}
|
|
|
|
static void borderselect_action(void)
|
|
{
|
|
rcti rect;
|
|
rctf rectf;
|
|
int val, selectmode;
|
|
short mval[2];
|
|
bActionChannel *chan;
|
|
bConstraintChannel *conchan;
|
|
bAction *act;
|
|
float ymin, ymax;
|
|
|
|
act=G.saction->action;
|
|
|
|
|
|
if (!act)
|
|
return;
|
|
|
|
if ( (val = get_border(&rect, 3)) ){
|
|
if (val == LEFTMOUSE)
|
|
selectmode = SELECT_ADD;
|
|
else
|
|
selectmode = SELECT_SUBTRACT;
|
|
|
|
mval[0]= rect.xmin;
|
|
mval[1]= rect.ymin+2;
|
|
areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin);
|
|
mval[0]= rect.xmax;
|
|
mval[1]= rect.ymax-2;
|
|
areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax);
|
|
|
|
ymax=count_action_levels(act) * (CHANNELHEIGHT+CHANNELSKIP);
|
|
for (chan=act->chanbase.first; chan; chan=chan->next){
|
|
|
|
/* Check action */
|
|
ymin=ymax-(CHANNELHEIGHT+CHANNELSKIP);
|
|
if (!((ymax < rectf.ymin) || (ymin > rectf.ymax)))
|
|
borderselect_ipo_key(chan->ipo, rectf.xmin, rectf.xmax,
|
|
selectmode);
|
|
|
|
ymax=ymin;
|
|
|
|
/* Check constraints */
|
|
for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
|
|
ymin=ymax-(CHANNELHEIGHT+CHANNELSKIP);
|
|
if (!((ymax < rectf.ymin) || (ymin > rectf.ymax)))
|
|
borderselect_ipo_key(conchan->ipo, rectf.xmin, rectf.xmax,
|
|
selectmode);
|
|
|
|
ymax=ymin;
|
|
}
|
|
}
|
|
allqueue(REDRAWNLA, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWIPO, 0);
|
|
}
|
|
}
|
|
|
|
bActionChannel* get_hilighted_action_channel(bAction* action)
|
|
{
|
|
bActionChannel *chan;
|
|
|
|
if (!action)
|
|
return NULL;
|
|
|
|
for (chan=action->chanbase.first; chan; chan=chan->next){
|
|
if (chan->flag & ACHAN_SELECTED && chan->flag & ACHAN_HILIGHTED)
|
|
return chan;
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
void set_exprap_action(int mode)
|
|
{
|
|
if(G.saction->action && G.saction->action->id.lib) return;
|
|
|
|
error ("Not yet implemented!");
|
|
}
|
|
|
|
void free_posebuf(void)
|
|
{
|
|
if (g_posebuf){
|
|
clear_pose(g_posebuf);
|
|
MEM_freeN (g_posebuf);
|
|
}
|
|
g_posebuf=NULL;
|
|
}
|
|
|
|
void copy_posebuf (void)
|
|
{
|
|
Object *ob;
|
|
|
|
free_posebuf();
|
|
|
|
ob=G.obpose;
|
|
if (!ob){
|
|
error ("Copybuf is empty");
|
|
return;
|
|
}
|
|
|
|
filter_pose_keys();
|
|
copy_pose(&g_posebuf, ob->pose, 0);
|
|
|
|
}
|
|
|
|
static void flip_name (char *name)
|
|
{
|
|
|
|
char prefix[128]={""}; /* The part before the facing */
|
|
char suffix[128]={""}; /* The part after the facing */
|
|
char replace[128]={""}; /* The replacement string */
|
|
|
|
char *index=NULL;
|
|
/* Find the last period */
|
|
|
|
strcpy (prefix, name);
|
|
|
|
/* Check for an instance of .Right */
|
|
if (!index){
|
|
index = strstr (prefix, "Right");
|
|
if (index){
|
|
*index=0;
|
|
strcpy (replace, "Left");
|
|
strcpy (suffix, index+6);
|
|
}
|
|
}
|
|
|
|
/* Che ck for an instance of .RIGHT */
|
|
if (!index){
|
|
index = strstr (prefix, "RIGHT");
|
|
if (index){
|
|
*index=0;
|
|
strcpy (replace, "LEFT");
|
|
strcpy (suffix, index+6);
|
|
}
|
|
}
|
|
|
|
|
|
/* Check for an instance of .right */
|
|
if (!index){
|
|
index = strstr (prefix, "right");
|
|
if (index){
|
|
*index=0;
|
|
strcpy (replace, "left");
|
|
strcpy (suffix, index+6);
|
|
}
|
|
}
|
|
|
|
/* Check for an instance of .left */
|
|
if (!index){
|
|
index = strstr (prefix, "left");
|
|
if (index){
|
|
*index=0;
|
|
strcpy (replace, "right");
|
|
strcpy (suffix, index+5);
|
|
}
|
|
}
|
|
|
|
/* Check for an instance of .LEFT */
|
|
if (!index){
|
|
index = strstr (prefix, "LEFT");
|
|
if (index){
|
|
*index=0;
|
|
strcpy (replace, "RIGHT");
|
|
strcpy (suffix, index+5);
|
|
}
|
|
}
|
|
|
|
/* Check for an instance of .Left */
|
|
if (!index){
|
|
index = strstr (prefix, "Left");
|
|
if (index){
|
|
*index=0;
|
|
strcpy (replace, "Right");
|
|
strcpy (suffix, index+5);
|
|
}
|
|
}
|
|
|
|
/* check for an instance of .L */
|
|
if (!index){
|
|
index = strstr (prefix, ".L");
|
|
if (index){
|
|
*index=0;
|
|
strcpy (replace, ".R");
|
|
strcpy (suffix, index+2);
|
|
}
|
|
}
|
|
|
|
/* check for an instance of .l */
|
|
if (!index){
|
|
index = strstr (prefix, ".l");
|
|
if (index){
|
|
*index=0;
|
|
strcpy (replace, ".r");
|
|
strcpy (suffix, index+2);
|
|
}
|
|
}
|
|
|
|
/* Checl for an instance of .R */
|
|
if (!index){
|
|
index = strstr (prefix, ".R");
|
|
if (index){
|
|
*index=0;
|
|
strcpy (replace, ".L");
|
|
strcpy (suffix, index+2);
|
|
}
|
|
}
|
|
|
|
/* Checl for an instance of .r */
|
|
if (!index){
|
|
index = strstr (prefix, ".r");
|
|
if (index){
|
|
*index=0;
|
|
strcpy (replace, ".l");
|
|
strcpy (suffix, index+2);
|
|
}
|
|
}
|
|
|
|
sprintf (name, "%s%s%s", prefix, replace, suffix);
|
|
}
|
|
|
|
void paste_posebuf (int flip){
|
|
Object *ob;
|
|
bPoseChannel *temp, *chan;
|
|
float eul[4];
|
|
Base *base;
|
|
int newchan = 0;
|
|
|
|
ob=G.obpose;
|
|
if (!ob)
|
|
return;
|
|
|
|
if (!g_posebuf){
|
|
error ("Copybuf is empty");
|
|
return;
|
|
};
|
|
|
|
collect_pose_garbage(ob);
|
|
|
|
/* Safely merge all of the channels in this pose into
|
|
any existing pose */
|
|
if (ob->pose){
|
|
if (U.uiflag & KEYINSERTACT){
|
|
/* Display "Avail, all" dialog */
|
|
}
|
|
for (chan=g_posebuf->chanbase.first; chan; chan=chan->next){
|
|
if (chan->flag & POSE_KEY){
|
|
temp = copy_pose_channel (chan);
|
|
if (flip){
|
|
flip_name (temp->name);
|
|
temp->loc[0]*=-1;
|
|
|
|
QuatToEul(temp->quat, eul);
|
|
eul[1]*=-1;
|
|
eul[2]*=-1;
|
|
EulToQuat(eul, temp->quat);
|
|
}
|
|
|
|
temp = set_pose_channel (ob->pose, temp);
|
|
|
|
if (U.uiflag & KEYINSERTACT){
|
|
/* Set keys on pose */
|
|
if (chan->flag & POSE_ROT){
|
|
set_action_key(ob->action, temp, AC_QUAT_X, newchan);
|
|
set_action_key(ob->action, temp, AC_QUAT_Y, newchan);
|
|
set_action_key(ob->action, temp, AC_QUAT_Z, newchan);
|
|
set_action_key(ob->action, temp, AC_QUAT_W, newchan);
|
|
};
|
|
if (chan->flag & POSE_SIZE){
|
|
set_action_key(ob->action, temp, AC_SIZE_X, newchan);
|
|
set_action_key(ob->action, temp, AC_SIZE_Y, newchan);
|
|
set_action_key(ob->action, temp, AC_SIZE_Z, newchan);
|
|
};
|
|
if (chan->flag & POSE_LOC){
|
|
set_action_key(ob->action, temp, AC_LOC_X, newchan);
|
|
set_action_key(ob->action, temp, AC_LOC_Y, newchan);
|
|
set_action_key(ob->action, temp, AC_LOC_Z, newchan);
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
if (U.uiflag & KEYINSERTACT){
|
|
remake_action_ipos(ob->action);
|
|
allqueue (REDRAWIPO, 0);
|
|
allqueue (REDRAWVIEW3D, 0);
|
|
allqueue (REDRAWACTION, 0);
|
|
allqueue(REDRAWNLA, 0);
|
|
}
|
|
|
|
/* Update deformation children */
|
|
if (G.obpose->type == OB_ARMATURE){
|
|
for (base= FIRSTBASE; base; base= base->next){
|
|
if (G.obpose==base->object->parent){
|
|
if (base->object->partype==PARSKEL)
|
|
makeDispList(base->object);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void set_action_key (struct bAction *act, struct bPoseChannel *chan, int adrcode, short makecurve)
|
|
{
|
|
set_action_key_time (act, chan, adrcode, makecurve, frame_to_float(CFRA));
|
|
}
|
|
|
|
static void set_action_key_time (bAction *act, bPoseChannel *chan, int adrcode, short makecurve, float time)
|
|
{
|
|
bActionChannel *achan;
|
|
char ipstr[256];
|
|
|
|
if (!act)
|
|
return;
|
|
|
|
if (!chan)
|
|
return;
|
|
/* See if this action channel exists already */
|
|
for (achan=act->chanbase.first; achan; achan=achan->next){
|
|
if (!strcmp (chan->name, achan->name))
|
|
break;
|
|
}
|
|
|
|
if (!achan){
|
|
if (!makecurve)
|
|
return;
|
|
achan = MEM_callocN (sizeof(bActionChannel), "actionChannel");
|
|
strcpy (achan->name, chan->name);
|
|
BLI_addtail (&act->chanbase, achan);
|
|
}
|
|
|
|
/* Ensure the channel appears selected in the action window */
|
|
achan->flag |= ACHAN_SELECTED;
|
|
|
|
/* Ensure this action channel has a valid Ipo */
|
|
if (!achan->ipo){
|
|
sprintf (ipstr, "%s.%s", act->id.name+2, chan->name);
|
|
ipstr[23]=0;
|
|
achan->ipo= add_ipo(ipstr, ID_AC);
|
|
}
|
|
|
|
insertactionkey(act, achan, chan, adrcode, makecurve, time);
|
|
|
|
}
|
|
|
|
static void insertactionkey(bAction *act, bActionChannel *achan, bPoseChannel *chan, int adrcode, short makecurve, float cfra)
|
|
{
|
|
IpoCurve *icu;
|
|
void *poin;
|
|
float curval;
|
|
int type;
|
|
ID *id;
|
|
|
|
if (!act){
|
|
return;
|
|
}
|
|
if (act->id.lib){
|
|
error ("Can't pose libactions");
|
|
return;
|
|
}
|
|
act->achan=achan;
|
|
act->pchan=chan;
|
|
|
|
id=(ID*) act;
|
|
|
|
/* First see if this curve exists */
|
|
if (!makecurve){
|
|
if (!achan->ipo)
|
|
return;
|
|
|
|
for (icu = achan->ipo->curve.first; icu; icu=icu->next){
|
|
if (icu->adrcode == adrcode)
|
|
break;
|
|
}
|
|
if (!icu)
|
|
return;
|
|
}
|
|
|
|
|
|
icu = get_ipocurve (id, GS(id->name), adrcode, achan->ipo);
|
|
|
|
if(icu) {
|
|
poin= get_ipo_poin(id, icu, &type);
|
|
if(poin) {
|
|
curval= read_ipo_poin(poin, type);
|
|
// cfra= frame_to_float(CFRA);
|
|
insert_vert_ipo(icu, cfra, curval);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
bAction *add_empty_action(void)
|
|
{
|
|
bAction *act;
|
|
|
|
act= alloc_libblock(&G.main->action, ID_AC, "Action");
|
|
act->id.flag |= LIB_FAKEUSER;
|
|
act->id.us++;
|
|
return act;
|
|
}
|
|
|
|
static void transform_actionchannel_keys(char mode)
|
|
{
|
|
bAction *act;
|
|
TransVert *tv;
|
|
int /*sel=0,*/ i;
|
|
bActionChannel *chan;
|
|
short mvals[2], mvalc[2], cent[2];
|
|
float sval[2], cval[2], lastcval[2];
|
|
short cancel=0;
|
|
float fac=0.0F;
|
|
int loop=1;
|
|
int tvtot=0;
|
|
float deltax, startx;
|
|
float cenf[2];
|
|
int invert=0, firsttime=1;
|
|
char str[256];
|
|
bConstraintChannel *conchan;
|
|
|
|
act=G.saction->action;
|
|
|
|
/* Ensure that partial selections result in beztriple selections */
|
|
for (chan=act->chanbase.first; chan; chan=chan->next){
|
|
tvtot+=fullselect_ipo_keys(chan->ipo);
|
|
|
|
for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
|
|
tvtot+=fullselect_ipo_keys(conchan->ipo);
|
|
}
|
|
|
|
/* If nothing is selected, bail out */
|
|
if (!tvtot)
|
|
return;
|
|
|
|
|
|
/* Build the transvert structure */
|
|
tv = MEM_callocN (sizeof(TransVert) * tvtot, "transVert");
|
|
tvtot=0;
|
|
for (chan=act->chanbase.first; chan; chan=chan->next){
|
|
/* 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);
|
|
}
|
|
|
|
/* Do the event loop */
|
|
cent[0] = curarea->winx + (G.saction->v2d.hor.xmax)/2;
|
|
cent[1] = curarea->winy + (G.saction->v2d.hor.ymax)/2;
|
|
areamouseco_to_ipoco(G.v2d, cent, &cenf[0], &cenf[1]);
|
|
|
|
getmouseco_areawin (mvals);
|
|
areamouseco_to_ipoco(G.v2d, mvals, &sval[0], &sval[1]);
|
|
|
|
startx=sval[0];
|
|
while (loop) {
|
|
/* Get the input */
|
|
/* If we're cancelling, reset transformations */
|
|
/* Else calc new transformation */
|
|
/* Perform the transformations */
|
|
while (qtest()) {
|
|
short val;
|
|
unsigned short event= extern_qread(&val);
|
|
|
|
if (val) {
|
|
switch (event) {
|
|
case LEFTMOUSE:
|
|
case SPACEKEY:
|
|
case RETKEY:
|
|
loop=0;
|
|
break;
|
|
case XKEY:
|
|
break;
|
|
case ESCKEY:
|
|
case RIGHTMOUSE:
|
|
cancel=1;
|
|
loop=0;
|
|
break;
|
|
default:
|
|
arrows_move_cursor(event);
|
|
break;
|
|
};
|
|
}
|
|
}
|
|
|
|
if (cancel) {
|
|
for (i=0; i<tvtot; i++) {
|
|
tv[i].loc[0]=tv[i].oldloc[0];
|
|
tv[i].loc[1]=tv[i].oldloc[1];
|
|
}
|
|
} else {
|
|
getmouseco_areawin (mvalc);
|
|
areamouseco_to_ipoco(G.v2d, mvalc, &cval[0], &cval[1]);
|
|
|
|
if (!firsttime && lastcval[0]==cval[0] && lastcval[1]==cval[1]) {
|
|
PIL_sleep_ms(1);
|
|
} else {
|
|
for (i=0; i<tvtot; i++){
|
|
tv[i].loc[0]=tv[i].oldloc[0];
|
|
|
|
switch (mode){
|
|
case 'g':
|
|
deltax = cval[0]-sval[0];
|
|
fac= deltax;
|
|
|
|
apply_keyb_grid(&fac, 0.0, 1.0, 0.1, U.flag & AUTOGRABGRID);
|
|
|
|
tv[i].loc[0]+=fac;
|
|
break;
|
|
case 's':
|
|
startx=mvals[0]-(ACTWIDTH/2+(curarea->winrct.xmax-curarea->winrct.xmin)/2);
|
|
deltax=mvalc[0]-(ACTWIDTH/2+(curarea->winrct.xmax-curarea->winrct.xmin)/2);
|
|
fac= fabs(deltax/startx);
|
|
|
|
apply_keyb_grid(&fac, 0.0, 0.2, 0.1, U.flag & AUTOSIZEGRID);
|
|
|
|
if (invert){
|
|
if (i % 03 == 0){
|
|
memcpy (tv[i].loc, tv[i].oldloc, sizeof(tv[i+2].oldloc));
|
|
}
|
|
if (i % 03 == 2){
|
|
memcpy (tv[i].loc, tv[i].oldloc, sizeof(tv[i-2].oldloc));
|
|
}
|
|
|
|
fac*=-1;
|
|
}
|
|
startx= (G.scene->r.cfra);
|
|
|
|
tv[i].loc[0]-= startx;
|
|
tv[i].loc[0]*=fac;
|
|
tv[i].loc[0]+= startx;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mode=='s'){
|
|
sprintf(str, "sizeX: %.3f", fac);
|
|
headerprint(str);
|
|
}
|
|
else if (mode=='g'){
|
|
sprintf(str, "deltaX: %.3f", fac);
|
|
headerprint(str);
|
|
}
|
|
|
|
if (G.saction->lock){
|
|
do_all_actions();
|
|
allqueue (REDRAWVIEW3D, 0);
|
|
allqueue (REDRAWACTION, 0);
|
|
allqueue (REDRAWIPO, 0);
|
|
allqueue(REDRAWNLA, 0);
|
|
force_draw_all();
|
|
}
|
|
else {
|
|
addqueue (curarea->win, REDRAWALL, 0);
|
|
force_draw ();
|
|
}
|
|
}
|
|
|
|
lastcval[0]= cval[0];
|
|
lastcval[1]= cval[1];
|
|
firsttime= 0;
|
|
}
|
|
|
|
/* Update the curve */
|
|
/* Depending on the lock status, draw necessary views */
|
|
|
|
do_all_actions();
|
|
remake_action_ipos(act);
|
|
allqueue (REDRAWVIEW3D, 0);
|
|
allqueue (REDRAWACTION, 0);
|
|
allqueue(REDRAWNLA, 0);
|
|
allqueue (REDRAWIPO, 0);
|
|
MEM_freeN (tv);
|
|
}
|
|
|
|
void deselect_actionchannel_keys (bAction *act, int test)
|
|
{
|
|
bActionChannel *chan;
|
|
bConstraintChannel *conchan;
|
|
int sel=1;;
|
|
|
|
if (!act)
|
|
return;
|
|
|
|
/* Determine if this is selection or deselection */
|
|
|
|
if (test){
|
|
for (chan=act->chanbase.first; chan; chan=chan->next){
|
|
/* Test the channel ipos */
|
|
if (is_ipo_key_selected(chan->ipo)){
|
|
sel = 0;
|
|
break;
|
|
}
|
|
|
|
/* Test the constraint ipos */
|
|
for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
|
|
if (is_ipo_key_selected(conchan->ipo)){
|
|
sel = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (sel == 0)
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
sel=0;
|
|
|
|
/* Set the flags */
|
|
for (chan=act->chanbase.first; chan; chan=chan->next){
|
|
set_ipo_key_selection(chan->ipo, sel);
|
|
for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
|
|
set_ipo_key_selection(conchan->ipo, sel);
|
|
}
|
|
}
|
|
|
|
void deselect_actionchannels (bAction *act, int test)
|
|
{
|
|
bActionChannel *chan;
|
|
bConstraintChannel *conchan;
|
|
int sel=1;
|
|
|
|
if (!act)
|
|
return;
|
|
|
|
/* See if we should be selecting or deselecting */
|
|
if (test){
|
|
for (chan=act->chanbase.first; chan; chan=chan->next){
|
|
if (!sel)
|
|
break;
|
|
|
|
if (chan->flag & ACHAN_SELECTED){
|
|
sel=0;
|
|
break;
|
|
}
|
|
if (sel){
|
|
for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
|
|
if (conchan->flag & CONSTRAINT_CHANNEL_SELECT){
|
|
sel=0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
sel=0;
|
|
|
|
/* Now set the flags */
|
|
for (chan=act->chanbase.first; chan; chan=chan->next){
|
|
select_poseelement_by_name(chan->name, sel);
|
|
|
|
if (sel)
|
|
chan->flag |= ACHAN_SELECTED;
|
|
else
|
|
chan->flag &= ~ACHAN_SELECTED;
|
|
|
|
for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
|
|
if (sel)
|
|
conchan->flag |= CONSTRAINT_CHANNEL_SELECT;
|
|
else
|
|
conchan->flag &= ~CONSTRAINT_CHANNEL_SELECT;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
static void hilight_channel (bAction *act, bActionChannel *chan, short select)
|
|
{
|
|
bActionChannel *curchan;
|
|
|
|
if (!act)
|
|
return;
|
|
|
|
for (curchan=act->chanbase.first; curchan; curchan=curchan->next){
|
|
if (curchan==chan && select)
|
|
curchan->flag |= ACHAN_HILIGHTED;
|
|
else
|
|
curchan->flag &= ~ACHAN_HILIGHTED;
|
|
}
|
|
}
|
|
|
|
static int select_channel(bAction *act, bActionChannel *chan,
|
|
int selectmode) {
|
|
/* Select the channel based on the selection mode
|
|
*/
|
|
int flag;
|
|
|
|
switch (selectmode) {
|
|
case SELECT_ADD:
|
|
chan->flag |= ACHAN_SELECTED;
|
|
break;
|
|
case SELECT_SUBTRACT:
|
|
chan->flag &= ~ACHAN_SELECTED;
|
|
break;
|
|
case SELECT_INVERT:
|
|
chan->flag ^= ACHAN_SELECTED;
|
|
break;
|
|
}
|
|
flag = (chan->flag & ACHAN_SELECTED) ? 1 : 0;
|
|
|
|
hilight_channel(act, chan, flag);
|
|
select_poseelement_by_name(chan->name, flag);
|
|
|
|
return flag;
|
|
}
|
|
|
|
static int select_constraint_channel(bAction *act,
|
|
bConstraintChannel *conchan,
|
|
int selectmode) {
|
|
/* Select the constraint channel based on the selection mode
|
|
*/
|
|
int flag;
|
|
|
|
switch (selectmode) {
|
|
case SELECT_ADD:
|
|
conchan->flag |= CONSTRAINT_CHANNEL_SELECT;
|
|
break;
|
|
case SELECT_SUBTRACT:
|
|
conchan->flag &= ~CONSTRAINT_CHANNEL_SELECT;
|
|
break;
|
|
case SELECT_INVERT:
|
|
conchan->flag ^= CONSTRAINT_CHANNEL_SELECT;
|
|
break;
|
|
}
|
|
flag = (conchan->flag & CONSTRAINT_CHANNEL_SELECT) ? 1 : 0;
|
|
|
|
return flag;
|
|
}
|
|
|
|
|
|
static void mouse_actionchannels(bAction *act, short *mval,
|
|
short *mvalo, int selectmode) {
|
|
/* Select action channels, based on mouse values.
|
|
* If mvalo is NULL we assume it is a one click
|
|
* action, other wise we treat it like it is a
|
|
* border select with mval[0],mval[1] and
|
|
* mvalo[0], mvalo[1] forming the corners of
|
|
* a rectangle.
|
|
*/
|
|
bActionChannel *chan;
|
|
float click;
|
|
int clickmin, clickmax;
|
|
int wsize, sel;
|
|
bConstraintChannel *conchan;
|
|
|
|
if (!act)
|
|
return;
|
|
|
|
if (selectmode == SELECT_REPLACE) {
|
|
deselect_actionchannels (act, 0);
|
|
selectmode = SELECT_ADD;
|
|
}
|
|
|
|
/* 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));
|
|
|
|
click = (wsize-(mval[1]+G.v2d->cur.ymin));
|
|
click += CHANNELHEIGHT/2;
|
|
click /= (CHANNELHEIGHT+CHANNELSKIP);
|
|
|
|
clickmin = (int) click;
|
|
|
|
/* Only one click */
|
|
if (mvalo == NULL) {
|
|
clickmax = clickmin;
|
|
}
|
|
/* Two click values (i.e., border select */
|
|
else {
|
|
click = (wsize-(mvalo[1]+G.v2d->cur.ymin));
|
|
click += CHANNELHEIGHT/2;
|
|
click /= (CHANNELHEIGHT+CHANNELSKIP);
|
|
|
|
if ( ((int) click) < clickmin) {
|
|
clickmax = clickmin;
|
|
clickmin = (int) click;
|
|
}
|
|
else {
|
|
clickmax = (int) click;
|
|
}
|
|
}
|
|
|
|
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 apply the selection mode on all
|
|
* 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 selecting stuff,
|
|
* until clickmax is less than zero or we run out of channels
|
|
* and constraint channels.
|
|
*/
|
|
|
|
for (chan = act->chanbase.first; chan; chan=chan->next){
|
|
if (clickmax < 0) break;
|
|
|
|
if ( clickmin <= 0) {
|
|
/* Select the channel with the given mode. If the
|
|
* channel is freshly selected then set it to the
|
|
* active channel for the action
|
|
*/
|
|
sel = (chan->flag & ACHAN_SELECTED);
|
|
if ( select_channel(act, chan, selectmode) && !sel ) {
|
|
act->achan = chan;
|
|
}
|
|
}
|
|
--clickmin;
|
|
--clickmax;
|
|
|
|
/* Check for click in a constraint */
|
|
for (conchan=chan->constraintChannels.first;
|
|
conchan; conchan=conchan->next){
|
|
if (clickmax < 0) break;
|
|
if ( clickmin <= 0) {
|
|
select_constraint_channel(act, conchan, selectmode);
|
|
}
|
|
--clickmin;
|
|
--clickmax;
|
|
}
|
|
}
|
|
|
|
allqueue (REDRAWIPO, 0);
|
|
allqueue (REDRAWVIEW3D, 0);
|
|
allqueue (REDRAWACTION, 0);
|
|
allqueue (REDRAWNLA, 0);
|
|
}
|
|
|
|
|
|
static void delete_actionchannel_keys(void)
|
|
{
|
|
bAction *act;
|
|
bActionChannel *chan;
|
|
bConstraintChannel *conchan;
|
|
|
|
act = G.saction->action;
|
|
if (!act)
|
|
return;
|
|
|
|
if (!okee("Erase selected keys"))
|
|
return;
|
|
|
|
for (chan = act->chanbase.first; chan; chan=chan->next){
|
|
|
|
/* Check action channel keys*/
|
|
delete_ipo_keys(chan->ipo);
|
|
|
|
/* Delete constraint channel keys */
|
|
for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
|
|
delete_ipo_keys(conchan->ipo);
|
|
}
|
|
|
|
remake_action_ipos (act);
|
|
allspace(REMAKEIPO, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWIPO, 0);
|
|
allqueue(REDRAWNLA, 0);
|
|
|
|
}
|
|
static void delete_actionchannels (void)
|
|
{
|
|
bConstraintChannel *conchan, *nextconchan;
|
|
bActionChannel *chan, *next;
|
|
bAction *act;
|
|
int freechan;
|
|
|
|
act=G.saction->action;
|
|
|
|
if (!act)
|
|
return;
|
|
|
|
for (chan=act->chanbase.first; chan; chan=chan->next){
|
|
if (chan->flag & ACHAN_SELECTED)
|
|
break;
|
|
for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
|
|
{
|
|
if (conchan->flag & CONSTRAINT_CHANNEL_SELECT){
|
|
chan=act->chanbase.last;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!chan && !conchan)
|
|
return;
|
|
|
|
if (!okee("Erase selected channels"))
|
|
return;
|
|
|
|
for (chan=act->chanbase.first; chan; chan=next){
|
|
freechan = 0;
|
|
next=chan->next;
|
|
|
|
/* Remove action channels */
|
|
if (chan->flag & ACHAN_SELECTED){
|
|
if (chan->ipo)
|
|
chan->ipo->id.us--; /* Release the ipo */
|
|
freechan = 1;
|
|
}
|
|
|
|
/* Remove constraint channels */
|
|
for (conchan=chan->constraintChannels.first; conchan; conchan=nextconchan){
|
|
nextconchan=conchan->next;
|
|
if (freechan || conchan->flag & CONSTRAINT_CHANNEL_SELECT){
|
|
if (conchan->ipo)
|
|
conchan->ipo->id.us--;
|
|
BLI_freelinkN(&chan->constraintChannels, conchan);
|
|
}
|
|
}
|
|
|
|
if (freechan)
|
|
BLI_freelinkN (&act->chanbase, chan);
|
|
|
|
}
|
|
|
|
allqueue (REDRAWACTION, 0);
|
|
allqueue(REDRAWNLA, 0);
|
|
|
|
}
|
|
|
|
static void sethandles_actionchannel_keys(int code)
|
|
{
|
|
bAction *act;
|
|
bActionChannel *chan;
|
|
|
|
/* Get the selected action, exit if none are selected
|
|
*/
|
|
act = G.saction->action;
|
|
if (!act)
|
|
return;
|
|
|
|
/* 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){
|
|
sethandles_ipo_keys(chan->ipo, code);
|
|
}
|
|
|
|
/* Clean up and redraw stuff
|
|
*/
|
|
remake_action_ipos (act);
|
|
allspace(REMAKEIPO, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWIPO, 0);
|
|
allqueue(REDRAWNLA, 0);
|
|
}
|
|
|
|
static void set_ipotype_actionchannels(void) {
|
|
|
|
bAction *act;
|
|
bActionChannel *chan;
|
|
short event;
|
|
|
|
/* Get the selected action, exit if none are selected
|
|
*/
|
|
act = G.saction->action;
|
|
if (!act)
|
|
return;
|
|
|
|
/* Present a popup menu asking the user what type
|
|
* of IPO curve he/she/GreenBTH wants. ;)
|
|
*/
|
|
event= pupmenu("Channel Ipo Type %t|Constant %x1|Linear %x2|Bezier %x3");
|
|
if(event < 1) return;
|
|
|
|
/* Loop through the channels and for the selected ones set
|
|
* 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_SELECTED){
|
|
if (chan->ipo)
|
|
setipotype_ipo(chan->ipo, event);
|
|
}
|
|
}
|
|
|
|
/* Clean up and redraw stuff
|
|
*/
|
|
remake_action_ipos (act);
|
|
allspace(REMAKEIPO, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWIPO, 0);
|
|
allqueue(REDRAWNLA, 0);
|
|
}
|
|
|
|
void select_all_keys_frames(bAction *act, short *mval,
|
|
short *mvalo, int selectmode) {
|
|
|
|
/* This function tries to select all action keys in
|
|
* every channel for a given range of keyframes that
|
|
* are within the mouse values mval and mvalo (usually
|
|
* the result of a border select). If mvalo is passed as
|
|
* NULL then the selection is treated as a one-click and
|
|
* the function tries to select all keys within half a
|
|
* frame of the click point.
|
|
*/
|
|
|
|
rcti rect;
|
|
rctf rectf;
|
|
bActionChannel *chan;
|
|
bConstraintChannel *conchan;
|
|
|
|
if (!act)
|
|
return;
|
|
|
|
if (selectmode == SELECT_REPLACE) {
|
|
deselect_actionchannel_keys(act, 0);
|
|
selectmode = SELECT_ADD;
|
|
}
|
|
|
|
if (mvalo == NULL) {
|
|
rect.xmin = rect.xmax = mval[0];
|
|
rect.ymin = rect.ymax = mval[1];
|
|
}
|
|
else {
|
|
if (mval[0] < mvalo[0] ) {
|
|
rect.xmin = mval[0];
|
|
rect.xmax = mvalo[0];
|
|
}
|
|
else {
|
|
rect.xmin = mvalo[0];
|
|
rect.xmax = mval[0];
|
|
}
|
|
if (mval[1] < mvalo[1] ) {
|
|
rect.ymin = mval[1];
|
|
rect.ymax = mvalo[1];
|
|
}
|
|
else {
|
|
rect.ymin = mvalo[1];
|
|
rect.ymax = mval[1];
|
|
}
|
|
}
|
|
|
|
mval[0]= rect.xmin;
|
|
mval[1]= rect.ymin+2;
|
|
areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin);
|
|
mval[0]= rect.xmax;
|
|
mval[1]= rect.ymax-2;
|
|
areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax);
|
|
|
|
if (mvalo == NULL) {
|
|
rectf.xmin = rectf.xmin - 0.5;
|
|
rectf.xmax = rectf.xmax + 0.5;
|
|
}
|
|
|
|
for (chan=act->chanbase.first; chan; chan=chan->next){
|
|
borderselect_ipo_key(chan->ipo, rectf.xmin, rectf.xmax,
|
|
selectmode);
|
|
for (conchan=chan->constraintChannels.first; conchan;
|
|
conchan=conchan->next){
|
|
borderselect_ipo_key(conchan->ipo, rectf.xmin, rectf.xmax,
|
|
selectmode);
|
|
}
|
|
}
|
|
allqueue(REDRAWNLA, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWIPO, 0);
|
|
}
|
|
|
|
|
|
void select_all_keys_channels(bAction *act, short *mval,
|
|
short *mvalo, int selectmode) {
|
|
bActionChannel *chan;
|
|
float click;
|
|
int clickmin, clickmax;
|
|
int wsize;
|
|
bConstraintChannel *conchan;
|
|
|
|
/* This function selects all the action keys that
|
|
* are in the mouse selection range defined by
|
|
* the ordered pairs mval and mvalo (usually
|
|
* these 2 are obtained from a border select).
|
|
* If mvalo is NULL, then the selection is
|
|
* treated like a one-click action, and at most
|
|
* one channel is selected.
|
|
*/
|
|
|
|
/* If the action is null then abort
|
|
*/
|
|
if (!act)
|
|
return;
|
|
|
|
if (selectmode == SELECT_REPLACE) {
|
|
deselect_actionchannel_keys(act, 0);
|
|
selectmode = SELECT_ADD;
|
|
}
|
|
|
|
/* 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));
|
|
|
|
click = (wsize-(mval[1]+G.v2d->cur.ymin));
|
|
click += CHANNELHEIGHT/2;
|
|
click /= (CHANNELHEIGHT+CHANNELSKIP);
|
|
|
|
clickmin = (int) click;
|
|
|
|
/* Only one click */
|
|
if (mvalo == NULL) {
|
|
clickmax = clickmin;
|
|
}
|
|
/* Two click values (i.e., border select) */
|
|
else {
|
|
click = (wsize-(mvalo[1]+G.v2d->cur.ymin));
|
|
click += CHANNELHEIGHT/2;
|
|
click /= (CHANNELHEIGHT+CHANNELSKIP);
|
|
|
|
if ( ((int) click) < clickmin) {
|
|
clickmax = clickmin;
|
|
clickmin = (int) click;
|
|
}
|
|
else {
|
|
clickmax = (int) click;
|
|
}
|
|
}
|
|
|
|
if (clickmax < 0) {
|
|
return;
|
|
}
|
|
|
|
for (chan = act->chanbase.first; chan; chan=chan->next){
|
|
if (clickmax < 0) break;
|
|
|
|
if ( clickmin <= 0) {
|
|
/* Select the channel with the given mode. If the
|
|
* channel is freshly selected then set it to the
|
|
* active channel for the action
|
|
*/
|
|
select_ipo_bezier_keys(chan->ipo, selectmode);
|
|
}
|
|
--clickmin;
|
|
--clickmax;
|
|
|
|
/* Check for click in a constraint */
|
|
for (conchan=chan->constraintChannels.first;
|
|
conchan; conchan=conchan->next){
|
|
if (clickmax < 0) break;
|
|
if ( clickmin <= 0) {
|
|
select_ipo_bezier_keys(chan->ipo, selectmode);
|
|
}
|
|
--clickmin;
|
|
--clickmax;
|
|
}
|
|
}
|
|
|
|
allqueue (REDRAWIPO, 0);
|
|
allqueue (REDRAWVIEW3D, 0);
|
|
allqueue (REDRAWACTION, 0);
|
|
allqueue (REDRAWNLA, 0);
|
|
|
|
}
|
|
|
|
static void borderselect_function(void (*select_func)(bAction *act,
|
|
short *mval,
|
|
short *mvalo,
|
|
int selectmode)) {
|
|
/* This function executes an arbitrary selection
|
|
* function as part of a border select. This
|
|
* way the same function that is used for
|
|
* right click selection points can generally
|
|
* be used as the argument to this function
|
|
*/
|
|
rcti rect;
|
|
short mval[2], mvalo[2];
|
|
bAction *act;
|
|
int val;
|
|
|
|
/* Get the selected action, exit if none are selected
|
|
*/
|
|
act=G.saction->action;
|
|
if (!act)
|
|
return;
|
|
|
|
/* Let the user draw a border (or abort)
|
|
*/
|
|
if ( (val=get_border (&rect, 3)) ) {
|
|
mval[0]= rect.xmin;
|
|
mval[1]= rect.ymin+2;
|
|
mvalo[0]= rect.xmax;
|
|
mvalo[1]= rect.ymax-2;
|
|
|
|
/* if the left mouse was used, do an additive
|
|
* selection with the user defined selection
|
|
* function.
|
|
*/
|
|
if (val == LEFTMOUSE)
|
|
select_func(act, mval, mvalo, SELECT_ADD);
|
|
|
|
/* if the right mouse was used, do a subtractive
|
|
* selection with the user defined selection
|
|
* function.
|
|
*/
|
|
else if (val == RIGHTMOUSE)
|
|
select_func(act, mval, mvalo, SELECT_SUBTRACT);
|
|
}
|
|
|
|
}
|
|
|
|
void winqreadactionspace(unsigned short event, short val, char ascii)
|
|
{
|
|
SpaceAction *saction;
|
|
bAction *act;
|
|
int doredraw= 0;
|
|
short mval[2];
|
|
float dx,dy;
|
|
int cfra;
|
|
|
|
if(curarea->win==0) return;
|
|
|
|
saction= curarea->spacedata.first;
|
|
if (!saction)
|
|
return;
|
|
|
|
act=saction->action;
|
|
if(val) {
|
|
|
|
if( uiDoBlocks(&curarea->uiblocks, event)!=UI_NOTHING ) event= 0;
|
|
|
|
getmouseco_areawin(mval);
|
|
|
|
switch(event) {
|
|
case UI_BUT_EVENT:
|
|
do_blenderbuttons(val);
|
|
break;
|
|
case HOMEKEY:
|
|
do_action_buttons(B_ACTHOME);
|
|
break;
|
|
|
|
case CKEY:
|
|
/* scroll the window so the current
|
|
* frame is in the center.
|
|
*/
|
|
center_currframe();
|
|
break;
|
|
|
|
case DKEY:
|
|
if (G.qual & LR_SHIFTKEY && mval[0]>ACTWIDTH){
|
|
duplicate_actionchannel_keys();
|
|
remake_action_ipos(act);
|
|
}
|
|
break;
|
|
case DELKEY:
|
|
case XKEY:
|
|
if (mval[0]<ACTWIDTH)
|
|
delete_actionchannels ();
|
|
else
|
|
delete_actionchannel_keys ();
|
|
break;
|
|
case GKEY:
|
|
if (mval[0]>=ACTWIDTH)
|
|
transform_actionchannel_keys ('g');
|
|
break;
|
|
case SKEY:
|
|
if (mval[0]>=ACTWIDTH)
|
|
transform_actionchannel_keys ('s');
|
|
break;
|
|
case AKEY:
|
|
if (mval[0]<ACTWIDTH){
|
|
deselect_actionchannels (act, 1);
|
|
allqueue (REDRAWVIEW3D, 0);
|
|
allqueue (REDRAWACTION, 0);
|
|
allqueue(REDRAWNLA, 0);
|
|
allqueue (REDRAWIPO, 0);
|
|
}
|
|
else{
|
|
deselect_actionchannel_keys (act, 1);
|
|
allqueue (REDRAWACTION, 0);
|
|
allqueue(REDRAWNLA, 0);
|
|
allqueue (REDRAWIPO, 0);
|
|
}
|
|
break;
|
|
|
|
/*** set the Ipo handles ***/
|
|
case VKEY:
|
|
sethandles_actionchannel_keys(HD_VECT);
|
|
break;
|
|
case HKEY:
|
|
if(G.qual & LR_SHIFTKEY) sethandles_actionchannel_keys(HD_AUTO);
|
|
else sethandles_actionchannel_keys(HD_ALIGN);
|
|
break;
|
|
|
|
/*** set the Ipo type ***/
|
|
case TKEY:
|
|
set_ipotype_actionchannels();
|
|
break;
|
|
|
|
case BKEY:
|
|
/* If the border select is initiated in the
|
|
* part of the action window where the channel
|
|
* names reside, then select the channels
|
|
*/
|
|
if (mval[0]<ACTWIDTH){
|
|
borderselect_function(mouse_actionchannels);
|
|
}
|
|
|
|
/* If the border select is initiated in the
|
|
* vertical scrollbar, then (de)select all keys
|
|
* for the channels in the selection region
|
|
*/
|
|
else if (IN_2D_VERT_SCROLL(mval)) {
|
|
borderselect_function(select_all_keys_channels);
|
|
}
|
|
|
|
/* If the border select is initiated in the
|
|
* horizontal scrollbar, then (de)select all keys
|
|
* for the keyframes in the selection region
|
|
*/
|
|
else if (IN_2D_HORIZ_SCROLL(mval)) {
|
|
borderselect_function(select_all_keys_frames);
|
|
}
|
|
|
|
/* Other wise, select the action keys
|
|
*/
|
|
else {
|
|
borderselect_action();
|
|
}
|
|
break;
|
|
case RIGHTMOUSE:
|
|
/* Right clicking in the channel area selects the
|
|
* channel or constraint channel
|
|
*/
|
|
if (mval[0]<ACTWIDTH) {
|
|
if(G.qual & LR_SHIFTKEY)
|
|
mouse_actionchannels(act, mval, NULL,
|
|
SELECT_INVERT);
|
|
else
|
|
mouse_actionchannels(act, mval, NULL,
|
|
SELECT_REPLACE);
|
|
}
|
|
|
|
/* Right clicking in the vertical scrollbar selects
|
|
* all of the keys for that channel at that height
|
|
*/
|
|
else if (IN_2D_VERT_SCROLL(mval)) {
|
|
if(G.qual & LR_SHIFTKEY)
|
|
select_all_keys_channels(act, mval, NULL,
|
|
SELECT_INVERT);
|
|
else
|
|
select_all_keys_channels(act, mval, NULL,
|
|
SELECT_REPLACE);
|
|
}
|
|
|
|
/* Right clicking in the horizontal scrollbar selects
|
|
* all of the keys within 0.5 of the nearest integer
|
|
* frame
|
|
*/
|
|
else if (IN_2D_HORIZ_SCROLL(mval)) {
|
|
if(G.qual & LR_SHIFTKEY)
|
|
select_all_keys_frames(act, mval, NULL,
|
|
SELECT_INVERT);
|
|
else
|
|
select_all_keys_frames(act, mval, NULL,
|
|
SELECT_REPLACE);
|
|
}
|
|
|
|
/* Clicking in the main area of the action window
|
|
* selects keys
|
|
*/
|
|
else {
|
|
if(G.qual & LR_SHIFTKEY)
|
|
mouse_action(SELECT_INVERT);
|
|
else
|
|
mouse_action(SELECT_REPLACE);
|
|
}
|
|
break;
|
|
|
|
case LEFTMOUSE:
|
|
if (mval[0]>ACTWIDTH){
|
|
do {
|
|
getmouseco_areawin(mval);
|
|
|
|
areamouseco_to_ipoco(G.v2d, mval, &dx, &dy);
|
|
|
|
cfra= (int)dx;
|
|
if(cfra< 1) cfra= 1;
|
|
|
|
if( cfra!=CFRA ) {
|
|
CFRA= cfra;
|
|
update_for_newframe();
|
|
force_draw_plus(SPACE_VIEW3D);
|
|
force_draw_plus(SPACE_IPO);
|
|
force_draw_plus(SPACE_BUTS);
|
|
}
|
|
|
|
} while(get_mbut()&L_MOUSE);
|
|
}
|
|
|
|
break;
|
|
case MIDDLEMOUSE:
|
|
case WHEELUPMOUSE:
|
|
case WHEELDOWNMOUSE:
|
|
view2dmove(event); /* in drawipo.c */
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(doredraw) addqueue(curarea->win, REDRAW, 1);
|
|
|
|
}
|
|
|