5245 lines
133 KiB
C
5245 lines
133 KiB
C
/**
|
|
* $Id$
|
|
*
|
|
* ***** BEGIN GPL 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.
|
|
*
|
|
* 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): 2007, Joshua Leung (major rewrite of Action Editor)
|
|
*
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <stddef.h>
|
|
#include <math.h>
|
|
|
|
#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_space_types.h"
|
|
#include "DNA_userdef_types.h"
|
|
#include "DNA_constraint_types.h"
|
|
#include "DNA_key_types.h"
|
|
#include "DNA_mesh_types.h"
|
|
#include "DNA_nla_types.h"
|
|
#include "DNA_lattice_types.h"
|
|
#include "DNA_gpencil_types.h"
|
|
|
|
#include "BKE_action.h"
|
|
#include "BKE_armature.h"
|
|
#include "BKE_constraint.h"
|
|
#include "BKE_curve.h"
|
|
#include "BKE_depsgraph.h"
|
|
#include "BKE_global.h"
|
|
#include "BKE_ipo.h"
|
|
#include "BKE_key.h"
|
|
#include "BKE_library.h"
|
|
#include "BKE_main.h"
|
|
#include "BKE_utildefines.h"
|
|
#include "BKE_object.h" /* for where_is_object in obanim -> action baking */
|
|
|
|
#include "BIF_butspace.h"
|
|
#include "BIF_editaction.h"
|
|
#include "BIF_editarmature.h"
|
|
#include "BIF_editnla.h"
|
|
#include "BIF_editview.h"
|
|
#include "BIF_gl.h"
|
|
#include "BIF_interface.h"
|
|
#include "BIF_keyframing.h"
|
|
#include "BIF_mywindow.h"
|
|
#include "BIF_poseobject.h"
|
|
#include "BIF_screen.h"
|
|
#include "BIF_space.h"
|
|
#include "BIF_toolbox.h"
|
|
#include "BIF_transform.h"
|
|
|
|
#include "BSE_edit.h"
|
|
#include "BSE_drawipo.h"
|
|
#include "BSE_headerbuttons.h"
|
|
#include "BSE_editaction_types.h"
|
|
#include "BSE_editipo.h"
|
|
#include "BSE_time.h"
|
|
#include "BSE_trans_types.h"
|
|
|
|
#include "BDR_drawaction.h"
|
|
#include "BDR_editobject.h"
|
|
#include "BDR_gpencil.h"
|
|
|
|
#include "mydevice.h"
|
|
#include "blendef.h"
|
|
#include "nla.h"
|
|
|
|
/* **************************************************** */
|
|
/* ACTION API */
|
|
|
|
/* this function adds a new Action block */
|
|
bAction *add_empty_action (char *name)
|
|
{
|
|
bAction *act;
|
|
|
|
act= alloc_libblock(&G.main->action, ID_AC, name);
|
|
act->id.flag |= LIB_FAKEUSER;
|
|
act->id.us++;
|
|
|
|
return act;
|
|
}
|
|
|
|
/* generic get current action call, for action window context */
|
|
bAction *ob_get_action (Object *ob)
|
|
{
|
|
bActionStrip *strip;
|
|
|
|
if(ob->action)
|
|
return ob->action;
|
|
|
|
for (strip=ob->nlastrips.first; strip; strip=strip->next) {
|
|
if (strip->flag & ACTSTRIP_SELECT)
|
|
return strip->act;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* used by ipo, outliner, buttons to find the active channel */
|
|
bActionChannel *get_hilighted_action_channel (bAction *action)
|
|
{
|
|
bActionChannel *achan;
|
|
|
|
if (!action)
|
|
return NULL;
|
|
|
|
for (achan= action->chanbase.first; achan; achan= achan->next) {
|
|
if (VISIBLE_ACHAN(achan)) {
|
|
if (SEL_ACHAN(achan) && (achan->flag & ACHAN_HILIGHTED))
|
|
return achan;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* ----------------------------------------- */
|
|
|
|
void remake_action_ipos (bAction *act)
|
|
{
|
|
bActionChannel *achan;
|
|
bConstraintChannel *conchan;
|
|
IpoCurve *icu;
|
|
|
|
for (achan= act->chanbase.first; achan; achan= achan->next) {
|
|
if (achan->ipo) {
|
|
for (icu = achan->ipo->curve.first; icu; icu=icu->next) {
|
|
sort_time_ipocurve(icu);
|
|
testhandles_ipocurve(icu);
|
|
}
|
|
}
|
|
for (conchan=achan->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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
synchronize_action_strips();
|
|
}
|
|
|
|
/* **************************************************** */
|
|
/* FILTER->EDIT STRUCTURES */
|
|
/*
|
|
* This method involves generating a list of edit structures which enable
|
|
* tools to naively perform the actions they require without all the boiler-plate
|
|
* associated with loops within loops and checking for cases to ignore.
|
|
*/
|
|
|
|
/* this function allocates memory for a new bActListElem struct for the
|
|
* provided action channel-data.
|
|
*/
|
|
bActListElem *make_new_actlistelem (void *data, short datatype, void *owner, short ownertype)
|
|
{
|
|
bActListElem *ale= NULL;
|
|
|
|
/* only allocate memory if there is data to convert */
|
|
if (data) {
|
|
/* allocate and set generic data */
|
|
ale= MEM_callocN(sizeof(bActListElem), "bActListElem");
|
|
|
|
ale->data= data;
|
|
ale->type= datatype;
|
|
ale->owner= owner;
|
|
ale->ownertype= ownertype;
|
|
|
|
if ((owner) && (ownertype == ACTTYPE_ACHAN)) {
|
|
bActionChannel *ochan= (bActionChannel *)owner;
|
|
ale->grp= ochan->grp;
|
|
}
|
|
else
|
|
ale->grp= NULL;
|
|
|
|
/* do specifics */
|
|
switch (datatype) {
|
|
case ACTTYPE_GROUP:
|
|
{
|
|
bActionGroup *agrp= (bActionGroup *)data;
|
|
|
|
ale->flag= agrp->flag;
|
|
|
|
ale->key_data= NULL;
|
|
ale->datatype= ALE_GROUP;
|
|
}
|
|
break;
|
|
case ACTTYPE_ACHAN:
|
|
{
|
|
bActionChannel *achan= (bActionChannel *)data;
|
|
|
|
ale->flag= achan->flag;
|
|
|
|
if (achan->ipo) {
|
|
ale->key_data= achan->ipo;
|
|
ale->datatype= ALE_IPO;
|
|
}
|
|
else {
|
|
ale->key_data= NULL;
|
|
ale->datatype= ALE_NONE;
|
|
}
|
|
}
|
|
break;
|
|
case ACTTYPE_CONCHAN:
|
|
case ACTTYPE_CONCHAN2:
|
|
{
|
|
bConstraintChannel *conchan= (bConstraintChannel *)data;
|
|
|
|
ale->flag= conchan->flag;
|
|
|
|
if (datatype == ACTTYPE_CONCHAN2) {
|
|
/* CONCHAN2 is a hack so that constraint-channels keyframes can be edited */
|
|
if (conchan->ipo) {
|
|
ale->key_data= conchan->ipo;
|
|
ale->datatype= ALE_IPO;
|
|
}
|
|
else {
|
|
ale->key_data= NULL;
|
|
ale->datatype= ALE_NONE;
|
|
}
|
|
}
|
|
else {
|
|
if (conchan->ipo && conchan->ipo->curve.first) {
|
|
/* we assume that constraint ipo blocks only have 1 curve:
|
|
* INFLUENCE, so we pretend that a constraint channel is
|
|
* really just a Ipo-Curve channel instead.
|
|
*/
|
|
ale->key_data= conchan->ipo->curve.first;
|
|
ale->datatype= ALE_ICU;
|
|
}
|
|
else {
|
|
ale->key_data= NULL;
|
|
ale->datatype= ALE_NONE;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case ACTTYPE_ICU:
|
|
{
|
|
IpoCurve *icu= (IpoCurve *)data;
|
|
|
|
ale->flag= icu->flag;
|
|
ale->key_data= icu;
|
|
ale->datatype= ALE_ICU;
|
|
}
|
|
break;
|
|
|
|
case ACTTYPE_FILLIPO:
|
|
case ACTTYPE_FILLCON:
|
|
{
|
|
bActionChannel *achan= (bActionChannel *)data;
|
|
|
|
if (datatype == ACTTYPE_FILLIPO)
|
|
ale->flag= FILTER_IPO_ACHAN(achan);
|
|
else
|
|
ale->flag= FILTER_CON_ACHAN(achan);
|
|
|
|
ale->key_data= NULL;
|
|
ale->datatype= ALE_NONE;
|
|
}
|
|
break;
|
|
case ACTTYPE_IPO:
|
|
{
|
|
ale->flag= 0;
|
|
ale->key_data= data;
|
|
ale->datatype= ALE_IPO;
|
|
}
|
|
break;
|
|
case ACTTYPE_GPLAYER:
|
|
{
|
|
bGPDlayer *gpl= (bGPDlayer *)data;
|
|
|
|
ale->flag= gpl->flag;
|
|
|
|
ale->key_data= NULL;
|
|
ale->datatype= ALE_GPFRAME;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* return created datatype */
|
|
return ale;
|
|
}
|
|
|
|
/* ----------------------------------------- */
|
|
|
|
static void actdata_filter_actionchannel (ListBase *act_data, bActionChannel *achan, int filter_mode)
|
|
{
|
|
bActListElem *ale;
|
|
bConstraintChannel *conchan;
|
|
IpoCurve *icu;
|
|
|
|
/* only work with this channel and its subchannels if it is visible */
|
|
if (!(filter_mode & ACTFILTER_VISIBLE) || VISIBLE_ACHAN(achan)) {
|
|
/* only work with this channel and its subchannels if it is editable */
|
|
if (!(filter_mode & ACTFILTER_FOREDIT) || EDITABLE_ACHAN(achan)) {
|
|
/* check if this achan should only be included if it is selected */
|
|
if (!(filter_mode & ACTFILTER_SEL) || SEL_ACHAN(achan)) {
|
|
/* are we only interested in the ipo-curves? */
|
|
if ((filter_mode & ACTFILTER_ONLYICU)==0) {
|
|
ale= make_new_actlistelem(achan, ACTTYPE_ACHAN, achan, ACTTYPE_ACHAN);
|
|
if (ale) BLI_addtail(act_data, ale);
|
|
}
|
|
}
|
|
else {
|
|
/* for insert key... this check could be improved */
|
|
return;
|
|
}
|
|
|
|
/* check if expanded - if not, continue on to next action channel */
|
|
if (EXPANDED_ACHAN(achan) == 0 && (filter_mode & ACTFILTER_ONLYICU)==0) {
|
|
/* only exit if we don't need to include constraint channels for group-channel keyframes */
|
|
if ( !(filter_mode & ACTFILTER_IPOKEYS) || (achan->grp == NULL) || (EXPANDED_AGRP(achan->grp)==0) )
|
|
return;
|
|
}
|
|
|
|
/* ipo channels */
|
|
if ((achan->ipo) && (filter_mode & ACTFILTER_IPOKEYS)==0) {
|
|
/* include ipo-expand widget? */
|
|
if ((filter_mode & ACTFILTER_CHANNELS) && (filter_mode & ACTFILTER_ONLYICU)==0) {
|
|
ale= make_new_actlistelem(achan, ACTTYPE_FILLIPO, achan, ACTTYPE_ACHAN);
|
|
if (ale) BLI_addtail(act_data, ale);
|
|
}
|
|
|
|
/* add ipo-curve channels? */
|
|
if (FILTER_IPO_ACHAN(achan) || (filter_mode & ACTFILTER_ONLYICU)) {
|
|
/* loop through ipo-curve channels, adding them */
|
|
for (icu= achan->ipo->curve.first; icu; icu=icu->next) {
|
|
ale= make_new_actlistelem(icu, ACTTYPE_ICU, achan, ACTTYPE_ACHAN);
|
|
if (ale) BLI_addtail(act_data, ale);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* constraint channels */
|
|
if (achan->constraintChannels.first) {
|
|
/* include constraint-expand widget? */
|
|
if ( (filter_mode & ACTFILTER_CHANNELS) && !(filter_mode & ACTFILTER_ONLYICU)
|
|
&& !(filter_mode & ACTFILTER_IPOKEYS) )
|
|
{
|
|
ale= make_new_actlistelem(achan, ACTTYPE_FILLCON, achan, ACTTYPE_ACHAN);
|
|
if (ale) BLI_addtail(act_data, ale);
|
|
}
|
|
|
|
/* add constraint channels? */
|
|
if (FILTER_CON_ACHAN(achan) || (filter_mode & ACTFILTER_IPOKEYS) || (filter_mode & ACTFILTER_ONLYICU)) {
|
|
/* loop through constraint channels, checking and adding them */
|
|
for (conchan=achan->constraintChannels.first; conchan; conchan=conchan->next) {
|
|
/* only work with this channel and its subchannels if it is editable */
|
|
if (!(filter_mode & ACTFILTER_FOREDIT) || EDITABLE_CONCHAN(conchan)) {
|
|
/* check if this conchan should only be included if it is selected */
|
|
if (!(filter_mode & ACTFILTER_SEL) || SEL_CONCHAN(conchan)) {
|
|
if (filter_mode & ACTFILTER_IPOKEYS) {
|
|
ale= make_new_actlistelem(conchan, ACTTYPE_CONCHAN2, achan, ACTTYPE_ACHAN);
|
|
if (ale) BLI_addtail(act_data, ale);
|
|
}
|
|
else {
|
|
ale= make_new_actlistelem(conchan, ACTTYPE_CONCHAN, achan, ACTTYPE_ACHAN);
|
|
if (ale) BLI_addtail(act_data, ale);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void actdata_filter_action (ListBase *act_data, bAction *act, int filter_mode)
|
|
{
|
|
bActListElem *ale=NULL;
|
|
bActionGroup *agrp;
|
|
bActionChannel *achan, *lastchan=NULL;
|
|
|
|
/* loop over groups */
|
|
for (agrp= act->groups.first; agrp; agrp= agrp->next) {
|
|
/* add this group as a channel first */
|
|
if (!(filter_mode & ACTFILTER_ONLYICU) && !(filter_mode & ACTFILTER_IPOKEYS)) {
|
|
/* check if filtering by selection */
|
|
if ( !(filter_mode & ACTFILTER_SEL) || SEL_AGRP(agrp) ) {
|
|
ale= make_new_actlistelem(agrp, ACTTYPE_GROUP, NULL, ACTTYPE_NONE);
|
|
if (ale) BLI_addtail(act_data, ale);
|
|
}
|
|
}
|
|
|
|
/* store reference to last channel of group */
|
|
if (agrp->channels.last)
|
|
lastchan= agrp->channels.last;
|
|
|
|
|
|
/* there are some situations, where only the channels of the active group should get considered */
|
|
if (!(filter_mode & ACTFILTER_ACTGROUPED) || (agrp->flag & AGRP_ACTIVE)) {
|
|
/* filters here are a bit convoulted...
|
|
* - groups show a "summary" of keyframes beside their name which must accessable for tools which handle keyframes
|
|
* - groups can be collapsed (and those tools which are only interested in channels rely on knowing that group is closed)
|
|
*
|
|
* cases when we should include action-channels and so-forth inside group:
|
|
* - we don't care about visibility
|
|
* - group is expanded
|
|
* - we're interested in keyframes, but not if they appear in selected channels
|
|
*/
|
|
if ( (!(filter_mode & ACTFILTER_VISIBLE) || EXPANDED_AGRP(agrp)) ||
|
|
( ((filter_mode & ACTFILTER_IPOKEYS) || (filter_mode & ACTFILTER_ONLYICU)) &&
|
|
(!(filter_mode & ACTFILTER_SEL) || (SEL_AGRP(agrp))) ) )
|
|
{
|
|
if (!(filter_mode & ACTFILTER_FOREDIT) || EDITABLE_AGRP(agrp)) {
|
|
for (achan= agrp->channels.first; achan && achan->grp==agrp; achan= achan->next) {
|
|
actdata_filter_actionchannel(act_data, achan, filter_mode);
|
|
}
|
|
|
|
/* remove group from filtered list if last element is group
|
|
* (i.e. only if group had channels, which were all hidden)
|
|
*/
|
|
if ( (ale) && (act_data->last == ale) &&
|
|
(ale->data == agrp) && (agrp->channels.first) )
|
|
{
|
|
BLI_freelinkN(act_data, ale);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* loop over un-grouped action channels (only if we're not only considering those channels in the active group) */
|
|
if (!(filter_mode & ACTFILTER_ACTGROUPED)) {
|
|
for (achan=(lastchan)?lastchan->next:act->chanbase.first; achan; achan=achan->next) {
|
|
actdata_filter_actionchannel(act_data, achan, filter_mode);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void actdata_filter_shapekey (ListBase *act_data, Key *key, int filter_mode)
|
|
{
|
|
bActListElem *ale;
|
|
KeyBlock *kb;
|
|
IpoCurve *icu;
|
|
int i;
|
|
|
|
/* are we filtering for display or editing */
|
|
if (filter_mode & ACTFILTER_FORDRAWING) {
|
|
/* for display - loop over shapekeys, adding ipo-curve references where needed */
|
|
kb= key->block.first;
|
|
|
|
/* loop through possible shapekeys, manually creating entries */
|
|
for (i= 1; i < key->totkey; i++) {
|
|
ale= MEM_callocN(sizeof(bActListElem), "bActListElem");
|
|
kb = kb->next;
|
|
|
|
ale->data= kb;
|
|
ale->type= ACTTYPE_SHAPEKEY; /* 'abused' usage of this type */
|
|
ale->owner= key;
|
|
ale->ownertype= ACTTYPE_SHAPEKEY;
|
|
ale->datatype= ALE_NONE;
|
|
ale->index = i;
|
|
|
|
if (key->ipo) {
|
|
for (icu= key->ipo->curve.first; icu; icu=icu->next) {
|
|
if (icu->adrcode == i) {
|
|
ale->key_data= icu;
|
|
ale->datatype= ALE_ICU;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
BLI_addtail(act_data, ale);
|
|
}
|
|
}
|
|
else {
|
|
/* loop over ipo curves if present - for editing */
|
|
if (key->ipo) {
|
|
if (filter_mode & ACTFILTER_IPOKEYS) {
|
|
ale= make_new_actlistelem(key->ipo, ACTTYPE_IPO, key, ACTTYPE_SHAPEKEY);
|
|
if (ale) BLI_addtail(act_data, ale);
|
|
}
|
|
else {
|
|
for (icu= key->ipo->curve.first; icu; icu=icu->next) {
|
|
ale= make_new_actlistelem(icu, ACTTYPE_ICU, key, ACTTYPE_SHAPEKEY);
|
|
if (ale) BLI_addtail(act_data, ale);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void actdata_filter_gpencil (ListBase *act_data, bScreen *sc, int filter_mode)
|
|
{
|
|
bActListElem *ale;
|
|
ScrArea *sa;
|
|
bGPdata *gpd;
|
|
bGPDlayer *gpl;
|
|
|
|
/* check if filtering types are appropriate */
|
|
if ( !(filter_mode & (ACTFILTER_IPOKEYS|ACTFILTER_ONLYICU|ACTFILTER_ACTGROUPED)) )
|
|
{
|
|
/* special hack for fullscreen area (which must be this one then):
|
|
* - we use the curarea->full as screen to get spaces from, since the
|
|
* old (pre-fullscreen) screen was stored there...
|
|
* - this is needed as all data would otherwise disappear
|
|
*/
|
|
if ((curarea->full) && (curarea->spacetype==SPACE_ACTION))
|
|
sc= curarea->full;
|
|
|
|
/* loop over spaces in current screen, finding gpd blocks (could be slow!) */
|
|
for (sa= sc->areabase.first; sa; sa= sa->next) {
|
|
/* try to get gp data */
|
|
gpd= gpencil_data_getactive(sa);
|
|
if (gpd == NULL) continue;
|
|
|
|
/* add gpd as channel too (if for drawing, and it has layers) */
|
|
if ((filter_mode & ACTFILTER_FORDRAWING) && (gpd->layers.first)) {
|
|
/* add to list */
|
|
ale= make_new_actlistelem(gpd, ACTTYPE_GPDATABLOCK, sa, ACTTYPE_SPECIALDATA);
|
|
if (ale) BLI_addtail(act_data, ale);
|
|
}
|
|
|
|
/* only add layers if they will be visible (if drawing channels) */
|
|
if ( !(filter_mode & ACTFILTER_VISIBLE) || (EXPANDED_GPD(gpd)) ) {
|
|
/* loop over layers as the conditions are acceptable */
|
|
for (gpl= gpd->layers.first; gpl; gpl= gpl->next) {
|
|
/* only if selected */
|
|
if (!(filter_mode & ACTFILTER_SEL) || SEL_GPL(gpl)) {
|
|
/* only if editable */
|
|
if (!(filter_mode & ACTFILTER_FOREDIT) || EDITABLE_GPL(gpl)) {
|
|
/* add to list */
|
|
ale= make_new_actlistelem(gpl, ACTTYPE_GPLAYER, gpd, ACTTYPE_GPDATABLOCK);
|
|
if (ale) BLI_addtail(act_data, ale);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* This function filters the active data source to leave only the desired
|
|
* data types. 'Public' api call.
|
|
* *act_data: is a pointer to a ListBase, to which the filtered action data
|
|
* will be placed for use.
|
|
* filter_mode: how should the data be filtered - bitmapping accessed flags
|
|
*/
|
|
void actdata_filter (ListBase *act_data, int filter_mode, void *data, short datatype)
|
|
{
|
|
/* only filter data if there's somewhere to put it */
|
|
if (data && act_data) {
|
|
bActListElem *ale, *next;
|
|
|
|
/* firstly filter the data */
|
|
switch (datatype) {
|
|
case ACTCONT_ACTION:
|
|
actdata_filter_action(act_data, data, filter_mode);
|
|
break;
|
|
case ACTCONT_SHAPEKEY:
|
|
actdata_filter_shapekey(act_data, data, filter_mode);
|
|
break;
|
|
case ACTCONT_GPENCIL:
|
|
actdata_filter_gpencil(act_data, data, filter_mode);
|
|
break;
|
|
}
|
|
|
|
/* remove any weedy entries */
|
|
for (ale= act_data->first; ale; ale= next) {
|
|
next= ale->next;
|
|
|
|
if (ale->type == ACTTYPE_NONE)
|
|
BLI_freelinkN(act_data, ale);
|
|
|
|
if (filter_mode & ACTFILTER_IPOKEYS) {
|
|
if (ale->datatype != ALE_IPO)
|
|
BLI_freelinkN(act_data, ale);
|
|
else if (ale->key_data == NULL)
|
|
BLI_freelinkN(act_data, ale);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* **************************************************** */
|
|
/* GENERAL ACTION TOOLS */
|
|
|
|
/* gets the key data from the currently selected
|
|
* mesh/lattice. If a mesh is not selected, or does not have
|
|
* key data, then we return NULL (currently only
|
|
* returns key data for RVK type meshes). If there
|
|
* is an action that is pinned, return null
|
|
*/
|
|
/* Note: there's a similar function in key.c (ob_get_key) */
|
|
Key *get_action_mesh_key(void)
|
|
{
|
|
Object *ob;
|
|
Key *key;
|
|
|
|
ob = OBACT;
|
|
if (ob == NULL)
|
|
return NULL;
|
|
|
|
if (G.saction->pin) return NULL;
|
|
|
|
if (ob->type==OB_MESH)
|
|
key = ((Mesh *)ob->data)->key;
|
|
else if (ob->type==OB_LATTICE)
|
|
key = ((Lattice *)ob->data)->key;
|
|
else if (ELEM(ob->type, OB_CURVE, OB_SURF))
|
|
key= ((Curve *)ob->data)->key;
|
|
else
|
|
return NULL;
|
|
|
|
if (key) {
|
|
if (key->type == KEY_RELATIVE)
|
|
return key;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* TODO: kill this! */
|
|
int get_nearest_key_num (Key *key, short *mval, float *x)
|
|
{
|
|
/* returns the key num that cooresponds to the
|
|
* y value of the mouse click. Does not check
|
|
* if this is a valid keynum. Also gives the Ipo
|
|
* x coordinate.
|
|
*/
|
|
int num;
|
|
float y;
|
|
|
|
areamouseco_to_ipoco(G.v2d, mval, x, &y);
|
|
num = (int) ((CHANNELHEIGHT/2 - y) / (CHANNELHEIGHT+CHANNELSKIP));
|
|
|
|
return (num + 1);
|
|
}
|
|
|
|
/* this function finds the channel that mouse is floating over */
|
|
void *get_nearest_act_channel (short mval[], short *ret_type, void **owner)
|
|
{
|
|
ListBase act_data = {NULL, NULL};
|
|
bActListElem *ale;
|
|
void *data;
|
|
short datatype;
|
|
int filter;
|
|
|
|
int clickmin, clickmax;
|
|
float x,y;
|
|
|
|
/* init 'owner' return val */
|
|
*owner= NULL;
|
|
|
|
/* determine what type of data we are operating on */
|
|
data = get_action_context(&datatype);
|
|
if (data == NULL) {
|
|
*ret_type= ACTTYPE_NONE;
|
|
return NULL;
|
|
}
|
|
|
|
areamouseco_to_ipoco(G.v2d, mval, &x, &y);
|
|
clickmin = (int) (((CHANNELHEIGHT/2) - y) / (CHANNELHEIGHT+CHANNELSKIP));
|
|
clickmax = clickmin;
|
|
|
|
if (clickmax < 0) {
|
|
*ret_type= ACTTYPE_NONE;
|
|
return NULL;
|
|
}
|
|
|
|
/* filter data */
|
|
filter= (ACTFILTER_FORDRAWING | ACTFILTER_VISIBLE | ACTFILTER_CHANNELS);
|
|
actdata_filter(&act_data, filter, data, datatype);
|
|
|
|
for (ale= act_data.first; ale; ale= ale->next) {
|
|
if (clickmax < 0)
|
|
break;
|
|
if (clickmin <= 0) {
|
|
/* found match */
|
|
*ret_type= ale->type;
|
|
data= ale->data;
|
|
*owner= ale->owner;
|
|
|
|
BLI_freelistN(&act_data);
|
|
|
|
return data;
|
|
}
|
|
--clickmin;
|
|
--clickmax;
|
|
}
|
|
|
|
/* cleanup */
|
|
BLI_freelistN(&act_data);
|
|
|
|
*ret_type= ACTTYPE_NONE;
|
|
return NULL;
|
|
}
|
|
|
|
/* used only by mouse_action. It is used to find the location of the nearest
|
|
* keyframe to where the mouse clicked,
|
|
*/
|
|
static void *get_nearest_action_key (float *selx, short *sel, short *ret_type, bActionChannel **par)
|
|
{
|
|
ListBase act_data = {NULL, NULL};
|
|
ListBase act_keys = {NULL, NULL};
|
|
bActListElem *ale;
|
|
ActKeyColumn *ak;
|
|
void *data;
|
|
short datatype;
|
|
int filter;
|
|
|
|
rctf rectf;
|
|
float xmin, xmax, x, y;
|
|
int clickmin, clickmax;
|
|
short mval[2];
|
|
short found = 0;
|
|
|
|
getmouseco_areawin (mval);
|
|
|
|
/* action-channel */
|
|
*par= NULL;
|
|
|
|
/* determine what type of data we are operating on */
|
|
data = get_action_context(&datatype);
|
|
if (data == NULL) {
|
|
*ret_type= ACTTYPE_NONE;
|
|
return NULL;
|
|
}
|
|
|
|
areamouseco_to_ipoco(G.v2d, mval, &x, &y);
|
|
clickmin = (int) (((CHANNELHEIGHT/2) - y) / (CHANNELHEIGHT+CHANNELSKIP));
|
|
clickmax = clickmin;
|
|
|
|
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);
|
|
|
|
/* if action is mapped in NLA, it returns a correction */
|
|
if (NLA_ACTION_SCALED && datatype==ACTCONT_ACTION) {
|
|
xmin= get_action_frame(OBACT, rectf.xmin);
|
|
xmax= get_action_frame(OBACT, rectf.xmax);
|
|
}
|
|
else {
|
|
xmin= rectf.xmin;
|
|
xmax= rectf.xmax;
|
|
}
|
|
|
|
if (clickmax < 0) {
|
|
*ret_type= ACTTYPE_NONE;
|
|
return NULL;
|
|
}
|
|
|
|
/* filter data */
|
|
filter= (ACTFILTER_FORDRAWING | ACTFILTER_VISIBLE | ACTFILTER_CHANNELS);
|
|
actdata_filter(&act_data, filter, data, datatype);
|
|
|
|
for (ale= act_data.first; ale; ale= ale->next) {
|
|
if (clickmax < 0)
|
|
break;
|
|
if (clickmin <= 0) {
|
|
/* found match */
|
|
|
|
/* make list of keyframes */
|
|
if (ale->key_data) {
|
|
switch (ale->datatype) {
|
|
case ALE_IPO:
|
|
{
|
|
Ipo *ipo= (Ipo *)ale->key_data;
|
|
ipo_to_keylist(ipo, &act_keys, NULL, NULL);
|
|
}
|
|
break;
|
|
case ALE_ICU:
|
|
{
|
|
IpoCurve *icu= (IpoCurve *)ale->key_data;
|
|
icu_to_keylist(icu, &act_keys, NULL, NULL);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else if (ale->type == ACTTYPE_GROUP) {
|
|
bActionGroup *agrp= (bActionGroup *)ale->data;
|
|
agroup_to_keylist(agrp, &act_keys, NULL, NULL);
|
|
}
|
|
else if (ale->type == ACTTYPE_GPDATABLOCK) {
|
|
/* cleanup */
|
|
BLI_freelistN(&act_data);
|
|
|
|
/* this channel currently doens't have any keyframes... must ignore! */
|
|
*ret_type= ACTTYPE_NONE;
|
|
return NULL;
|
|
}
|
|
else if (ale->type == ACTTYPE_GPLAYER) {
|
|
bGPDlayer *gpl= (bGPDlayer *)ale->data;
|
|
gpl_to_keylist(gpl, &act_keys, NULL, NULL);
|
|
}
|
|
|
|
/* loop through keyframes, finding one that was clicked on */
|
|
for (ak= act_keys.first; ak; ak= ak->next) {
|
|
if (IN_RANGE(ak->cfra, xmin, xmax)) {
|
|
*selx= ak->cfra;
|
|
found= 1;
|
|
break;
|
|
}
|
|
}
|
|
/* no matching keyframe found - set to mean frame value so it doesn't actually select anything */
|
|
if (found == 0)
|
|
*selx= ((xmax+xmin) / 2);
|
|
|
|
/* figure out what to return */
|
|
if (datatype == ACTCONT_ACTION) {
|
|
*par= ale->owner; /* assume that this is an action channel */
|
|
*ret_type= ale->type;
|
|
data = ale->data;
|
|
}
|
|
else if (datatype == ACTCONT_SHAPEKEY) {
|
|
data = ale->key_data;
|
|
*ret_type= ACTTYPE_ICU;
|
|
}
|
|
else if (datatype == ACTCONT_GPENCIL) {
|
|
data = ale->data;
|
|
*ret_type= ACTTYPE_GPLAYER;
|
|
}
|
|
|
|
/* cleanup tempolary lists */
|
|
BLI_freelistN(&act_keys);
|
|
act_keys.first = act_keys.last = NULL;
|
|
|
|
BLI_freelistN(&act_data);
|
|
|
|
return data;
|
|
}
|
|
--clickmin;
|
|
--clickmax;
|
|
}
|
|
|
|
/* cleanup */
|
|
BLI_freelistN(&act_data);
|
|
|
|
*ret_type= ACTTYPE_NONE;
|
|
return NULL;
|
|
}
|
|
|
|
void *get_action_context (short *datatype)
|
|
{
|
|
bAction *act;
|
|
Key *key;
|
|
|
|
/* get pointers to active action/shapekey blocks */
|
|
act = (G.saction)? G.saction->action: NULL;
|
|
key = get_action_mesh_key();
|
|
|
|
/* check mode selector */
|
|
if (G.saction) {
|
|
switch (G.saction->mode) {
|
|
case SACTCONT_ACTION:
|
|
*datatype= ACTCONT_ACTION;
|
|
return act;
|
|
|
|
case SACTCONT_SHAPEKEY:
|
|
*datatype= ACTCONT_SHAPEKEY;
|
|
return key;
|
|
|
|
case SACTCONT_GPENCIL:
|
|
*datatype= ACTCONT_GPENCIL;
|
|
return G.curscreen; // FIXME: add that dopesheet type thing here!
|
|
|
|
default: /* includes SACTCONT_DOPESHEET for now */
|
|
*datatype= ACTCONT_NONE;
|
|
return NULL;
|
|
}
|
|
}
|
|
else {
|
|
/* resort to guessing based on what is available */
|
|
if (act) {
|
|
*datatype= ACTCONT_ACTION;
|
|
return act;
|
|
}
|
|
else if (key) {
|
|
*datatype= ACTCONT_SHAPEKEY;
|
|
return key;
|
|
}
|
|
else {
|
|
*datatype= ACTCONT_NONE;
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Quick-tool for preview-range functionality in Action Editor for setting Preview-Range
|
|
* bounds to extents of Action, when Ctrl-Alt-P is used. Only available for actions.
|
|
*/
|
|
void action_previewrange_set (bAction *act)
|
|
{
|
|
float start, end;
|
|
|
|
/* sanity check */
|
|
if (act == NULL)
|
|
return;
|
|
|
|
/* calculate range + make sure it is adjusted for nla-scaling */
|
|
calc_action_range(act, &start, &end, 0);
|
|
if (NLA_ACTION_SCALED) {
|
|
start= get_action_frame_inv(OBACT, start);
|
|
end= get_action_frame_inv(OBACT, end);
|
|
}
|
|
|
|
/* set preview range */
|
|
G.scene->r.psfra= (int)start;
|
|
G.scene->r.pefra= (int)end;
|
|
|
|
BIF_undo_push("Set anim-preview range");
|
|
allqueue(REDRAWTIME, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWNLA, 0);
|
|
allqueue(REDRAWIPO, 0);
|
|
allqueue(REDRAWBUTSALL, 0);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
/* ACTION CHANNEL GROUPS */
|
|
|
|
/* Get the active action-group for an Action */
|
|
bActionGroup *get_active_actiongroup (bAction *act)
|
|
{
|
|
bActionGroup *agrp= NULL;
|
|
|
|
if (act && act->groups.first) {
|
|
for (agrp= act->groups.first; agrp; agrp= agrp->next) {
|
|
if (agrp->flag & AGRP_ACTIVE)
|
|
break;
|
|
}
|
|
}
|
|
|
|
return agrp;
|
|
}
|
|
|
|
/* Make the given Action-Group the active one */
|
|
void set_active_actiongroup (bAction *act, bActionGroup *agrp, short select)
|
|
{
|
|
bActionGroup *grp;
|
|
|
|
/* sanity checks */
|
|
if (act == NULL)
|
|
return;
|
|
|
|
/* Deactive all others */
|
|
for (grp= act->groups.first; grp; grp= grp->next) {
|
|
if ((grp==agrp) && (select))
|
|
grp->flag |= AGRP_ACTIVE;
|
|
else
|
|
grp->flag &= ~AGRP_ACTIVE;
|
|
}
|
|
}
|
|
|
|
/* Add given channel into (active) group
|
|
* - assumes that channel is not linked to anything anymore
|
|
* - always adds at the end of the group
|
|
*/
|
|
static void action_groups_addachan (bAction *act, bActionGroup *agrp, bActionChannel *achan)
|
|
{
|
|
bActionChannel *chan;
|
|
short done=0;
|
|
|
|
/* sanity checks */
|
|
if (ELEM3(NULL, act, agrp, achan))
|
|
return;
|
|
|
|
/* if no channels, just add to two lists at the same time */
|
|
if (act->chanbase.first == NULL) {
|
|
achan->next = achan->prev = NULL;
|
|
|
|
agrp->channels.first = agrp->channels.last = achan;
|
|
act->chanbase.first = act->chanbase.last = achan;
|
|
|
|
achan->grp= agrp;
|
|
return;
|
|
}
|
|
|
|
/* try to find a channel to slot this in before/after */
|
|
for (chan= act->chanbase.first; chan; chan= chan->next) {
|
|
/* if channel has no group, then we have ungrouped channels, which should always occur after groups */
|
|
if (chan->grp == NULL) {
|
|
BLI_insertlinkbefore(&act->chanbase, chan, achan);
|
|
|
|
if (agrp->channels.first == NULL)
|
|
agrp->channels.first= achan;
|
|
agrp->channels.last= achan;
|
|
|
|
done= 1;
|
|
break;
|
|
}
|
|
|
|
/* if channel has group after current, we can now insert (otherwise we have gone too far) */
|
|
else if (chan->grp == agrp->next) {
|
|
BLI_insertlinkbefore(&act->chanbase, chan, achan);
|
|
|
|
if (agrp->channels.first == NULL)
|
|
agrp->channels.first= achan;
|
|
agrp->channels.last= achan;
|
|
|
|
done= 1;
|
|
break;
|
|
}
|
|
|
|
/* if channel has group we're targeting, check whether it is the last one of these */
|
|
else if (chan->grp == agrp) {
|
|
if ((chan->next) && (chan->next->grp != agrp)) {
|
|
BLI_insertlinkafter(&act->chanbase, chan, achan);
|
|
agrp->channels.last= achan;
|
|
done= 1;
|
|
break;
|
|
}
|
|
else if (chan->next == NULL) {
|
|
BLI_addtail(&act->chanbase, achan);
|
|
agrp->channels.last= achan;
|
|
done= 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* if channel has group before target, check whether the next one is something after target */
|
|
else if (chan->grp == agrp->prev) {
|
|
if (chan->next) {
|
|
if ((chan->next->grp != chan->grp) && (chan->next->grp != agrp)) {
|
|
BLI_insertlinkafter(&act->chanbase, chan, achan);
|
|
|
|
agrp->channels.first= achan;
|
|
agrp->channels.last= achan;
|
|
|
|
done= 1;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
BLI_insertlinkafter(&act->chanbase, chan, achan);
|
|
|
|
agrp->channels.first= achan;
|
|
agrp->channels.last= achan;
|
|
|
|
done= 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* only if added, set channel as belonging to this group */
|
|
if (done) {
|
|
achan->grp= agrp;
|
|
}
|
|
else
|
|
printf("Error: ActionChannel: '%s' couldn't be added to Group: '%s' \n", achan->name, agrp->name);
|
|
}
|
|
|
|
/* Remove the given channel from all groups */
|
|
static void action_groups_removeachan (bAction *act, bActionChannel *achan)
|
|
{
|
|
/* sanity checks */
|
|
if (ELEM(NULL, act, achan))
|
|
return;
|
|
|
|
/* check if any group used this directly */
|
|
if (achan->grp) {
|
|
bActionGroup *agrp= achan->grp;
|
|
|
|
if (agrp->channels.first == agrp->channels.last) {
|
|
if (agrp->channels.first == achan) {
|
|
agrp->channels.first= NULL;
|
|
agrp->channels.last= NULL;
|
|
}
|
|
}
|
|
else if (agrp->channels.first == achan) {
|
|
if ((achan->next) && (achan->next->grp==agrp))
|
|
agrp->channels.first= achan->next;
|
|
else
|
|
agrp->channels.first= NULL;
|
|
}
|
|
else if (agrp->channels.last == achan) {
|
|
if ((achan->prev) && (achan->prev->grp==agrp))
|
|
agrp->channels.last= achan->prev;
|
|
else
|
|
agrp->channels.last= NULL;
|
|
}
|
|
|
|
achan->grp= NULL;
|
|
}
|
|
|
|
/* now just remove from list */
|
|
BLI_remlink(&act->chanbase, achan);
|
|
}
|
|
|
|
/* Add a new Action-Group or add channels to active one */
|
|
void action_groups_group (short add_group)
|
|
{
|
|
bAction *act;
|
|
bActionChannel *achan, *anext;
|
|
bActionGroup *agrp;
|
|
void *data;
|
|
short datatype;
|
|
|
|
/* validate type of data we are working on */
|
|
data = get_action_context(&datatype);
|
|
if (data == NULL) return;
|
|
if (datatype != ACTCONT_ACTION) return;
|
|
act= (bAction *)data;
|
|
|
|
/* get active group */
|
|
if ((act->groups.first==NULL) || (add_group)) {
|
|
/* Add a new group, and make it active */
|
|
agrp= MEM_callocN(sizeof(bActionGroup), "bActionGroup");
|
|
|
|
agrp->flag |= (AGRP_ACTIVE|AGRP_SELECTED|AGRP_EXPANDED);
|
|
sprintf(agrp->name, "Group");
|
|
|
|
BLI_addtail(&act->groups, agrp);
|
|
BLI_uniquename(&act->groups, agrp, "Group", offsetof(bActionGroup, name), 32);
|
|
|
|
set_active_actiongroup(act, agrp, 1);
|
|
|
|
add_group= 1;
|
|
}
|
|
else {
|
|
agrp= get_active_actiongroup(act);
|
|
|
|
if (agrp == NULL) {
|
|
error("No Active Action Group");
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* loop through action-channels, finding those that are selected + visible to move */
|
|
// FIXME: this should be done with action api instead
|
|
for (achan= act->chanbase.first; achan; achan= anext) {
|
|
anext= achan->next;
|
|
|
|
/* make sure not already in new-group */
|
|
if (achan->grp != agrp) {
|
|
if ((achan->grp==NULL) || (EXPANDED_AGRP(achan->grp))) {
|
|
if (VISIBLE_ACHAN(achan) && SEL_ACHAN(achan)) {
|
|
/* unlink from everything else */
|
|
action_groups_removeachan(act, achan);
|
|
|
|
/* add to end of group's channels */
|
|
action_groups_addachan(act, agrp, achan);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* updates and undo */
|
|
if (add_group)
|
|
BIF_undo_push("Add Action Group");
|
|
else
|
|
BIF_undo_push("Add to Action Group");
|
|
|
|
allqueue(REDRAWACTION, 0);
|
|
}
|
|
|
|
/* Remove selected channels from their groups */
|
|
void action_groups_ungroup (void)
|
|
{
|
|
ListBase act_data = {NULL, NULL};
|
|
bActListElem *ale;
|
|
bAction *act;
|
|
void *data;
|
|
short datatype;
|
|
short filter;
|
|
|
|
/* validate type of data we are working on */
|
|
data = get_action_context(&datatype);
|
|
if (data == NULL) return;
|
|
if (datatype != ACTCONT_ACTION) return;
|
|
act= (bAction *)data;
|
|
|
|
/* filter data */
|
|
filter= (ACTFILTER_VISIBLE|ACTFILTER_SEL);
|
|
actdata_filter(&act_data, filter, act, ACTCONT_ACTION);
|
|
|
|
/* Only ungroup selected action-channels */
|
|
for (ale= act_data.first; ale; ale= ale->next) {
|
|
if (ale->type == ACTTYPE_ACHAN) {
|
|
action_groups_removeachan(act, ale->data);
|
|
BLI_addtail(&act->chanbase, ale->data);
|
|
}
|
|
}
|
|
|
|
BLI_freelistN(&act_data);
|
|
|
|
/* updates and undo */
|
|
BIF_undo_push("Remove From Action Groups");
|
|
|
|
allqueue(REDRAWACTION, 0);
|
|
}
|
|
|
|
/* Copy colors from a specified theme's color set to an Action/Bone Group */
|
|
void actionbone_group_copycolors (bActionGroup *grp, short init_new)
|
|
{
|
|
/* error checking */
|
|
if (grp == NULL)
|
|
return;
|
|
|
|
/* only do color copying if using a custom color */
|
|
if (grp->customCol) {
|
|
if (grp->customCol > 0) {
|
|
/* copy theme colors on-to group's custom color in case user tries to edit color */
|
|
bTheme *btheme= U.themes.first;
|
|
ThemeWireColor *col_set= &btheme->tarm[(grp->customCol - 1)];
|
|
|
|
memcpy(&grp->cs, col_set, sizeof(ThemeWireColor));
|
|
}
|
|
else if (init_new) {
|
|
/* init custom colors with a generic multi-color rgb set, if not initialised already (and allowed to do so) */
|
|
if (grp->cs.solid[0] == 0) {
|
|
/* define for setting colors in theme below */
|
|
#define SETCOL(col, r, g, b, a) col[0]=r; col[1]=g; col[2]= b; col[3]= a;
|
|
|
|
SETCOL(grp->cs.solid, 0xff, 0x00, 0x00, 255);
|
|
SETCOL(grp->cs.select, 0x81, 0xe6, 0x14, 255);
|
|
SETCOL(grp->cs.active, 0x18, 0xb6, 0xe0, 255);
|
|
|
|
#undef SETCOL
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* This function is used when inserting keyframes for pose-channels. It assigns the
|
|
* action-channel with the nominated name to a group with the same name as that of
|
|
* the pose-channel with the nominated name.
|
|
*
|
|
* Note: this function calls validate_action_channel if action channel doesn't exist
|
|
*/
|
|
void verify_pchan2achan_grouping (bAction *act, bPose *pose, char name[])
|
|
{
|
|
bActionChannel *achan;
|
|
bPoseChannel *pchan;
|
|
|
|
/* sanity checks */
|
|
if (ELEM3(NULL, act, pose, name))
|
|
return;
|
|
if (name[0] == 0)
|
|
return;
|
|
|
|
/* try to get the channels */
|
|
pchan= get_pose_channel(pose, name);
|
|
if (pchan == NULL) return;
|
|
achan= verify_action_channel(act, name);
|
|
|
|
/* check if pchan has a group */
|
|
if ((pchan->agrp_index > 0) && (achan->grp == NULL)) {
|
|
bActionGroup *agrp, *grp=NULL;
|
|
|
|
/* get group to try to be like */
|
|
agrp= (bActionGroup *)BLI_findlink(&pose->agroups, (pchan->agrp_index - 1));
|
|
if (agrp == NULL) {
|
|
error("PoseChannel has invalid group!");
|
|
return;
|
|
}
|
|
|
|
/* try to find a group which is similar to the one we want (or add one) */
|
|
for (grp= act->groups.first; grp; grp= grp->next) {
|
|
if (!strcmp(grp->name, agrp->name))
|
|
break;
|
|
}
|
|
if (grp == NULL) {
|
|
grp= MEM_callocN(sizeof(bActionGroup), "bActionGroup");
|
|
|
|
grp->flag |= (AGRP_ACTIVE|AGRP_SELECTED|AGRP_EXPANDED);
|
|
|
|
/* copy name */
|
|
sprintf(grp->name, agrp->name);
|
|
|
|
/* deal with group-color copying (grp is destination, agrp is source) */
|
|
memcpy(grp, agrp, sizeof(bActionGroup));
|
|
actionbone_group_copycolors(grp, 1);
|
|
|
|
BLI_addtail(&act->groups, grp);
|
|
}
|
|
|
|
/* make sure this channel is definitely not connected to anything before adding to group */
|
|
action_groups_removeachan(act, achan);
|
|
action_groups_addachan(act, grp, achan);
|
|
}
|
|
}
|
|
|
|
/* This function is used when the user specifically requests to sync changes of pchans + bone groups
|
|
* to achans + action groups. All achans are detached from their groups, and all groups are destroyed.
|
|
* They are then recreated when the achans are reassigned to groups.
|
|
*
|
|
* Note: This doesn't preserve hand-created groups, and will operate on ALL action-channels regardless of
|
|
* whether they were selected or active. More specific filtering can be added later.
|
|
*/
|
|
void sync_pchan2achan_grouping ()
|
|
{
|
|
void *data;
|
|
short datatype;
|
|
bAction *act;
|
|
bActionChannel *achan, *next, *last;
|
|
bPose *pose;
|
|
|
|
/* determine what type of data we are operating on */
|
|
data = get_action_context(&datatype);
|
|
if ((datatype != ACTCONT_ACTION) || (data==NULL)) return;
|
|
if ((G.saction->pin) || (OBACT==NULL) || (OBACT->type != OB_ARMATURE)) {
|
|
error("Action doesn't belong to active armature");
|
|
return;
|
|
}
|
|
|
|
/* get data */
|
|
act= (bAction *)data;
|
|
pose= OBACT->pose;
|
|
|
|
/* remove achan->group links, then delete all groups */
|
|
for (achan= act->chanbase.first; achan; achan= achan->next)
|
|
achan->grp = NULL;
|
|
BLI_freelistN(&act->groups);
|
|
|
|
/* loop through all achans, reassigning them to groups (colors are resyncronised) */
|
|
last= act->chanbase.last;
|
|
for (achan= act->chanbase.first; achan && achan!=last; achan= next) {
|
|
next= achan->next;
|
|
verify_pchan2achan_grouping(act, pose, achan->name);
|
|
}
|
|
|
|
/* undo and redraw */
|
|
BIF_undo_push("Sync Armature-Data and Action");
|
|
allqueue(REDRAWACTION, 0);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
/* TRANSFORM TOOLS */
|
|
|
|
/* main call to start transforming keyframes */
|
|
void transform_action_keys (int mode, int dummy)
|
|
{
|
|
void *data;
|
|
short datatype;
|
|
short context = (U.flag & USER_DRAGIMMEDIATE)?CTX_TWEAK:CTX_NONE;
|
|
|
|
/* determine what type of data we are operating on */
|
|
data = get_action_context(&datatype);
|
|
if (data == NULL) return;
|
|
|
|
switch (mode) {
|
|
case 'g':
|
|
{
|
|
initTransform(TFM_TIME_TRANSLATE, context);
|
|
Transform();
|
|
}
|
|
break;
|
|
case 's':
|
|
{
|
|
initTransform(TFM_TIME_SCALE, context);
|
|
Transform();
|
|
}
|
|
break;
|
|
case 't':
|
|
{
|
|
initTransform(TFM_TIME_SLIDE, context);
|
|
Transform();
|
|
}
|
|
break;
|
|
case 'e':
|
|
{
|
|
initTransform(TFM_TIME_EXTEND, context);
|
|
Transform();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------- */
|
|
|
|
/* duplicate keyframes */
|
|
void duplicate_action_keys (void)
|
|
{
|
|
ListBase act_data = {NULL, NULL};
|
|
bActListElem *ale;
|
|
void *data;
|
|
short datatype;
|
|
int filter;
|
|
|
|
/* determine what type of data we are operating on */
|
|
data = get_action_context(&datatype);
|
|
if (data == NULL) return;
|
|
|
|
/* filter data */
|
|
if (datatype == ACTCONT_GPENCIL)
|
|
filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT);
|
|
else
|
|
filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS);
|
|
actdata_filter(&act_data, filter, data, datatype);
|
|
|
|
/* loop through filtered data and duplicate selected keys */
|
|
for (ale= act_data.first; ale; ale= ale->next) {
|
|
if (ale->type == ACTTYPE_GPLAYER)
|
|
duplicate_gplayer_frames(ale->data);
|
|
else
|
|
duplicate_ipo_keys((Ipo *)ale->key_data);
|
|
}
|
|
|
|
/* free filtered list */
|
|
BLI_freelistN(&act_data);
|
|
|
|
/* now, go into transform-grab mode, to move keys */
|
|
BIF_TransformSetUndo("Add Duplicate");
|
|
transform_action_keys('g', 0);
|
|
}
|
|
|
|
/* this function is responsible for snapping the current frame to selected data */
|
|
void snap_cfra_action()
|
|
{
|
|
ListBase act_data = {NULL, NULL};
|
|
bActListElem *ale;
|
|
int filter;
|
|
void *data;
|
|
short datatype;
|
|
|
|
/* get data */
|
|
data= get_action_context(&datatype);
|
|
if (data == NULL) return;
|
|
|
|
/* filter data */
|
|
filter= (ACTFILTER_VISIBLE | ACTFILTER_IPOKEYS);
|
|
actdata_filter(&act_data, filter, data, datatype);
|
|
|
|
/* snap current frame to selected data */
|
|
snap_cfra_ipo_keys(NULL, -1);
|
|
|
|
for (ale= act_data.first; ale; ale= ale->next) {
|
|
if (NLA_ACTION_SCALED && datatype==ACTCONT_ACTION) {
|
|
actstrip_map_ipo_keys(OBACT, ale->key_data, 0, 1);
|
|
snap_cfra_ipo_keys(ale->key_data, 0);
|
|
actstrip_map_ipo_keys(OBACT, ale->key_data, 1, 1);
|
|
}
|
|
else
|
|
snap_cfra_ipo_keys(ale->key_data, 0);
|
|
}
|
|
BLI_freelistN(&act_data);
|
|
|
|
snap_cfra_ipo_keys(NULL, 1);
|
|
|
|
BIF_undo_push("Snap Current Frame to Keys");
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWIPO, 0);
|
|
allqueue(REDRAWNLA, 0);
|
|
}
|
|
|
|
/* this function is responsible for snapping keyframes to frame-times */
|
|
void snap_action_keys(short mode)
|
|
{
|
|
ListBase act_data = {NULL, NULL};
|
|
bActListElem *ale;
|
|
int filter;
|
|
void *data;
|
|
short datatype;
|
|
char str[32];
|
|
|
|
/* get data */
|
|
data= get_action_context(&datatype);
|
|
if (data == NULL) return;
|
|
|
|
/* determine mode */
|
|
switch (mode) {
|
|
case 1:
|
|
strcpy(str, "Snap Keys To Nearest Frame");
|
|
break;
|
|
case 2:
|
|
if (G.saction->flag & SACTION_DRAWTIME)
|
|
strcpy(str, "Snap Keys To Current Time");
|
|
else
|
|
strcpy(str, "Snap Keys To Current Frame");
|
|
break;
|
|
case 3:
|
|
strcpy(str, "Snap Keys To Nearest Marker");
|
|
break;
|
|
case 4:
|
|
strcpy(str, "Snap Keys To Nearest Second");
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
/* filter data */
|
|
if (datatype == ACTCONT_GPENCIL)
|
|
filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT);
|
|
else
|
|
filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS);
|
|
actdata_filter(&act_data, filter, data, datatype);
|
|
|
|
/* snap to frame */
|
|
for (ale= act_data.first; ale; ale= ale->next) {
|
|
if (NLA_ACTION_SCALED && datatype==ACTCONT_ACTION) {
|
|
actstrip_map_ipo_keys(OBACT, ale->key_data, 0, 1);
|
|
snap_ipo_keys(ale->key_data, mode);
|
|
actstrip_map_ipo_keys(OBACT, ale->key_data, 1, 1);
|
|
}
|
|
else if (ale->type == ACTTYPE_GPLAYER)
|
|
snap_gplayer_frames(ale->data, mode);
|
|
else
|
|
snap_ipo_keys(ale->key_data, mode);
|
|
}
|
|
BLI_freelistN(&act_data);
|
|
|
|
if (datatype == ACTCONT_ACTION)
|
|
remake_action_ipos(data);
|
|
|
|
BIF_undo_push(str);
|
|
allspace(REMAKEIPO, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWIPO, 0);
|
|
allqueue(REDRAWNLA, 0);
|
|
allqueue(REDRAWVIEW3D, 0);
|
|
}
|
|
|
|
/* this function is responsible for snapping keyframes to frame-times */
|
|
void mirror_action_keys(short mode)
|
|
{
|
|
ListBase act_data = {NULL, NULL};
|
|
bActListElem *ale;
|
|
int filter;
|
|
void *data;
|
|
short datatype;
|
|
char str[32];
|
|
|
|
/* get data */
|
|
data= get_action_context(&datatype);
|
|
if (data == NULL) return;
|
|
|
|
/* determine mode */
|
|
switch (mode) {
|
|
case 1:
|
|
strcpy(str, "Mirror Keys Over Current Frame");
|
|
break;
|
|
case 2:
|
|
strcpy(str, "Mirror Keys Over Y-Axis");
|
|
break;
|
|
case 3:
|
|
strcpy(str, "Mirror Keys Over X-Axis");
|
|
break;
|
|
case 4:
|
|
strcpy(str, "Mirror Keys Over Marker");
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
/* filter data */
|
|
if (datatype == ACTCONT_GPENCIL)
|
|
filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT);
|
|
else
|
|
filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS);
|
|
actdata_filter(&act_data, filter, data, datatype);
|
|
|
|
/* mirror */
|
|
for (ale= act_data.first; ale; ale= ale->next) {
|
|
if (NLA_ACTION_SCALED && datatype==ACTCONT_ACTION) {
|
|
actstrip_map_ipo_keys(OBACT, ale->key_data, 0, 1);
|
|
mirror_ipo_keys(ale->key_data, mode);
|
|
actstrip_map_ipo_keys(OBACT, ale->key_data, 1, 1);
|
|
}
|
|
else if (ale->type == ACTTYPE_GPLAYER)
|
|
mirror_gplayer_frames(ale->data, mode);
|
|
else
|
|
mirror_ipo_keys(ale->key_data, mode);
|
|
}
|
|
BLI_freelistN(&act_data);
|
|
|
|
if (datatype == ACTCONT_ACTION)
|
|
remake_action_ipos(data);
|
|
|
|
BIF_undo_push(str);
|
|
allspace(REMAKEIPO, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWIPO, 0);
|
|
allqueue(REDRAWNLA, 0);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
/* ADD/REMOVE KEYFRAMES */
|
|
|
|
/* This function allows the user to insert keyframes on the current
|
|
* frame from the Action Editor, using the current values of the channels
|
|
* to be keyframed.
|
|
*/
|
|
void insertkey_action(void)
|
|
{
|
|
void *data;
|
|
short datatype;
|
|
|
|
Object *ob= OBACT;
|
|
short mode;
|
|
float cfra;
|
|
|
|
/* get data */
|
|
data= get_action_context(&datatype);
|
|
if (data == NULL) return;
|
|
cfra = frame_to_float(CFRA);
|
|
|
|
if (datatype == ACTCONT_ACTION) {
|
|
ListBase act_data = {NULL, NULL};
|
|
bActListElem *ale;
|
|
int filter;
|
|
|
|
/* ask user what to keyframe */
|
|
mode = pupmenu("Insert Key%t|All Channels%x1|Only Selected Channels%x2|In Active Group%x3");
|
|
if (mode <= 0) return;
|
|
|
|
/* filter data */
|
|
filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_ONLYICU );
|
|
if (mode == 2) filter |= ACTFILTER_SEL;
|
|
else if (mode == 3) filter |= ACTFILTER_ACTGROUPED;
|
|
|
|
actdata_filter(&act_data, filter, data, datatype);
|
|
|
|
/* loop through ipo curves retrieved */
|
|
for (ale= act_data.first; ale; ale= ale->next) {
|
|
/* verify that this is indeed an ipo curve */
|
|
if (ale->key_data && ale->owner) {
|
|
bActionChannel *achan= (bActionChannel *)ale->owner;
|
|
bConstraintChannel *conchan= (ale->type==ACTTYPE_CONCHAN) ? ale->data : NULL;
|
|
IpoCurve *icu= (IpoCurve *)ale->key_data;
|
|
|
|
if (ob)
|
|
insertkey((ID *)ob, icu->blocktype, achan->name, ((conchan)?(conchan->name):(NULL)), icu->adrcode, 0);
|
|
else
|
|
insert_vert_icu(icu, cfra, icu->curval, 0);
|
|
}
|
|
}
|
|
|
|
/* cleanup */
|
|
BLI_freelistN(&act_data);
|
|
}
|
|
else if (datatype == ACTCONT_SHAPEKEY) {
|
|
Key *key= (Key *)data;
|
|
IpoCurve *icu;
|
|
|
|
/* ask user if they want to insert a keyframe */
|
|
mode = okee("Insert Keyframe?");
|
|
if (mode <= 0) return;
|
|
|
|
if (key->ipo) {
|
|
for (icu= key->ipo->curve.first; icu; icu=icu->next) {
|
|
insert_vert_icu(icu, cfra, icu->curval, 0);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
/* this tool is not supported in this mode */
|
|
return;
|
|
}
|
|
|
|
BIF_undo_push("Insert Key");
|
|
allspace(REMAKEIPO, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWIPO, 0);
|
|
allqueue(REDRAWNLA, 0);
|
|
allqueue(REDRAWBUTSOBJECT, 0);
|
|
}
|
|
|
|
/* delete selected keyframes */
|
|
void delete_action_keys (void)
|
|
{
|
|
ListBase act_data = {NULL, NULL};
|
|
bActListElem *ale;
|
|
void *data;
|
|
short datatype;
|
|
int filter;
|
|
|
|
/* determine what type of data we are operating on */
|
|
data = get_action_context(&datatype);
|
|
if (data == NULL) return;
|
|
|
|
/* filter data */
|
|
if (datatype == ACTCONT_GPENCIL)
|
|
filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT);
|
|
else
|
|
filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS);
|
|
actdata_filter(&act_data, filter, data, datatype);
|
|
|
|
/* loop through filtered data and delete selected keys */
|
|
for (ale= act_data.first; ale; ale= ale->next) {
|
|
if (ale->type == ACTTYPE_GPLAYER)
|
|
delete_gplayer_frames((bGPDlayer *)ale->data);
|
|
else
|
|
delete_ipo_keys((Ipo *)ale->key_data);
|
|
}
|
|
|
|
/* free filtered list */
|
|
BLI_freelistN(&act_data);
|
|
|
|
if (datatype == ACTCONT_ACTION)
|
|
remake_action_ipos(data);
|
|
|
|
BIF_undo_push("Delete Action Keys");
|
|
allspace(REMAKEIPO, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWIPO, 0);
|
|
allqueue(REDRAWNLA, 0);
|
|
}
|
|
|
|
/* delete selected action-channels (only achans and conchans are considered) */
|
|
void delete_action_channels (void)
|
|
{
|
|
ListBase act_data = {NULL, NULL};
|
|
bActListElem *ale, *next;
|
|
bAction *act;
|
|
void *data;
|
|
short datatype;
|
|
int filter;
|
|
|
|
/* determine what type of data we are operating on */
|
|
data = get_action_context(&datatype);
|
|
if (data == NULL) return;
|
|
if (datatype != ACTCONT_ACTION) return;
|
|
act= (bAction *)data;
|
|
|
|
/* deal with groups first */
|
|
if (act->groups.first) {
|
|
bActionGroup *agrp, *grp;
|
|
bActionChannel *chan, *nchan;
|
|
|
|
/* unlink achan's that belonged to this group (and make sure they're not selected if they weren't visible) */
|
|
for (agrp= act->groups.first; agrp; agrp= grp) {
|
|
grp= agrp->next;
|
|
|
|
/* remove if group is selected */
|
|
if (SEL_AGRP(agrp)) {
|
|
for (chan= agrp->channels.first; chan && chan->grp==agrp; chan= nchan) {
|
|
nchan= chan->next;
|
|
|
|
action_groups_removeachan(act, chan);
|
|
BLI_addtail(&act->chanbase, chan);
|
|
|
|
if (EXPANDED_AGRP(agrp) == 0)
|
|
chan->flag &= ~(ACHAN_SELECTED|ACHAN_HILIGHTED);
|
|
}
|
|
|
|
BLI_freelinkN(&act->groups, agrp);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* filter data */
|
|
filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_CHANNELS | ACTFILTER_SEL);
|
|
actdata_filter(&act_data, filter, data, datatype);
|
|
|
|
/* remove irrelevant entries */
|
|
for (ale= act_data.first; ale; ale= next) {
|
|
next= ale->next;
|
|
|
|
if (ale->type != ACTTYPE_ACHAN)
|
|
BLI_freelinkN(&act_data, ale);
|
|
}
|
|
|
|
/* clean up action channels */
|
|
for (ale= act_data.first; ale; ale= next) {
|
|
bActionChannel *achan= (bActionChannel *)ale->data;
|
|
bConstraintChannel *conchan, *cnext;
|
|
next= ale->next;
|
|
|
|
/* release references to ipo users */
|
|
if (achan->ipo)
|
|
achan->ipo->id.us--;
|
|
|
|
for (conchan= achan->constraintChannels.first; conchan; conchan=cnext) {
|
|
cnext= conchan->next;
|
|
|
|
if (conchan->ipo)
|
|
conchan->ipo->id.us--;
|
|
}
|
|
|
|
/* remove action-channel from group(s) */
|
|
if (achan->grp)
|
|
action_groups_removeachan(act, achan);
|
|
|
|
/* free memory */
|
|
BLI_freelistN(&achan->constraintChannels);
|
|
BLI_freelinkN(&act->chanbase, achan);
|
|
BLI_freelinkN(&act_data, ale);
|
|
}
|
|
|
|
remake_action_ipos(data);
|
|
|
|
BIF_undo_push("Delete Action Channels");
|
|
allspace(REMAKEIPO, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWIPO, 0);
|
|
allqueue(REDRAWNLA, 0);
|
|
}
|
|
|
|
/* 'Clean' IPO curves - remove any unnecessary keyframes */
|
|
void clean_action (void)
|
|
{
|
|
ListBase act_data = {NULL, NULL};
|
|
bActListElem *ale;
|
|
int filter;
|
|
void *data;
|
|
short datatype, ok;
|
|
|
|
/* don't proceed any further if nothing to work on or user refuses */
|
|
data= get_action_context(&datatype);
|
|
ok= fbutton(&G.scene->toolsettings->clean_thresh,
|
|
0.0000001f, 1.0f, 0.001f, 0.1f,
|
|
"Clean Threshold");
|
|
if (!ok) return;
|
|
if (datatype == ACTCONT_GPENCIL) return;
|
|
|
|
/* filter data */
|
|
filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_SEL | ACTFILTER_ONLYICU);
|
|
actdata_filter(&act_data, filter, data, datatype);
|
|
|
|
/* loop through filtered data and clean curves */
|
|
for (ale= act_data.first; ale; ale= ale->next) {
|
|
clean_ipo_curve((IpoCurve *)ale->key_data);
|
|
}
|
|
|
|
/* admin and redraws */
|
|
BLI_freelistN(&act_data);
|
|
|
|
BIF_undo_push("Clean Action");
|
|
allqueue(REMAKEIPO, 0);
|
|
allqueue(REDRAWIPO, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWNLA, 0);
|
|
}
|
|
|
|
|
|
/* little cache for values... */
|
|
typedef struct tempFrameValCache {
|
|
float frame, val;
|
|
} tempFrameValCache;
|
|
|
|
/* Evaluates the curves between each selected keyframe on each frame, and keys the value */
|
|
void sample_action_keys (void)
|
|
{
|
|
ListBase act_data = {NULL, NULL};
|
|
bActListElem *ale;
|
|
int filter;
|
|
void *data;
|
|
short datatype;
|
|
|
|
/* sanity checks */
|
|
data= get_action_context(&datatype);
|
|
if (data == NULL) return;
|
|
if (datatype == ACTCONT_GPENCIL) return;
|
|
|
|
/* filter data */
|
|
filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_ONLYICU);
|
|
actdata_filter(&act_data, filter, data, datatype);
|
|
|
|
/* loop through filtered data and add keys between selected keyframes on every frame */
|
|
for (ale= act_data.first; ale; ale= ale->next) {
|
|
IpoCurve *icu= (IpoCurve *)ale->key_data;
|
|
BezTriple *bezt, *start=NULL, *end=NULL;
|
|
tempFrameValCache *value_cache, *fp;
|
|
int sfra, range;
|
|
int i, n;
|
|
|
|
/* find selected keyframes... once pair has been found, add keyframes */
|
|
for (i=0, bezt=icu->bezt; i < icu->totvert; i++, bezt++) {
|
|
/* check if selected, and which end this is */
|
|
if (BEZSELECTED(bezt)) {
|
|
if (start) {
|
|
/* set end */
|
|
end= bezt;
|
|
|
|
/* cache values then add keyframes using these values, as adding
|
|
* keyframes while sampling will affect the outcome...
|
|
*/
|
|
range= (int)( ceil(end->vec[1][0] - start->vec[1][0]) );
|
|
sfra= (int)( floor(start->vec[1][0]) );
|
|
|
|
if (range) {
|
|
value_cache= MEM_callocN(sizeof(tempFrameValCache)*range, "IcuFrameValCache");
|
|
|
|
/* sample values */
|
|
for (n=0, fp=value_cache; n<range && fp; n++, fp++) {
|
|
fp->frame= (float)(sfra + n);
|
|
fp->val= eval_icu(icu, fp->frame);
|
|
}
|
|
|
|
/* add keyframes with these */
|
|
for (n=0, fp=value_cache; n<range && fp; n++, fp++) {
|
|
insert_vert_icu(icu, fp->frame, fp->val, 1);
|
|
}
|
|
|
|
/* free temp cache */
|
|
MEM_freeN(value_cache);
|
|
|
|
/* as we added keyframes, we need to compensate so that bezt is at the right place */
|
|
bezt = icu->bezt + i + range - 1;
|
|
i += (range - 1);
|
|
}
|
|
|
|
/* bezt was selected, so it now marks the start of a whole new chain to search */
|
|
start= bezt;
|
|
end= NULL;
|
|
}
|
|
else {
|
|
/* just set start keyframe */
|
|
start= bezt;
|
|
end= NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* recalculate channel's handles? */
|
|
calchandles_ipocurve(icu);
|
|
}
|
|
|
|
/* admin and redraws */
|
|
BLI_freelistN(&act_data);
|
|
|
|
BIF_undo_push("Sample Action Keys");
|
|
allqueue(REMAKEIPO, 0);
|
|
allqueue(REDRAWIPO, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWNLA, 0);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
/* COPY/PASTE FOR ACTIONS */
|
|
/* - The copy/paste buffer currently stores a set of Action Channels, with temporary
|
|
* IPO-blocks, and also temporary IpoCurves which only contain the selected keyframes.
|
|
* - Only pastes between compatable data is possible (i.e. same achan->name, ipo-curve type, etc.)
|
|
* Unless there is only one element in the buffer, names are also tested to check for compatability.
|
|
* - All pasted frames are offset by the same amount. This is calculated as the difference in the times of
|
|
* the current frame and the 'first keyframe' (i.e. the earliest one in all channels).
|
|
* - The earliest frame is calculated per copy operation.
|
|
*/
|
|
|
|
/* globals for copy/paste data (like for other copy/paste buffers) */
|
|
ListBase actcopybuf = {NULL, NULL};
|
|
static float actcopy_firstframe= 999999999.0f;
|
|
|
|
/* This function frees any MEM_calloc'ed copy/paste buffer data */
|
|
void free_actcopybuf ()
|
|
{
|
|
bActionChannel *achan, *anext;
|
|
bConstraintChannel *conchan, *cnext;
|
|
|
|
for (achan= actcopybuf.first; achan; achan= anext) {
|
|
anext= achan->next;
|
|
|
|
if (achan->ipo) {
|
|
free_ipo(achan->ipo);
|
|
MEM_freeN(achan->ipo);
|
|
}
|
|
|
|
for (conchan=achan->constraintChannels.first; conchan; conchan=cnext) {
|
|
cnext= conchan->next;
|
|
|
|
if (conchan->ipo) {
|
|
free_ipo(conchan->ipo);
|
|
MEM_freeN(conchan->ipo);
|
|
}
|
|
|
|
BLI_freelinkN(&achan->constraintChannels, conchan);
|
|
}
|
|
|
|
BLI_freelinkN(&actcopybuf, achan);
|
|
}
|
|
|
|
actcopybuf.first= actcopybuf.last= NULL;
|
|
actcopy_firstframe= 999999999.0f;
|
|
}
|
|
|
|
/* This function adds data to the copy/paste buffer, freeing existing data first
|
|
* Only the selected action channels gets their selected keyframes copied.
|
|
*/
|
|
void copy_actdata ()
|
|
{
|
|
ListBase act_data = {NULL, NULL};
|
|
bActListElem *ale;
|
|
int filter;
|
|
void *data;
|
|
short datatype;
|
|
|
|
/* clear buffer first */
|
|
free_actcopybuf();
|
|
|
|
/* get data */
|
|
data= get_action_context(&datatype);
|
|
if (data == NULL) return;
|
|
|
|
/* filter data */
|
|
filter= (ACTFILTER_VISIBLE | ACTFILTER_SEL | ACTFILTER_IPOKEYS);
|
|
actdata_filter(&act_data, filter, data, datatype);
|
|
|
|
/* assume that each of these is an ipo-block */
|
|
for (ale= act_data.first; ale; ale= ale->next) {
|
|
bActionChannel *achan;
|
|
Ipo *ipo= ale->key_data;
|
|
Ipo *ipn;
|
|
IpoCurve *icu, *icn;
|
|
BezTriple *bezt;
|
|
int i;
|
|
|
|
/* coerce an action-channel out of owner */
|
|
if (ale->ownertype == ACTTYPE_ACHAN) {
|
|
bActionChannel *achanO= ale->owner;
|
|
achan= MEM_callocN(sizeof(bActionChannel), "ActCopyPasteAchan");
|
|
strcpy(achan->name, achanO->name);
|
|
}
|
|
else if (ale->ownertype == ACTTYPE_SHAPEKEY) {
|
|
achan= MEM_callocN(sizeof(bActionChannel), "ActCopyPasteAchan");
|
|
strcpy(achan->name, "#ACP_ShapeKey");
|
|
}
|
|
else
|
|
continue;
|
|
BLI_addtail(&actcopybuf, achan);
|
|
|
|
/* add constraint channel if needed, then add new ipo-block */
|
|
if (ale->type == ACTTYPE_CONCHAN) {
|
|
bConstraintChannel *conchanO= ale->data;
|
|
bConstraintChannel *conchan;
|
|
|
|
conchan= MEM_callocN(sizeof(bConstraintChannel), "ActCopyPasteConchan");
|
|
strcpy(conchan->name, conchanO->name);
|
|
BLI_addtail(&achan->constraintChannels, conchan);
|
|
|
|
conchan->ipo= ipn= MEM_callocN(sizeof(Ipo), "ActCopyPasteIpo");
|
|
}
|
|
else {
|
|
achan->ipo= ipn= MEM_callocN(sizeof(Ipo), "ActCopyPasteIpo");
|
|
}
|
|
ipn->blocktype = ipo->blocktype;
|
|
|
|
/* now loop through curves, and only copy selected keyframes */
|
|
for (icu= ipo->curve.first; icu; icu= icu->next) {
|
|
/* allocate a new curve */
|
|
icn= MEM_callocN(sizeof(IpoCurve), "ActCopyPasteIcu");
|
|
icn->blocktype = icu->blocktype;
|
|
icn->adrcode = icu->adrcode;
|
|
BLI_addtail(&ipn->curve, icn);
|
|
|
|
/* find selected BezTriples to add to the buffer (and set first frame) */
|
|
for (i=0, bezt=icu->bezt; i < icu->totvert; i++, bezt++) {
|
|
if (BEZSELECTED(bezt)) {
|
|
/* add to buffer ipo-curve */
|
|
insert_bezt_icu(icn, bezt);
|
|
|
|
/* check if this is the earliest frame encountered so far */
|
|
if (bezt->vec[1][0] < actcopy_firstframe)
|
|
actcopy_firstframe= bezt->vec[1][0];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* check if anything ended up in the buffer */
|
|
if (ELEM(NULL, actcopybuf.first, actcopybuf.last))
|
|
error("Nothing copied to buffer");
|
|
|
|
/* free temp memory */
|
|
BLI_freelistN(&act_data);
|
|
}
|
|
|
|
void paste_actdata ()
|
|
{
|
|
ListBase act_data = {NULL, NULL};
|
|
bActListElem *ale;
|
|
int filter;
|
|
void *data;
|
|
short datatype;
|
|
Object *ob= OBACT;
|
|
|
|
const float offset = (float)(CFRA - actcopy_firstframe);
|
|
char *actname = NULL, *conname = NULL;
|
|
short no_name= 0;
|
|
|
|
/* check if buffer is empty */
|
|
if (ELEM(NULL, actcopybuf.first, actcopybuf.last)) {
|
|
error("No data in buffer to paste");
|
|
return;
|
|
}
|
|
/* check if single channel in buffer (disregard names if so) */
|
|
if (actcopybuf.first == actcopybuf.last)
|
|
no_name= 1;
|
|
|
|
/* get data */
|
|
data= get_action_context(&datatype);
|
|
if (data == NULL) return;
|
|
|
|
/* filter data */
|
|
filter= (ACTFILTER_VISIBLE | ACTFILTER_SEL | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS);
|
|
actdata_filter(&act_data, filter, data, datatype);
|
|
|
|
/* from selected channels */
|
|
for (ale= act_data.first; ale; ale= ale->next) {
|
|
Ipo *ipo_src = NULL;
|
|
bActionChannel *achan;
|
|
IpoCurve *ico, *icu;
|
|
BezTriple *bezt;
|
|
int i;
|
|
|
|
/* find suitable IPO-block from buffer to paste from */
|
|
for (achan= actcopybuf.first; achan; achan= achan->next) {
|
|
/* try to match data */
|
|
if (ale->ownertype == ACTTYPE_ACHAN) {
|
|
bActionChannel *achant= ale->owner;
|
|
|
|
/* check if we have a corresponding action channel */
|
|
if ((no_name) || (strcmp(achan->name, achant->name)==0)) {
|
|
actname= achant->name;
|
|
|
|
/* check if this is a constraint channel */
|
|
if (ale->type == ACTTYPE_CONCHAN) {
|
|
bConstraintChannel *conchant= ale->data;
|
|
bConstraintChannel *conchan;
|
|
|
|
for (conchan=achan->constraintChannels.first; conchan; conchan=conchan->next) {
|
|
if (strcmp(conchan->name, conchant->name)==0) {
|
|
conname= conchant->name;
|
|
ipo_src= conchan->ipo;
|
|
break;
|
|
}
|
|
}
|
|
if (ipo_src) break;
|
|
}
|
|
else {
|
|
ipo_src= achan->ipo;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (ale->ownertype == ACTTYPE_SHAPEKEY) {
|
|
/* check if this action channel is "#ACP_ShapeKey" */
|
|
if ((no_name) || (strcmp(achan->name, "#ACP_ShapeKey")==0)) {
|
|
actname= NULL;
|
|
ipo_src= achan->ipo;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* this shouldn't happen, but it might */
|
|
if (ipo_src == NULL)
|
|
continue;
|
|
|
|
/* loop over curves, pasting keyframes */
|
|
for (ico= ipo_src->curve.first; ico; ico= ico->next) {
|
|
/* get IPO-curve to paste to (IPO-curve might not exist for destination, so gets created) */
|
|
icu= verify_ipocurve((ID *)ob, ico->blocktype, actname, conname, NULL, ico->adrcode, 1);
|
|
|
|
if (icu) {
|
|
/* just start pasting, with the the first keyframe on the current frame, and so on */
|
|
for (i=0, bezt=ico->bezt; i < ico->totvert; i++, bezt++) {
|
|
/* temporarily apply offset to src beztriple while copying */
|
|
bezt->vec[0][0] += offset;
|
|
bezt->vec[1][0] += offset;
|
|
bezt->vec[2][0] += offset;
|
|
|
|
/* insert the keyframe */
|
|
insert_bezt_icu(icu, bezt);
|
|
|
|
/* un-apply offset from src beztriple after copying */
|
|
bezt->vec[0][0] -= offset;
|
|
bezt->vec[1][0] -= offset;
|
|
bezt->vec[2][0] -= offset;
|
|
}
|
|
|
|
/* recalculate channel's handles? */
|
|
calchandles_ipocurve(icu);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* free temp memory */
|
|
BLI_freelistN(&act_data);
|
|
|
|
/* do depsgraph updates (for 3d-view)? */
|
|
if ((ob) && (G.saction->pin==0)) {
|
|
if (ob->type == OB_ARMATURE)
|
|
DAG_object_flush_update(G.scene, ob, OB_RECALC_OB|OB_RECALC_DATA);
|
|
else
|
|
DAG_object_flush_update(G.scene, ob, OB_RECALC_OB);
|
|
}
|
|
|
|
/* undo and redraw stuff */
|
|
allqueue(REDRAWVIEW3D, 0);
|
|
allspace(REMAKEIPO, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWIPO, 0);
|
|
allqueue(REDRAWNLA, 0);
|
|
BIF_undo_push("Paste Action Keyframes");
|
|
}
|
|
|
|
/* **************************************************** */
|
|
/* VARIOUS SETTINGS */
|
|
|
|
/* This function combines several features related to setting
|
|
* various ipo extrapolation/interpolation
|
|
*/
|
|
void action_set_ipo_flags (short mode, short event)
|
|
{
|
|
ListBase act_data = {NULL, NULL};
|
|
bActListElem *ale;
|
|
void *data;
|
|
short datatype;
|
|
int filter;
|
|
|
|
/* determine what type of data we are operating on */
|
|
data = get_action_context(&datatype);
|
|
if (data == NULL) return;
|
|
if (datatype == ACTCONT_GPENCIL) return;
|
|
|
|
/* determine which set of processing we are doing */
|
|
switch (mode) {
|
|
case SET_EXTEND_POPUP:
|
|
{
|
|
/* present popup menu for ipo extrapolation type */
|
|
event
|
|
= pupmenu("Channel Extending Type %t|"
|
|
"Constant %x11|"
|
|
"Extrapolation %x12|"
|
|
"Cyclic %x13|"
|
|
"Cyclic extrapolation %x14");
|
|
if (event < 1) return;
|
|
}
|
|
break;
|
|
case SET_IPO_POPUP:
|
|
{
|
|
/* present popup menu for ipo interpolation type */
|
|
event
|
|
= pupmenu("Channel Ipo Type %t|"
|
|
"Constant %x1|"
|
|
"Linear %x2|"
|
|
"Bezier %x3");
|
|
if (event < 1) return;
|
|
}
|
|
break;
|
|
|
|
case SET_IPO_MENU: /* called from menus */
|
|
case SET_EXTEND_MENU:
|
|
break;
|
|
|
|
default: /* weird, unhandled case */
|
|
return;
|
|
}
|
|
|
|
/* filter data */
|
|
filter= (ACTFILTER_VISIBLE | ACTFILTER_SEL | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS);
|
|
actdata_filter(&act_data, filter, data, datatype);
|
|
|
|
/* loop through setting flags */
|
|
for (ale= act_data.first; ale; ale= ale->next) {
|
|
Ipo *ipo= (Ipo *)ale->key_data;
|
|
|
|
/* depending on the mode */
|
|
switch (mode) {
|
|
case SET_EXTEND_POPUP: /* extrapolation */
|
|
case SET_EXTEND_MENU:
|
|
{
|
|
switch (event) {
|
|
case SET_EXTEND_CONSTANT:
|
|
setexprap_ipoloop(ipo, IPO_HORIZ);
|
|
break;
|
|
case SET_EXTEND_EXTRAPOLATION:
|
|
setexprap_ipoloop(ipo, IPO_DIR);
|
|
break;
|
|
case SET_EXTEND_CYCLIC:
|
|
setexprap_ipoloop(ipo, IPO_CYCL);
|
|
break;
|
|
case SET_EXTEND_CYCLICEXTRAPOLATION:
|
|
setexprap_ipoloop(ipo, IPO_CYCLX);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case SET_IPO_POPUP: /* interpolation */
|
|
case SET_IPO_MENU:
|
|
{
|
|
setipotype_ipo(ipo, event);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* cleanup */
|
|
BLI_freelistN(&act_data);
|
|
|
|
if (datatype == ACTCONT_ACTION)
|
|
remake_action_ipos(data);
|
|
|
|
BIF_undo_push("Set Ipo Type");
|
|
allspace(REMAKEIPO, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWIPO, 0);
|
|
allqueue(REDRAWNLA, 0);
|
|
}
|
|
|
|
/* this function sets the handles on keyframes */
|
|
void sethandles_action_keys (int code)
|
|
{
|
|
ListBase act_data = {NULL, NULL};
|
|
bActListElem *ale;
|
|
void *data;
|
|
short datatype;
|
|
int filter;
|
|
|
|
/* determine what type of data we are operating on */
|
|
data = get_action_context(&datatype);
|
|
if (data == NULL) return;
|
|
if (datatype == ACTCONT_GPENCIL) return;
|
|
|
|
/* filter data */
|
|
filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS);
|
|
actdata_filter(&act_data, filter, data, datatype);
|
|
|
|
/* loop through setting flags */
|
|
for (ale= act_data.first; ale; ale= ale->next) {
|
|
sethandles_ipo_keys((Ipo *)ale->key_data, code);
|
|
}
|
|
|
|
/* cleanup */
|
|
BLI_freelistN(&act_data);
|
|
if (datatype == ACTCONT_ACTION)
|
|
remake_action_ipos(data);
|
|
|
|
BIF_undo_push("Set Handle Type");
|
|
allspace(REMAKEIPO, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWIPO, 0);
|
|
allqueue(REDRAWNLA, 0);
|
|
}
|
|
|
|
/* ----------------------------------------- */
|
|
|
|
/* this gets called when nkey is pressed (no Transform Properties panel yet) */
|
|
static void numbuts_action ()
|
|
{
|
|
void *data;
|
|
short datatype;
|
|
|
|
void *act_channel, *channel_owner;
|
|
short chantype;
|
|
|
|
bActionGroup *agrp= NULL;
|
|
bActionChannel *achan= NULL;
|
|
bConstraintChannel *conchan= NULL;
|
|
IpoCurve *icu= NULL;
|
|
KeyBlock *kb= NULL;
|
|
bGPdata *gpd= NULL;
|
|
bGPDlayer *gpl= NULL;
|
|
|
|
short mval[2];
|
|
|
|
int but=0;
|
|
char str[128];
|
|
short expand, protect, mute;
|
|
float slidermin, slidermax;
|
|
|
|
|
|
/* determine what type of data we are operating on */
|
|
data = get_action_context(&datatype);
|
|
if (data == NULL) return;
|
|
|
|
/* figure out what is under cursor */
|
|
getmouseco_areawin(mval);
|
|
if (mval[0] > NAMEWIDTH)
|
|
return;
|
|
act_channel= get_nearest_act_channel(mval, &chantype, &channel_owner);
|
|
|
|
/* create items for clever-numbut */
|
|
if (chantype == ACTTYPE_ACHAN) {
|
|
/* Action Channel */
|
|
achan= (bActionChannel *)act_channel;
|
|
|
|
strcpy(str, achan->name);
|
|
protect= (achan->flag & ACHAN_PROTECTED);
|
|
expand = (achan->flag & ACHAN_EXPANDED);
|
|
mute = (achan->ipo)? (achan->ipo->muteipo): 0;
|
|
|
|
add_numbut(but++, TEX, "ActChan: ", 0, 31, str, "Name of Action Channel");
|
|
add_numbut(but++, TOG|SHO, "Expanded", 0, 24, &expand, "Action Channel is Expanded");
|
|
add_numbut(but++, TOG|SHO, "Muted", 0, 24, &mute, "Channel is Muted");
|
|
add_numbut(but++, TOG|SHO, "Protected", 0, 24, &protect, "Channel is Protected");
|
|
}
|
|
else if (chantype == ACTTYPE_CONCHAN) {
|
|
/* Constraint Channel */
|
|
conchan= (bConstraintChannel *)act_channel;
|
|
|
|
strcpy(str, conchan->name);
|
|
protect= (conchan->flag & CONSTRAINT_CHANNEL_PROTECTED);
|
|
mute = (conchan->ipo)? (conchan->ipo->muteipo): 0;
|
|
|
|
add_numbut(but++, TEX, "ConChan: ", 0, 29, str, "Name of Constraint Channel");
|
|
add_numbut(but++, TOG|SHO, "Muted", 0, 24, &mute, "Channel is Muted");
|
|
add_numbut(but++, TOG|SHO, "Protected", 0, 24, &protect, "Channel is Protected");
|
|
}
|
|
else if (chantype == ACTTYPE_ICU) {
|
|
/* IPO Curve */
|
|
icu= (IpoCurve *)act_channel;
|
|
|
|
if (G.saction->pin)
|
|
sprintf(str, getname_ipocurve(icu, NULL));
|
|
else
|
|
sprintf(str, getname_ipocurve(icu, OBACT));
|
|
|
|
if (IS_EQ(icu->slide_max, icu->slide_min)) {
|
|
if (IS_EQ(icu->ymax, icu->ymin)) {
|
|
icu->slide_min= -100.0;
|
|
icu->slide_max= 100.0;
|
|
}
|
|
else {
|
|
icu->slide_min= icu->ymin;
|
|
icu->slide_max= icu->ymax;
|
|
}
|
|
}
|
|
slidermin= icu->slide_min;
|
|
slidermax= icu->slide_max;
|
|
|
|
//protect= (icu->flag & IPO_PROTECT);
|
|
mute = (icu->flag & IPO_MUTE);
|
|
|
|
add_numbut(but++, NUM|FLO, "Slider Min:", -10000, slidermax, &slidermin, 0);
|
|
add_numbut(but++, NUM|FLO, "Slider Max:", slidermin, 10000, &slidermax, 0);
|
|
add_numbut(but++, TOG|SHO, "Muted", 0, 24, &mute, "Channel is Muted");
|
|
//add_numbut(but++, TOG|SHO, "Protected", 0, 24, &protect, "Channel is Protected");
|
|
}
|
|
else if (chantype == ACTTYPE_SHAPEKEY) {
|
|
/* Shape Key */
|
|
kb= (KeyBlock *)act_channel;
|
|
|
|
if (kb->name[0] == '\0') {
|
|
Key *key= (Key *)data;
|
|
int keynum= BLI_findindex(&key->block, kb);
|
|
|
|
sprintf(str, "Key %d", keynum);
|
|
}
|
|
else
|
|
strcpy(str, kb->name);
|
|
|
|
if (kb->slidermin >= kb->slidermax) {
|
|
kb->slidermin = 0.0;
|
|
kb->slidermax = 1.0;
|
|
}
|
|
|
|
add_numbut(but++, TEX, "KB: ", 0, 24, str,
|
|
"Does this really need a tool tip?");
|
|
add_numbut(but++, NUM|FLO, "Slider Min:",
|
|
-10000, kb->slidermax, &kb->slidermin, 0);
|
|
add_numbut(but++, NUM|FLO, "Slider Max:",
|
|
kb->slidermin, 10000, &kb->slidermax, 0);
|
|
}
|
|
else if (chantype == ACTTYPE_GROUP) {
|
|
/* Action Group */
|
|
agrp= (bActionGroup *)act_channel;
|
|
|
|
strcpy(str, agrp->name);
|
|
protect= (agrp->flag & AGRP_PROTECTED);
|
|
expand = (agrp->flag & AGRP_EXPANDED);
|
|
|
|
add_numbut(but++, TEX, "ActGroup: ", 0, 31, str, "Name of Action Group");
|
|
add_numbut(but++, TOG|SHO, "Expanded", 0, 24, &expand, "Action Group is Expanded");
|
|
add_numbut(but++, TOG|SHO, "Protected", 0, 24, &protect, "Group is Protected");
|
|
}
|
|
else if (chantype == ACTTYPE_GPLAYER) {
|
|
/* Grease-Pencil Layer */
|
|
gpd= (bGPdata *)channel_owner;
|
|
gpl= (bGPDlayer *)act_channel;
|
|
|
|
strcpy(str, gpl->info);
|
|
protect= (gpl->flag & GP_LAYER_LOCKED);
|
|
mute = (gpl->flag & GP_LAYER_HIDE);
|
|
|
|
add_numbut(but++, TEX, "GP-Layer: ", 0, 128, str, "Name of Grease Pencil Layer");
|
|
add_numbut(but++, TOG|SHO, "Hide", 0, 24, &mute, "Grease Pencil Layer is Visible");
|
|
add_numbut(but++, TOG|SHO, "Protected", 0, 24, &protect, "Grease Pencil Layer is Protected");
|
|
}
|
|
else {
|
|
/* nothing under-cursor */
|
|
return;
|
|
}
|
|
|
|
/* draw clever-numbut */
|
|
if (do_clever_numbuts(str, but, REDRAW)) {
|
|
/* restore settings based on type */
|
|
if (icu) {
|
|
icu->slide_min= slidermin;
|
|
icu->slide_max= slidermax;
|
|
|
|
//if (protect) icu->flag |= IPO_PROTECT;
|
|
//else icu->flag &= ~IPO_PROTECT;
|
|
if (mute) icu->flag |= IPO_MUTE;
|
|
else icu->flag &= ~IPO_MUTE;
|
|
}
|
|
else if (conchan) {
|
|
strcpy(conchan->name, str);
|
|
|
|
if (protect) conchan->flag |= CONSTRAINT_CHANNEL_PROTECTED;
|
|
else conchan->flag &= ~CONSTRAINT_CHANNEL_PROTECTED;
|
|
|
|
if (conchan->ipo)
|
|
conchan->ipo->muteipo = mute;
|
|
}
|
|
else if (achan) {
|
|
strcpy(achan->name, str);
|
|
|
|
if (expand) achan->flag |= ACHAN_EXPANDED;
|
|
else achan->flag &= ~ACHAN_EXPANDED;
|
|
|
|
if (protect) achan->flag |= ACHAN_PROTECTED;
|
|
else achan->flag &= ~ACHAN_PROTECTED;
|
|
|
|
if (achan->ipo)
|
|
achan->ipo->muteipo = mute;
|
|
}
|
|
else if (agrp) {
|
|
strcpy(agrp->name, str);
|
|
BLI_uniquename(&( ((bAction *)data)->groups ), agrp, "Group", offsetof(bActionGroup, name), 32);
|
|
|
|
if (expand) agrp->flag |= AGRP_EXPANDED;
|
|
else agrp->flag &= ~AGRP_EXPANDED;
|
|
|
|
if (protect) agrp->flag |= AGRP_PROTECTED;
|
|
else agrp->flag &= ~AGRP_PROTECTED;
|
|
}
|
|
else if (gpl) {
|
|
strcpy(gpl->info, str);
|
|
BLI_uniquename(&gpd->layers, gpl, "GP_Layer", offsetof(bGPDlayer, info), 128);
|
|
|
|
if (mute) gpl->flag |= GP_LAYER_HIDE;
|
|
else gpl->flag &= ~GP_LAYER_HIDE;;
|
|
|
|
if (protect) gpl->flag |= GP_LAYER_LOCKED;
|
|
else gpl->flag &= ~GP_LAYER_LOCKED;
|
|
}
|
|
|
|
allqueue(REDRAWACTION, 0);
|
|
allspace(REMAKEIPO, 0);
|
|
allqueue(REDRAWIPO, 0);
|
|
allqueue(REDRAWVIEW3D, 0);
|
|
}
|
|
}
|
|
|
|
/* Set/clear a particular flag (setting) for all selected + visible channels
|
|
* mode: 0 = toggle, 1 = turn on, 2 = turn off
|
|
*/
|
|
void setflag_action_channels (short mode)
|
|
{
|
|
ListBase act_data = {NULL, NULL};
|
|
bActListElem *ale;
|
|
int filter;
|
|
void *data;
|
|
short datatype;
|
|
char str[32];
|
|
short val;
|
|
|
|
/* get data */
|
|
data= get_action_context(&datatype);
|
|
if (data == NULL) return;
|
|
|
|
/* get setting to affect */
|
|
if (mode == 2) {
|
|
val= pupmenu("Disable Setting%t|Protect %x1|Mute%x2");
|
|
sprintf(str, "Disable Action Setting");
|
|
}
|
|
else if (mode == 1) {
|
|
val= pupmenu("Enable Setting%t|Protect %x1|Mute%x2");
|
|
sprintf(str, "Enable Action Setting");
|
|
}
|
|
else {
|
|
val= pupmenu("Toggle Setting%t|Protect %x1|Mute%x2");
|
|
sprintf(str, "Toggle Action Setting");
|
|
}
|
|
if (val <= 0) return;
|
|
|
|
/* filter data */
|
|
filter= (ACTFILTER_VISIBLE | ACTFILTER_CHANNELS | ACTFILTER_SEL);
|
|
actdata_filter(&act_data, filter, data, datatype);
|
|
|
|
/* affect selected channels */
|
|
for (ale= act_data.first; ale; ale= ale->next) {
|
|
switch (ale->type) {
|
|
case ACTTYPE_GROUP:
|
|
{
|
|
bActionGroup *agrp= (bActionGroup *)ale->data;
|
|
|
|
/* only 'protect' is available */
|
|
if (val == 1) {
|
|
if (mode == 2)
|
|
agrp->flag &= ~AGRP_PROTECTED;
|
|
else if (mode == 1)
|
|
agrp->flag |= AGRP_PROTECTED;
|
|
else
|
|
agrp->flag ^= AGRP_PROTECTED;
|
|
}
|
|
}
|
|
break;
|
|
case ACTTYPE_ACHAN:
|
|
{
|
|
bActionChannel *achan= (bActionChannel *)ale->data;
|
|
|
|
/* 'protect' and 'mute' */
|
|
if ((val == 2) && (achan->ipo)) {
|
|
Ipo *ipo= achan->ipo;
|
|
|
|
/* mute */
|
|
if (mode == 2)
|
|
ipo->muteipo= 0;
|
|
else if (mode == 1)
|
|
ipo->muteipo= 1;
|
|
else
|
|
ipo->muteipo= (ipo->muteipo) ? 0 : 1;
|
|
}
|
|
else if (val == 1) {
|
|
/* protected */
|
|
if (mode == 2)
|
|
achan->flag &= ~ACHAN_PROTECTED;
|
|
else if (mode == 1)
|
|
achan->flag |= ACHAN_PROTECTED;
|
|
else
|
|
achan->flag ^= ACHAN_PROTECTED;
|
|
}
|
|
}
|
|
break;
|
|
case ACTTYPE_CONCHAN:
|
|
{
|
|
bConstraintChannel *conchan= (bConstraintChannel *)ale->data;
|
|
|
|
/* 'protect' and 'mute' */
|
|
if ((val == 2) && (conchan->ipo)) {
|
|
Ipo *ipo= conchan->ipo;
|
|
|
|
/* mute */
|
|
if (mode == 2)
|
|
ipo->muteipo= 0;
|
|
else if (mode == 1)
|
|
ipo->muteipo= 1;
|
|
else
|
|
ipo->muteipo= (ipo->muteipo) ? 0 : 1;
|
|
}
|
|
else if (val == 1) {
|
|
/* protect */
|
|
if (mode == 2)
|
|
conchan->flag &= ~CONSTRAINT_CHANNEL_PROTECTED;
|
|
else if (mode == 1)
|
|
conchan->flag |= CONSTRAINT_CHANNEL_PROTECTED;
|
|
else
|
|
conchan->flag ^= CONSTRAINT_CHANNEL_PROTECTED;
|
|
}
|
|
}
|
|
break;
|
|
case ACTTYPE_ICU:
|
|
{
|
|
IpoCurve *icu= (IpoCurve *)ale->data;
|
|
|
|
/* mute */
|
|
if (val == 2) {
|
|
if (mode == 2)
|
|
icu->flag &= ~IPO_MUTE;
|
|
else if (mode == 1)
|
|
icu->flag |= IPO_MUTE;
|
|
else
|
|
icu->flag ^= IPO_MUTE;
|
|
}
|
|
}
|
|
break;
|
|
case ACTTYPE_GPLAYER:
|
|
{
|
|
bGPDlayer *gpl= (bGPDlayer *)ale->data;
|
|
|
|
/* 'protect' and 'mute' */
|
|
if (val == 2) {
|
|
/* mute */
|
|
if (mode == 2)
|
|
gpl->flag &= ~GP_LAYER_HIDE;
|
|
else if (mode == 1)
|
|
gpl->flag |= GP_LAYER_HIDE;
|
|
else
|
|
gpl->flag ^= GP_LAYER_HIDE;
|
|
}
|
|
else if (val == 1) {
|
|
/* protected */
|
|
if (mode == 2)
|
|
gpl->flag &= ~GP_LAYER_LOCKED;
|
|
else if (mode == 1)
|
|
gpl->flag |= GP_LAYER_LOCKED;
|
|
else
|
|
gpl->flag ^= GP_LAYER_LOCKED;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
BLI_freelistN(&act_data);
|
|
|
|
BIF_undo_push(str);
|
|
allspace(REMAKEIPO, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWIPO, 0);
|
|
allqueue(REDRAWNLA, 0);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
/* CHANNEL SELECTION */
|
|
|
|
/* select_mode = SELECT_REPLACE
|
|
* = SELECT_ADD
|
|
* = SELECT_SUBTRACT
|
|
* = SELECT_INVERT
|
|
*/
|
|
static void select_action_group (bAction *act, bActionGroup *agrp, int selectmode)
|
|
{
|
|
/* Select the channel based on the selection mode */
|
|
short select;
|
|
|
|
switch (selectmode) {
|
|
case SELECT_ADD:
|
|
agrp->flag |= AGRP_SELECTED;
|
|
break;
|
|
case SELECT_SUBTRACT:
|
|
agrp->flag &= ~AGRP_SELECTED;
|
|
break;
|
|
case SELECT_INVERT:
|
|
agrp->flag ^= AGRP_SELECTED;
|
|
break;
|
|
}
|
|
select = (agrp->flag & AGRP_SELECTED) ? 1 : 0;
|
|
|
|
set_active_actiongroup(act, agrp, select);
|
|
}
|
|
|
|
static void hilight_channel (bAction *act, bActionChannel *achan, short select)
|
|
{
|
|
bActionChannel *curchan;
|
|
|
|
if (!act)
|
|
return;
|
|
|
|
for (curchan=act->chanbase.first; curchan; curchan=curchan->next) {
|
|
if (curchan==achan && select)
|
|
curchan->flag |= ACHAN_HILIGHTED;
|
|
else
|
|
curchan->flag &= ~ACHAN_HILIGHTED;
|
|
}
|
|
}
|
|
|
|
/* Syncs selection of channels with selection of object elements in posemode */
|
|
/* messy call... */
|
|
static void select_poseelement_by_name (char *name, int select)
|
|
{
|
|
Object *ob= OBACT;
|
|
bPoseChannel *pchan;
|
|
|
|
if ((ob==NULL) || (ob->type!=OB_ARMATURE))
|
|
return;
|
|
|
|
if (abs(select) == 2) {
|
|
for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next)
|
|
pchan->bone->flag &= ~(BONE_ACTIVE);
|
|
}
|
|
|
|
pchan= get_pose_channel(ob->pose, name);
|
|
if (pchan) {
|
|
if (select)
|
|
pchan->bone->flag |= (BONE_SELECTED);
|
|
else
|
|
pchan->bone->flag &= ~(BONE_SELECTED);
|
|
if (select == 2)
|
|
pchan->bone->flag |= (BONE_ACTIVE);
|
|
}
|
|
}
|
|
|
|
/* apparently within active object context */
|
|
/* called extern, like on bone selection */
|
|
void select_actionchannel_by_name (bAction *act, char *name, int select)
|
|
{
|
|
bActionChannel *achan;
|
|
|
|
if (act == NULL)
|
|
return;
|
|
|
|
for (achan = act->chanbase.first; achan; achan= achan->next) {
|
|
if (!strcmp(achan->name, name)) {
|
|
if (select) {
|
|
achan->flag |= ACHAN_SELECTED;
|
|
hilight_channel(act, achan, 1);
|
|
}
|
|
else {
|
|
achan->flag &= ~ACHAN_SELECTED;
|
|
hilight_channel(act, achan, 0);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* select_mode = SELECT_REPLACE
|
|
* = SELECT_ADD
|
|
* = SELECT_SUBTRACT
|
|
* = SELECT_INVERT
|
|
*/
|
|
|
|
/* exported for outliner (ton) */
|
|
/* apparently within active object context */
|
|
int select_channel (bAction *act, bActionChannel *achan, int selectmode)
|
|
{
|
|
/* Select the channel based on the selection mode */
|
|
int flag;
|
|
|
|
switch (selectmode) {
|
|
case SELECT_ADD:
|
|
achan->flag |= ACHAN_SELECTED;
|
|
break;
|
|
case SELECT_SUBTRACT:
|
|
achan->flag &= ~ACHAN_SELECTED;
|
|
break;
|
|
case SELECT_INVERT:
|
|
achan->flag ^= ACHAN_SELECTED;
|
|
break;
|
|
}
|
|
flag = (achan->flag & ACHAN_SELECTED) ? 1 : 0;
|
|
|
|
hilight_channel(act, achan, flag);
|
|
select_poseelement_by_name(achan->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;
|
|
}
|
|
|
|
int select_icu_channel (bAction *act, IpoCurve *icu, int selectmode)
|
|
{
|
|
/* Select the channel based on the selection mode */
|
|
int flag;
|
|
|
|
switch (selectmode) {
|
|
case SELECT_ADD:
|
|
icu->flag |= IPO_SELECT;
|
|
break;
|
|
case SELECT_SUBTRACT:
|
|
icu->flag &= ~IPO_SELECT;
|
|
break;
|
|
case SELECT_INVERT:
|
|
icu->flag ^= IPO_SELECT;
|
|
break;
|
|
}
|
|
flag = (icu->flag & IPO_SELECT) ? 1 : 0;
|
|
return flag;
|
|
}
|
|
|
|
int select_gplayer_channel (bGPdata *gpd, bGPDlayer *gpl, int selectmode)
|
|
{
|
|
/* Select the channel based on the selection mode */
|
|
int flag;
|
|
|
|
switch (selectmode) {
|
|
case SELECT_ADD:
|
|
gpl->flag |= GP_LAYER_SELECT;
|
|
break;
|
|
case SELECT_SUBTRACT:
|
|
gpl->flag &= ~GP_LAYER_SELECT;
|
|
break;
|
|
case SELECT_INVERT:
|
|
gpl->flag ^= GP_LAYER_SELECT;
|
|
break;
|
|
}
|
|
|
|
flag = (gpl->flag & GP_LAYER_SELECT) ? 1 : 0;
|
|
if (flag)
|
|
gpencil_layer_setactive(gpd, gpl);
|
|
|
|
return flag;
|
|
}
|
|
|
|
|
|
/* select only the active action-group's action channels */
|
|
void select_action_group_channels (bAction *act, bActionGroup *agrp)
|
|
{
|
|
bActionChannel *achan;
|
|
|
|
/* error checking */
|
|
if (ELEM(NULL, act, agrp))
|
|
return;
|
|
|
|
/* deselect all other channels */
|
|
deselect_actionchannels(act, 0);
|
|
|
|
/* only select channels in group */
|
|
for (achan= agrp->channels.first; achan && achan->grp==agrp; achan= achan->next) {
|
|
select_channel(act, achan, SELECT_ADD);
|
|
|
|
/* messy... set active bone */
|
|
select_poseelement_by_name(achan->name, 1);
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------- */
|
|
|
|
/* De-selects or inverts the selection of Channels in a given Action
|
|
* mode: 0 = default behaviour (select all), 1 = test if (de)select all, 2 = invert all
|
|
*/
|
|
void deselect_actionchannels (bAction *act, short mode)
|
|
{
|
|
ListBase act_data = {NULL, NULL};
|
|
bActListElem *ale;
|
|
int filter, sel=1;
|
|
|
|
/* filter data */
|
|
filter= ACTFILTER_VISIBLE;
|
|
actdata_filter(&act_data, filter, act, ACTCONT_ACTION);
|
|
|
|
/* See if we should be selecting or deselecting */
|
|
if (mode == 1) {
|
|
for (ale= act_data.first; ale; ale= ale->next) {
|
|
if (sel == 0)
|
|
break;
|
|
|
|
switch (ale->type) {
|
|
case ACTTYPE_GROUP:
|
|
if (ale->flag & AGRP_SELECTED)
|
|
sel= 0;
|
|
break;
|
|
case ACTTYPE_ACHAN:
|
|
if (ale->flag & ACHAN_SELECTED)
|
|
sel= 0;
|
|
break;
|
|
case ACTTYPE_CONCHAN:
|
|
if (ale->flag & CONSTRAINT_CHANNEL_SELECT)
|
|
sel=0;
|
|
break;
|
|
case ACTTYPE_ICU:
|
|
if (ale->flag & IPO_SELECT)
|
|
sel=0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
sel= 0;
|
|
|
|
/* Now set the flags */
|
|
for (ale= act_data.first; ale; ale= ale->next) {
|
|
switch (ale->type) {
|
|
case ACTTYPE_GROUP:
|
|
{
|
|
bActionGroup *agrp= (bActionGroup *)ale->data;
|
|
|
|
if (mode == 2)
|
|
agrp->flag ^= AGRP_SELECTED;
|
|
else if (sel)
|
|
agrp->flag |= AGRP_SELECTED;
|
|
else
|
|
agrp->flag &= ~AGRP_SELECTED;
|
|
|
|
agrp->flag &= ~AGRP_ACTIVE;
|
|
}
|
|
break;
|
|
case ACTTYPE_ACHAN:
|
|
{
|
|
bActionChannel *achan= (bActionChannel *)ale->data;
|
|
|
|
if (mode == 2)
|
|
achan->flag ^= AGRP_SELECTED;
|
|
else if (sel)
|
|
achan->flag |= ACHAN_SELECTED;
|
|
else
|
|
achan->flag &= ~ACHAN_SELECTED;
|
|
|
|
select_poseelement_by_name(achan->name, sel);
|
|
achan->flag &= ~ACHAN_HILIGHTED;
|
|
}
|
|
break;
|
|
case ACTTYPE_CONCHAN:
|
|
{
|
|
bConstraintChannel *conchan= (bConstraintChannel *)ale->data;
|
|
|
|
if (mode == 2)
|
|
conchan->flag ^= CONSTRAINT_CHANNEL_SELECT;
|
|
else if (sel)
|
|
conchan->flag |= CONSTRAINT_CHANNEL_SELECT;
|
|
else
|
|
conchan->flag &= ~CONSTRAINT_CHANNEL_SELECT;
|
|
}
|
|
break;
|
|
case ACTTYPE_ICU:
|
|
{
|
|
IpoCurve *icu= (IpoCurve *)ale->data;
|
|
|
|
if (mode == 2)
|
|
icu->flag ^= IPO_SELECT;
|
|
else if (sel)
|
|
icu->flag |= IPO_SELECT;
|
|
else
|
|
icu->flag &= ~IPO_SELECT;
|
|
|
|
icu->flag &= ~IPO_ACTIVE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Cleanup */
|
|
BLI_freelistN(&act_data);
|
|
}
|
|
|
|
/* deselects channels in the action editor */
|
|
void deselect_action_channels (short mode)
|
|
{
|
|
void *data;
|
|
short datatype;
|
|
|
|
/* determine what type of data we are operating on */
|
|
data = get_action_context(&datatype);
|
|
if (data == NULL) return;
|
|
|
|
/* based on type */
|
|
if (datatype == ACTCONT_ACTION)
|
|
deselect_actionchannels(data, mode);
|
|
else if (datatype == ACTCONT_GPENCIL)
|
|
deselect_gpencil_layers(data, mode);
|
|
// should shapekey channels be allowed to do this?
|
|
}
|
|
|
|
/* deselects keyframes in the action editor */
|
|
void deselect_action_keys (short test, short sel)
|
|
{
|
|
ListBase act_data = {NULL, NULL};
|
|
bActListElem *ale;
|
|
int filter;
|
|
void *data;
|
|
short datatype;
|
|
|
|
/* determine what type of data we are operating on */
|
|
data = get_action_context(&datatype);
|
|
if (data == NULL) return;
|
|
|
|
/* determine type-based settings */
|
|
if (datatype == ACTCONT_GPENCIL)
|
|
filter= (ACTFILTER_VISIBLE);
|
|
else
|
|
filter= (ACTFILTER_VISIBLE | ACTFILTER_IPOKEYS);
|
|
|
|
/* filter data */
|
|
actdata_filter(&act_data, filter, data, datatype);
|
|
|
|
/* See if we should be selecting or deselecting */
|
|
if (test) {
|
|
for (ale= act_data.first; ale; ale= ale->next) {
|
|
if (ale->type == ACTTYPE_GPLAYER) {
|
|
if (is_gplayer_frame_selected(ale->data)) {
|
|
sel= 0;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
if (is_ipo_key_selected(ale->key_data)) {
|
|
sel= 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Now set the flags */
|
|
for (ale= act_data.first; ale; ale= ale->next) {
|
|
if (ale->type == ACTTYPE_GPLAYER)
|
|
set_gplayer_frame_selection(ale->data, sel);
|
|
else
|
|
set_ipo_key_selection(ale->key_data, sel);
|
|
}
|
|
|
|
/* Cleanup */
|
|
BLI_freelistN(&act_data);
|
|
}
|
|
|
|
/* selects all keyframes in the action editor - per channel or time
|
|
* mode = 0: all in channel; mode = 1: all in frame
|
|
*/
|
|
void selectall_action_keys (short mval[], short mode, short select_mode)
|
|
{
|
|
void *data;
|
|
short datatype;
|
|
|
|
/* determine what type of data we are operating on */
|
|
data = get_action_context(&datatype);
|
|
if (data == NULL) return;
|
|
|
|
if (select_mode == SELECT_REPLACE) {
|
|
deselect_action_keys(0, 0);
|
|
select_mode = SELECT_ADD;
|
|
}
|
|
|
|
/* depending on mode */
|
|
switch (mode) {
|
|
case 0: /* all in channel*/
|
|
{
|
|
void *act_channel, *channel_owner;
|
|
short chantype;
|
|
|
|
/* get channel, and act according to type */
|
|
act_channel= get_nearest_act_channel(mval, &chantype, &channel_owner);
|
|
switch (chantype) {
|
|
case ACTTYPE_GROUP:
|
|
{
|
|
bActionGroup *agrp= (bActionGroup *)act_channel;
|
|
bActionChannel *achan;
|
|
bConstraintChannel *conchan;
|
|
|
|
for (achan= agrp->channels.first; achan && achan->grp==agrp; achan= achan->next) {
|
|
select_ipo_bezier_keys(achan->ipo, select_mode);
|
|
|
|
for (conchan=achan->constraintChannels.first; conchan; conchan=conchan->next)
|
|
select_ipo_bezier_keys(conchan->ipo, select_mode);
|
|
}
|
|
}
|
|
break;
|
|
case ACTTYPE_ACHAN:
|
|
{
|
|
bActionChannel *achan= (bActionChannel *)act_channel;
|
|
select_ipo_bezier_keys(achan->ipo, select_mode);
|
|
}
|
|
break;
|
|
case ACTTYPE_CONCHAN:
|
|
{
|
|
bConstraintChannel *conchan= (bConstraintChannel *)act_channel;
|
|
select_ipo_bezier_keys(conchan->ipo, select_mode);
|
|
}
|
|
break;
|
|
case ACTTYPE_ICU:
|
|
{
|
|
IpoCurve *icu= (IpoCurve *)act_channel;
|
|
select_icu_bezier_keys(icu, select_mode);
|
|
}
|
|
break;
|
|
case ACTTYPE_GPLAYER:
|
|
{
|
|
bGPDlayer *gpl= (bGPDlayer *)act_channel;
|
|
select_gpencil_frames(gpl, select_mode);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case 1: /* all in frame */
|
|
{
|
|
ListBase act_data = {NULL, NULL};
|
|
bActListElem *ale;
|
|
int filter;
|
|
rcti rect;
|
|
rctf rectf;
|
|
|
|
/* use bounding box to find kframe */
|
|
rect.xmin = rect.xmax = mval[0];
|
|
rect.ymin = rect.ymax = mval[1];
|
|
|
|
mval[0]= rect.xmin;
|
|
mval[1]= rect.ymin+2;
|
|
areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin);
|
|
rectf.xmax= rectf.xmin;
|
|
rectf.ymax= rectf.ymin;
|
|
|
|
rectf.xmin = rectf.xmin - 0.5f;
|
|
rectf.xmax = rectf.xmax + 0.5f;
|
|
|
|
/* filter data */
|
|
if (datatype == ACTCONT_GPENCIL)
|
|
filter= (ACTFILTER_VISIBLE);
|
|
else
|
|
filter= (ACTFILTER_VISIBLE | ACTFILTER_IPOKEYS);
|
|
actdata_filter(&act_data, filter, data, datatype);
|
|
|
|
/* Now set the flags */
|
|
for (ale= act_data.first; ale; ale= ale->next) {
|
|
borderselect_ipo_key(ale->key_data, rectf.xmin, rectf.xmax, select_mode);
|
|
}
|
|
|
|
/* Cleanup */
|
|
BLI_freelistN(&act_data);
|
|
}
|
|
break;
|
|
}
|
|
|
|
allqueue(REDRAWNLA, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWIPO, 0);
|
|
}
|
|
|
|
/* Selects all visible keyframes between the specified markers */
|
|
void markers_selectkeys_between (void)
|
|
{
|
|
ListBase act_data = {NULL, NULL};
|
|
bActListElem *ale;
|
|
int filter;
|
|
void *data;
|
|
short datatype;
|
|
float min, max;
|
|
|
|
/* determine what type of data we are operating on */
|
|
data = get_action_context(&datatype);
|
|
if (data == NULL) return;
|
|
|
|
/* get extreme markers */
|
|
get_minmax_markers(1, &min, &max);
|
|
if (min==max) return;
|
|
min -= 0.5f;
|
|
max += 0.5f;
|
|
|
|
/* filter data */
|
|
filter= (ACTFILTER_VISIBLE | ACTFILTER_IPOKEYS);
|
|
actdata_filter(&act_data, filter, data, datatype);
|
|
|
|
/* select keys in-between */
|
|
for (ale= act_data.first; ale; ale= ale->next) {
|
|
if(NLA_ACTION_SCALED && datatype==ACTCONT_ACTION) {
|
|
actstrip_map_ipo_keys(OBACT, ale->key_data, 0, 1);
|
|
borderselect_ipo_key(ale->key_data, min, max, SELECT_ADD);
|
|
actstrip_map_ipo_keys(OBACT, ale->key_data, 1, 1);
|
|
}
|
|
else {
|
|
borderselect_ipo_key(ale->key_data, min, max, SELECT_ADD);
|
|
}
|
|
}
|
|
|
|
/* Cleanup */
|
|
BLI_freelistN(&act_data);
|
|
}
|
|
|
|
/* Selects all the keyframes on either side of the current frame (depends on which side the mouse is on) */
|
|
void selectkeys_leftright (short leftright, short select_mode)
|
|
{
|
|
ListBase act_data = {NULL, NULL};
|
|
bActListElem *ale;
|
|
int filter;
|
|
void *data;
|
|
short datatype;
|
|
float min, max;
|
|
|
|
if (select_mode==SELECT_REPLACE) {
|
|
select_mode=SELECT_ADD;
|
|
deselect_action_keys(0, 0);
|
|
}
|
|
|
|
/* determine what type of data we are operating on */
|
|
data = get_action_context(&datatype);
|
|
if (data == NULL) return;
|
|
|
|
if (leftright == 1) {
|
|
min = -MAXFRAMEF;
|
|
max = (float)(CFRA + 0.1f);
|
|
}
|
|
else {
|
|
min = (float)(CFRA - 0.1f);
|
|
max = MAXFRAMEF;
|
|
}
|
|
|
|
/* filter data */
|
|
if (datatype == ACTCONT_GPENCIL)
|
|
filter= (ACTFILTER_VISIBLE);
|
|
else
|
|
filter= (ACTFILTER_VISIBLE | ACTFILTER_IPOKEYS);
|
|
actdata_filter(&act_data, filter, data, datatype);
|
|
|
|
/* select keys on the side where most data occurs */
|
|
for (ale= act_data.first; ale; ale= ale->next) {
|
|
if (NLA_ACTION_SCALED && datatype==ACTCONT_ACTION) {
|
|
actstrip_map_ipo_keys(OBACT, ale->key_data, 0, 1);
|
|
borderselect_ipo_key(ale->key_data, min, max, SELECT_ADD);
|
|
actstrip_map_ipo_keys(OBACT, ale->key_data, 1, 1);
|
|
}
|
|
else if (ale->type == ACTTYPE_GPLAYER)
|
|
borderselect_gplayer_frames(ale->data, min, max, SELECT_ADD);
|
|
else
|
|
borderselect_ipo_key(ale->key_data, min, max, SELECT_ADD);
|
|
}
|
|
|
|
/* Cleanup */
|
|
BLI_freelistN(&act_data);
|
|
|
|
allqueue(REDRAWNLA, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWIPO, 0);
|
|
}
|
|
|
|
/* ----------------------------------------- */
|
|
|
|
/* Jumps to the frame where the next/previous keyframe (that is visible) occurs
|
|
* dir: indicates direction
|
|
*/
|
|
void nextprev_action_keyframe (short dir)
|
|
{
|
|
ListBase act_data = {NULL, NULL};
|
|
bActListElem *ale;
|
|
int filter;
|
|
void *data;
|
|
short datatype;
|
|
|
|
ListBase elems= {NULL, NULL};
|
|
CfraElem *ce, *nearest=NULL;
|
|
float dist, min_dist= 1000000;
|
|
|
|
|
|
/* determine what type of data we are operating on */
|
|
data = get_action_context(&datatype);
|
|
if (data == NULL) return;
|
|
|
|
/* abort if no direction */
|
|
if (dir == 0)
|
|
return;
|
|
|
|
/* get list of keyframes that can be used (in global-time) */
|
|
if (datatype == ACTCONT_GPENCIL)
|
|
filter= (ACTFILTER_VISIBLE);
|
|
else
|
|
filter= (ACTFILTER_VISIBLE | ACTFILTER_IPOKEYS);
|
|
actdata_filter(&act_data, filter, data, datatype);
|
|
|
|
for (ale= act_data.first; ale; ale= ale->next) {
|
|
if (NLA_ACTION_SCALED && datatype==ACTCONT_ACTION) {
|
|
actstrip_map_ipo_keys(OBACT, ale->key_data, 0, 1);
|
|
make_cfra_list(ale->key_data, &elems);
|
|
actstrip_map_ipo_keys(OBACT, ale->key_data, 1, 1);
|
|
}
|
|
else if (ale->type == ACTTYPE_GPLAYER)
|
|
gplayer_make_cfra_list(ale->key_data, &elems, 0);
|
|
else
|
|
make_cfra_list(ale->key_data, &elems);
|
|
}
|
|
|
|
BLI_freelistN(&act_data);
|
|
|
|
/* find nearest keyframe to current frame */
|
|
for (ce= elems.first; ce; ce= ce->next) {
|
|
dist= ABS(ce->cfra - CFRA);
|
|
|
|
if (dist < min_dist) {
|
|
min_dist= dist;
|
|
nearest= ce;
|
|
}
|
|
}
|
|
|
|
/* if a nearest keyframe was found, use the one either side */
|
|
if (nearest) {
|
|
short changed= 0;
|
|
|
|
if ((dir > 0) && (nearest->next)) {
|
|
CFRA= (int)nearest->next->cfra;
|
|
changed= 1;
|
|
}
|
|
else if ((dir < 0) && (nearest->prev)) {
|
|
CFRA= (int)nearest->prev->cfra;
|
|
changed= 1;
|
|
}
|
|
|
|
if (changed) {
|
|
update_for_newframe();
|
|
allqueue(REDRAWALL, 0);
|
|
}
|
|
}
|
|
|
|
/* free temp data */
|
|
BLI_freelistN(&elems);
|
|
}
|
|
|
|
/* ----------------------------------------- */
|
|
|
|
/* This function makes a list of the selected keyframes
|
|
* in the ipo curves it has been passed
|
|
*/
|
|
static void make_sel_cfra_list (Ipo *ipo, ListBase *elems)
|
|
{
|
|
IpoCurve *icu;
|
|
|
|
if (ipo == NULL) return;
|
|
|
|
for (icu= ipo->curve.first; icu; icu= icu->next) {
|
|
BezTriple *bezt;
|
|
int a= 0;
|
|
|
|
for (bezt=icu->bezt; a<icu->totvert; a++, bezt++) {
|
|
if (bezt && BEZSELECTED(bezt))
|
|
add_to_cfra_elem(elems, bezt);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* This function selects all key frames in the same column(s) as a already selected key(s)
|
|
* or marker(s), or all the keyframes on a particular frame (triggered by a RMB on x-scrollbar)
|
|
*/
|
|
void column_select_action_keys (int mode)
|
|
{
|
|
ListBase elems= {NULL, NULL};
|
|
CfraElem *ce;
|
|
IpoCurve *icu;
|
|
ListBase act_data = {NULL, NULL};
|
|
bActListElem *ale;
|
|
int filter;
|
|
void *data;
|
|
short datatype;
|
|
|
|
/* determine what type of data we are operating on */
|
|
data = get_action_context(&datatype);
|
|
if (data == NULL) return;
|
|
|
|
/* build list of columns */
|
|
switch (mode) {
|
|
case 1: /* list of selected keys */
|
|
if (datatype == ACTCONT_GPENCIL) {
|
|
filter= (ACTFILTER_VISIBLE);
|
|
actdata_filter(&act_data, filter, data, datatype);
|
|
|
|
for (ale= act_data.first; ale; ale= ale->next)
|
|
gplayer_make_cfra_list(ale->data, &elems, 1);
|
|
}
|
|
else {
|
|
filter= (ACTFILTER_VISIBLE | ACTFILTER_IPOKEYS);
|
|
actdata_filter(&act_data, filter, data, datatype);
|
|
|
|
for (ale= act_data.first; ale; ale= ale->next)
|
|
make_sel_cfra_list(ale->key_data, &elems);
|
|
}
|
|
|
|
BLI_freelistN(&act_data);
|
|
break;
|
|
case 2: /* list of selected markers */
|
|
make_marker_cfra_list(&elems, 1);
|
|
|
|
/* apply scaled action correction if needed */
|
|
if (NLA_ACTION_SCALED && datatype==ACTCONT_ACTION) {
|
|
for (ce= elems.first; ce; ce= ce->next)
|
|
ce->cfra= get_action_frame(OBACT, ce->cfra);
|
|
}
|
|
break;
|
|
case 3: /* current frame */
|
|
/* make a single CfraElem */
|
|
ce= MEM_callocN(sizeof(CfraElem), "cfraElem");
|
|
BLI_addtail(&elems, ce);
|
|
|
|
/* apply scaled action correction if needed */
|
|
if (NLA_ACTION_SCALED && datatype==ACTCONT_ACTION)
|
|
ce->cfra= (float)get_action_frame(OBACT, (float)CFRA);
|
|
else
|
|
ce->cfra= (float)CFRA;
|
|
}
|
|
|
|
/* loop through all of the keys and select additional keyframes
|
|
* based on the keys found to be selected above
|
|
*/
|
|
if (datatype == ACTCONT_GPENCIL)
|
|
filter= (ACTFILTER_VISIBLE);
|
|
else
|
|
filter= (ACTFILTER_VISIBLE | ACTFILTER_ONLYICU);
|
|
actdata_filter(&act_data, filter, data, datatype);
|
|
|
|
for (ale= act_data.first; ale; ale= ale->next) {
|
|
for (ce= elems.first; ce; ce= ce->next) {
|
|
/* select elements with frame number matching cfraelem */
|
|
if (ale->type == ACTTYPE_GPLAYER) {
|
|
bGPDlayer *gpl= (bGPDlayer *)ale->data;
|
|
bGPDframe *gpf;
|
|
|
|
for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
|
|
if ( (int)ce->cfra == gpf->framenum )
|
|
gpf->flag |= GP_FRAME_SELECT;
|
|
}
|
|
}
|
|
else {
|
|
for (icu= ale->key_data; icu; icu= icu->next) {
|
|
BezTriple *bezt;
|
|
int verts = 0;
|
|
|
|
for (bezt=icu->bezt; verts<icu->totvert; bezt++, verts++) {
|
|
if (bezt) {
|
|
if( (int)(ce->cfra) == (int)(bezt->vec[1][0]) )
|
|
bezt->f2 |= 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
BLI_freelistN(&act_data);
|
|
BLI_freelistN(&elems);
|
|
}
|
|
|
|
/* borderselect: for action-channels */
|
|
void borderselect_actionchannels (void)
|
|
{
|
|
ListBase act_data = {NULL, NULL};
|
|
bActListElem *ale;
|
|
int filter;
|
|
void *data;
|
|
short datatype;
|
|
|
|
rcti rect;
|
|
rctf rectf;
|
|
int val, selectmode;
|
|
short mval[2];
|
|
float ymin, ymax;
|
|
|
|
/* determine what type of data we are operating on */
|
|
data = get_action_context(&datatype);
|
|
if (data == NULL) return;
|
|
if (ELEM(datatype, ACTCONT_ACTION, ACTCONT_GPENCIL)==0) return;
|
|
|
|
/* draw and handle the borderselect stuff (ui) and get the select rect */
|
|
if ( (val = get_border(&rect, 3)) ) {
|
|
selectmode= ((val==LEFTMOUSE) ? SELECT_ADD : 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 = CHANNELHEIGHT/2;
|
|
|
|
/* filter data */
|
|
filter= (ACTFILTER_VISIBLE | ACTFILTER_CHANNELS);
|
|
actdata_filter(&act_data, filter, data, datatype);
|
|
|
|
/* loop over data, doing border select */
|
|
for (ale= act_data.first; ale; ale= ale->next) {
|
|
ymin=ymax-(CHANNELHEIGHT+CHANNELSKIP);
|
|
|
|
/* if channel is within border-select region, alter it */
|
|
if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))) {
|
|
/* only the following types can be selected */
|
|
switch (ale->type) {
|
|
case ACTTYPE_GROUP: /* action group */
|
|
{
|
|
bActionGroup *agrp= (bActionGroup *)ale->data;
|
|
|
|
if (selectmode == SELECT_ADD)
|
|
agrp->flag |= AGRP_SELECTED;
|
|
else
|
|
agrp->flag &= ~AGRP_SELECTED;
|
|
}
|
|
break;
|
|
case ACTTYPE_ACHAN: /* action channel */
|
|
case ACTTYPE_FILLIPO: /* expand ipo curves = action channel */
|
|
case ACTTYPE_FILLCON: /* expand constraint channels = action channel */
|
|
{
|
|
bActionChannel *achan= (bActionChannel *)ale->data;
|
|
|
|
if (selectmode == SELECT_ADD)
|
|
achan->flag |= ACHAN_SELECTED;
|
|
else
|
|
achan->flag &= ~ACHAN_SELECTED;
|
|
|
|
/* messy... set active bone */
|
|
select_poseelement_by_name(achan->name, selectmode);
|
|
}
|
|
break;
|
|
case ACTTYPE_CONCHAN: /* constraint channel */
|
|
{
|
|
bConstraintChannel *conchan = (bConstraintChannel *)ale->data;
|
|
|
|
if (selectmode == SELECT_ADD)
|
|
conchan->flag |= CONSTRAINT_CHANNEL_SELECT;
|
|
else
|
|
conchan->flag &= ~CONSTRAINT_CHANNEL_SELECT;
|
|
}
|
|
break;
|
|
case ACTTYPE_ICU: /* ipo-curve channel */
|
|
{
|
|
IpoCurve *icu = (IpoCurve *)ale->data;
|
|
|
|
if (selectmode == SELECT_ADD)
|
|
icu->flag |= IPO_SELECT;
|
|
else
|
|
icu->flag &= ~IPO_SELECT;
|
|
}
|
|
break;
|
|
case ACTTYPE_GPLAYER: /* grease-pencil layer */
|
|
{
|
|
bGPDlayer *gpl = (bGPDlayer *)ale->data;
|
|
|
|
if (selectmode == SELECT_ADD)
|
|
gpl->flag |= GP_LAYER_SELECT;
|
|
else
|
|
gpl->flag &= ~GP_LAYER_SELECT;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* select action-channel 'owner' */
|
|
if ((ale->owner) && (ale->ownertype == ACTTYPE_ACHAN)) {
|
|
bActionChannel *achano= (bActionChannel *)ale->owner;
|
|
|
|
/* messy... set active bone */
|
|
select_poseelement_by_name(achano->name, selectmode);
|
|
}
|
|
}
|
|
|
|
ymax=ymin;
|
|
}
|
|
|
|
/* cleanup */
|
|
BLI_freelistN(&act_data);
|
|
|
|
BIF_undo_push("Border Select Action");
|
|
allqueue(REDRAWIPO, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWNLA, 0);
|
|
allqueue(REDRAWVIEW3D, 0);
|
|
}
|
|
}
|
|
|
|
/* some quick defines for borderselect modes */
|
|
enum {
|
|
ACTEDIT_BORDERSEL_ALL = 0,
|
|
ACTEDIT_BORDERSEL_FRA,
|
|
ACTEDIT_BORDERSEL_CHA
|
|
};
|
|
|
|
/* borderselect: for keyframes only */
|
|
void borderselect_action (void)
|
|
{
|
|
ListBase act_data = {NULL, NULL};
|
|
bActListElem *ale;
|
|
int filter;
|
|
void *data;
|
|
short datatype;
|
|
|
|
rcti rect;
|
|
rctf rectf;
|
|
int val, selectmode, mode;
|
|
int (*select_function)(BezTriple *);
|
|
short mval[2];
|
|
float ymin, ymax;
|
|
|
|
/* determine what type of data we are operating on */
|
|
data = get_action_context(&datatype);
|
|
if (data == NULL) return;
|
|
|
|
/* what should be selected (based on the starting location of cursor) */
|
|
getmouseco_areawin(mval);
|
|
if (IN_2D_VERT_SCROLL(mval))
|
|
mode = ACTEDIT_BORDERSEL_CHA;
|
|
else if (IN_2D_HORIZ_SCROLL(mval))
|
|
mode = ACTEDIT_BORDERSEL_FRA;
|
|
else
|
|
mode = ACTEDIT_BORDERSEL_ALL;
|
|
|
|
/* draw and handle the borderselect stuff (ui) and get the select rect */
|
|
if ( (val = get_border(&rect, 3)) ) {
|
|
if (val == LEFTMOUSE) {
|
|
selectmode = SELECT_ADD;
|
|
select_function = select_bezier_add;
|
|
}
|
|
else {
|
|
selectmode = SELECT_SUBTRACT;
|
|
select_function = select_bezier_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);
|
|
|
|
/* if action is mapped in NLA, it returns a correction */
|
|
if (NLA_ACTION_SCALED && datatype==ACTCONT_ACTION) {
|
|
rectf.xmin= get_action_frame(OBACT, rectf.xmin);
|
|
rectf.xmax= get_action_frame(OBACT, rectf.xmax);
|
|
}
|
|
|
|
ymax = CHANNELHEIGHT/2;
|
|
|
|
/* filter data */
|
|
filter= (ACTFILTER_VISIBLE | ACTFILTER_CHANNELS);
|
|
actdata_filter(&act_data, filter, data, datatype);
|
|
|
|
/* loop over data, doing border select */
|
|
for (ale= act_data.first; ale; ale= ale->next) {
|
|
ymin=ymax-(CHANNELHEIGHT+CHANNELSKIP);
|
|
|
|
/* what gets selected depends on the mode (based on initial position of cursor) */
|
|
switch (mode) {
|
|
case ACTEDIT_BORDERSEL_FRA: /* all in frame(s) */
|
|
if (ale->key_data) {
|
|
if (ale->datatype == ALE_IPO)
|
|
borderselect_ipo_key(ale->key_data, rectf.xmin, rectf.xmax, selectmode);
|
|
else if (ale->datatype == ALE_ICU)
|
|
borderselect_icu_key(ale->key_data, rectf.xmin, rectf.xmax, select_function);
|
|
}
|
|
else if (ale->type == ACTTYPE_GROUP) {
|
|
bActionGroup *agrp= ale->data;
|
|
bActionChannel *achan;
|
|
bConstraintChannel *conchan;
|
|
|
|
for (achan= agrp->channels.first; achan && achan->grp==agrp; achan= achan->next) {
|
|
borderselect_ipo_key(achan->ipo, rectf.xmin, rectf.xmax, selectmode);
|
|
|
|
for (conchan=achan->constraintChannels.first; conchan; conchan=conchan->next)
|
|
borderselect_ipo_key(conchan->ipo, rectf.xmin, rectf.xmax, selectmode);
|
|
}
|
|
}
|
|
else if (ale->type == ACTTYPE_GPLAYER) {
|
|
borderselect_gplayer_frames(ale->data, rectf.xmin, rectf.xmax, selectmode);
|
|
}
|
|
break;
|
|
case ACTEDIT_BORDERSEL_CHA: /* all in channel(s) */
|
|
if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))) {
|
|
if (ale->key_data) {
|
|
if (ale->datatype == ALE_IPO)
|
|
select_ipo_bezier_keys(ale->key_data, selectmode);
|
|
else if (ale->datatype == ALE_ICU)
|
|
select_icu_bezier_keys(ale->key_data, selectmode);
|
|
}
|
|
else if (ale->type == ACTTYPE_GROUP) {
|
|
bActionGroup *agrp= ale->data;
|
|
bActionChannel *achan;
|
|
bConstraintChannel *conchan;
|
|
|
|
for (achan= agrp->channels.first; achan && achan->grp==agrp; achan= achan->next) {
|
|
select_ipo_bezier_keys(achan->ipo, selectmode);
|
|
|
|
for (conchan=achan->constraintChannels.first; conchan; conchan=conchan->next)
|
|
select_ipo_bezier_keys(conchan->ipo, selectmode);
|
|
}
|
|
}
|
|
else if (ale->type == ACTTYPE_GPLAYER) {
|
|
select_gpencil_frames(ale->data, selectmode);
|
|
}
|
|
}
|
|
break;
|
|
default: /* any keyframe inside region defined by region */
|
|
if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))) {
|
|
if (ale->key_data) {
|
|
if (ale->datatype == ALE_IPO)
|
|
borderselect_ipo_key(ale->key_data, rectf.xmin, rectf.xmax, selectmode);
|
|
else if (ale->datatype == ALE_ICU)
|
|
borderselect_icu_key(ale->key_data, rectf.xmin, rectf.xmax, select_function);
|
|
}
|
|
else if (ale->type == ACTTYPE_GROUP) {
|
|
bActionGroup *agrp= ale->data;
|
|
bActionChannel *achan;
|
|
bConstraintChannel *conchan;
|
|
|
|
for (achan= agrp->channels.first; achan && achan->grp==agrp; achan= achan->next) {
|
|
borderselect_ipo_key(achan->ipo, rectf.xmin, rectf.xmax, selectmode);
|
|
|
|
for (conchan=achan->constraintChannels.first; conchan; conchan=conchan->next)
|
|
borderselect_ipo_key(conchan->ipo, rectf.xmin, rectf.xmax, selectmode);
|
|
}
|
|
}
|
|
else if (ale->type == ACTTYPE_GPLAYER) {
|
|
borderselect_gplayer_frames(ale->data, rectf.xmin, rectf.xmax, selectmode);
|
|
}
|
|
}
|
|
}
|
|
|
|
ymax=ymin;
|
|
}
|
|
|
|
/* cleanup */
|
|
BLI_freelistN(&act_data);
|
|
|
|
BIF_undo_push("Border Select Action");
|
|
allqueue(REDRAWIPO, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWNLA, 0);
|
|
}
|
|
}
|
|
|
|
/* **************************************************** */
|
|
/* MOUSE-HANDLING */
|
|
|
|
/* right-hand side - mouse click */
|
|
static void mouse_action (int selectmode)
|
|
{
|
|
void *data;
|
|
short datatype;
|
|
|
|
bAction *act= NULL;
|
|
bActionGroup *agrp= NULL;
|
|
bActionChannel *achan= NULL;
|
|
bConstraintChannel *conchan= NULL;
|
|
IpoCurve *icu= NULL;
|
|
bGPdata *gpd = NULL;
|
|
bGPDlayer *gpl = NULL;
|
|
TimeMarker *marker, *pmarker;
|
|
|
|
void *act_channel;
|
|
short sel, act_type = 0;
|
|
float selx = 0.0;
|
|
|
|
/* determine what type of data we are operating on */
|
|
data = get_action_context(&datatype);
|
|
if (data == NULL) return;
|
|
if (datatype == ACTCONT_ACTION) act= (bAction *)data;
|
|
if (datatype == ACTCONT_GPENCIL) gpd= (bGPdata *)data;
|
|
|
|
act_channel= get_nearest_action_key(&selx, &sel, &act_type, &achan);
|
|
marker= find_nearest_marker(SCE_MARKERS, 1);
|
|
pmarker= (act) ? find_nearest_marker(&act->markers, 1) : NULL;
|
|
|
|
if (marker) {
|
|
/* what about scene's markers? */
|
|
if (selectmode == SELECT_REPLACE) {
|
|
deselect_markers(0, 0);
|
|
marker->flag |= SELECT;
|
|
}
|
|
else if (selectmode == SELECT_INVERT) {
|
|
if (marker->flag & SELECT)
|
|
marker->flag &= ~SELECT;
|
|
else
|
|
marker->flag |= SELECT;
|
|
}
|
|
else if (selectmode == SELECT_ADD)
|
|
marker->flag |= SELECT;
|
|
else if (selectmode == SELECT_SUBTRACT)
|
|
marker->flag &= ~SELECT;
|
|
|
|
std_rmouse_transform(transform_markers);
|
|
|
|
allqueue(REDRAWMARKER, 0);
|
|
}
|
|
else if (pmarker) {
|
|
/* action's markers are drawn behind scene markers */
|
|
if (selectmode == SELECT_REPLACE) {
|
|
action_set_activemarker(act, pmarker, 1);
|
|
pmarker->flag |= SELECT;
|
|
}
|
|
else if (selectmode == SELECT_INVERT) {
|
|
if (pmarker->flag & SELECT) {
|
|
pmarker->flag &= ~SELECT;
|
|
action_set_activemarker(act, NULL, 0);
|
|
}
|
|
else {
|
|
pmarker->flag |= SELECT;
|
|
action_set_activemarker(act, pmarker, 0);
|
|
}
|
|
}
|
|
else if (selectmode == SELECT_ADD) {
|
|
pmarker->flag |= SELECT;
|
|
action_set_activemarker(act, pmarker, 0);
|
|
}
|
|
else if (selectmode == SELECT_SUBTRACT) {
|
|
pmarker->flag &= ~SELECT;
|
|
action_set_activemarker(act, NULL, 0);
|
|
}
|
|
|
|
// TODO: local-markers cannot be moved atm...
|
|
//std_rmouse_transform(transform_markers);
|
|
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWBUTSEDIT, 0);
|
|
}
|
|
else if (act_channel) {
|
|
/* must have been a channel */
|
|
switch (act_type) {
|
|
case ACTTYPE_ICU:
|
|
icu= (IpoCurve *)act_channel;
|
|
break;
|
|
case ACTTYPE_CONCHAN:
|
|
conchan= (bConstraintChannel *)act_channel;
|
|
break;
|
|
case ACTTYPE_ACHAN:
|
|
achan= (bActionChannel *)act_channel;
|
|
break;
|
|
case ACTTYPE_GROUP:
|
|
agrp= (bActionGroup *)act_channel;
|
|
break;
|
|
case ACTTYPE_GPLAYER:
|
|
gpl= (bGPDlayer *)act_channel;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
if (selectmode == SELECT_REPLACE) {
|
|
selectmode = SELECT_ADD;
|
|
|
|
deselect_action_keys(0, 0);
|
|
|
|
if (datatype == ACTCONT_ACTION) {
|
|
deselect_action_channels(0);
|
|
|
|
/* Highlight either an Action-Channel or Action-Group */
|
|
if (achan) {
|
|
achan->flag |= ACHAN_SELECTED;
|
|
hilight_channel(act, achan, 1);
|
|
select_poseelement_by_name(achan->name, 2); /* 2 is activate */
|
|
}
|
|
else if (agrp) {
|
|
agrp->flag |= AGRP_SELECTED;
|
|
set_active_actiongroup(act, agrp, 1);
|
|
}
|
|
}
|
|
else if (datatype == ACTCONT_GPENCIL) {
|
|
deselect_action_channels(0);
|
|
|
|
/* Highlight gpencil layer */
|
|
gpl->flag |= GP_LAYER_SELECT;
|
|
gpencil_layer_setactive(gpd, gpl);
|
|
}
|
|
}
|
|
|
|
if (icu)
|
|
select_icu_key(icu, selx, selectmode);
|
|
else if (conchan)
|
|
select_ipo_key(conchan->ipo, selx, selectmode);
|
|
else if (achan)
|
|
select_ipo_key(achan->ipo, selx, selectmode);
|
|
else if (agrp) {
|
|
for (achan= agrp->channels.first; achan && achan->grp==agrp; achan= achan->next) {
|
|
select_ipo_key(achan->ipo, selx, selectmode);
|
|
|
|
for (conchan=achan->constraintChannels.first; conchan; conchan=conchan->next)
|
|
select_ipo_key(conchan->ipo, selx, selectmode);
|
|
}
|
|
}
|
|
else if (gpl)
|
|
select_gpencil_frame(gpl, (int)selx, selectmode);
|
|
|
|
std_rmouse_transform(transform_action_keys);
|
|
|
|
allqueue(REDRAWIPO, 0);
|
|
allqueue(REDRAWVIEW3D, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWNLA, 0);
|
|
allqueue(REDRAWOOPS, 0);
|
|
allqueue(REDRAWBUTSALL, 0);
|
|
}
|
|
}
|
|
|
|
/* lefthand side - mouse-click */
|
|
static void mouse_actionchannels (short mval[])
|
|
{
|
|
bAction *act= G.saction->action;
|
|
void *data, *act_channel, *channel_owner;
|
|
short datatype, chantype;
|
|
|
|
/* determine what type of data we are operating on */
|
|
data = get_action_context(&datatype);
|
|
if (data == NULL) return;
|
|
|
|
/* get channel to work on */
|
|
act_channel= get_nearest_act_channel(mval, &chantype, &channel_owner);
|
|
|
|
/* action to take depends on what channel we've got */
|
|
switch (chantype) {
|
|
case ACTTYPE_GROUP:
|
|
{
|
|
bActionGroup *agrp= (bActionGroup *)act_channel;
|
|
|
|
if ((mval[0] < 16) && (agrp->channels.first)) {
|
|
/* toggle expand */
|
|
agrp->flag ^= AGRP_EXPANDED;
|
|
}
|
|
else if (mval[0] >= (NAMEWIDTH-16)) {
|
|
/* toggle protection/locking */
|
|
agrp->flag ^= AGRP_PROTECTED;
|
|
}
|
|
else {
|
|
/* select/deselect group */
|
|
if (G.qual == LR_SHIFTKEY) {
|
|
/* inverse selection status of group */
|
|
select_action_group(act, agrp, SELECT_INVERT);
|
|
}
|
|
else if (G.qual == (LR_CTRLKEY|LR_SHIFTKEY)) {
|
|
/* select all in group (and deselect everthing else) */
|
|
select_action_group_channels(act, agrp);
|
|
select_action_group(act, agrp, SELECT_ADD);
|
|
}
|
|
else {
|
|
/* select group by itself */
|
|
deselect_actionchannels(act, 0);
|
|
select_action_group(act, agrp, SELECT_ADD);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case ACTTYPE_ACHAN:
|
|
{
|
|
bActionChannel *achan= (bActionChannel *)act_channel;
|
|
|
|
if (mval[0] >= (NAMEWIDTH-16)) {
|
|
/* toggle protect */
|
|
achan->flag ^= ACHAN_PROTECTED;
|
|
}
|
|
else if ((mval[0] >= (NAMEWIDTH-32)) && (achan->ipo)) {
|
|
/* toggle mute */
|
|
achan->ipo->muteipo = (achan->ipo->muteipo)? 0: 1;
|
|
}
|
|
else if (mval[0] <= 17) {
|
|
/* toggle expand */
|
|
achan->flag ^= ACHAN_EXPANDED;
|
|
}
|
|
else {
|
|
/* select/deselect achan */
|
|
if (G.qual & LR_SHIFTKEY) {
|
|
select_channel(act, achan, SELECT_INVERT);
|
|
}
|
|
else {
|
|
deselect_actionchannels(act, 0);
|
|
select_channel(act, achan, SELECT_ADD);
|
|
}
|
|
|
|
/* messy... set active bone */
|
|
select_poseelement_by_name(achan->name, 2);
|
|
}
|
|
}
|
|
break;
|
|
case ACTTYPE_FILLIPO:
|
|
{
|
|
bActionChannel *achan= (bActionChannel *)act_channel;
|
|
|
|
achan->flag ^= ACHAN_SHOWIPO;
|
|
|
|
if ((mval[0] > 24) && (achan->flag & ACHAN_SHOWIPO)) {
|
|
/* select+make active achan */
|
|
deselect_actionchannels(act, 0);
|
|
select_channel(act, achan, SELECT_ADD);
|
|
|
|
/* messy... set active bone */
|
|
select_poseelement_by_name(achan->name, 2);
|
|
}
|
|
}
|
|
break;
|
|
case ACTTYPE_FILLCON:
|
|
{
|
|
bActionChannel *achan= (bActionChannel *)act_channel;
|
|
|
|
achan->flag ^= ACHAN_SHOWCONS;
|
|
|
|
if ((mval[0] > 24) && (achan->flag & ACHAN_SHOWCONS)) {
|
|
/* select+make active achan */
|
|
deselect_actionchannels(act, 0);
|
|
select_channel(act, achan, SELECT_ADD);
|
|
|
|
/* messy... set active bone */
|
|
select_poseelement_by_name(achan->name, 2);
|
|
}
|
|
}
|
|
break;
|
|
case ACTTYPE_ICU:
|
|
{
|
|
IpoCurve *icu= (IpoCurve *)act_channel;
|
|
|
|
#if 0 /* disabled until all ipo tools support this -------> */
|
|
if (mval[0] >= (NAMEWIDTH-16)) {
|
|
/* toggle protection */
|
|
icu->flag ^= IPO_PROTECT;
|
|
}
|
|
#endif /* <------- end of disabled code */
|
|
if (mval[0] >= (NAMEWIDTH-16)) {
|
|
/* toggle mute */
|
|
icu->flag ^= IPO_MUTE;
|
|
}
|
|
else {
|
|
/* select/deselect */
|
|
select_icu_channel(act, icu, SELECT_INVERT);
|
|
}
|
|
|
|
allspace(REMAKEIPO, 0);
|
|
}
|
|
break;
|
|
case ACTTYPE_CONCHAN:
|
|
{
|
|
bConstraintChannel *conchan= (bConstraintChannel *)act_channel;
|
|
|
|
if (mval[0] >= (NAMEWIDTH-16)) {
|
|
/* toggle protection */
|
|
conchan->flag ^= CONSTRAINT_CHANNEL_PROTECTED;
|
|
}
|
|
else if ((mval[0] >= (NAMEWIDTH-32)) && (conchan->ipo)) {
|
|
/* toggle mute */
|
|
conchan->ipo->muteipo = (conchan->ipo->muteipo)? 0: 1;
|
|
}
|
|
else {
|
|
/* select/deselect */
|
|
select_constraint_channel(act, conchan, SELECT_INVERT);
|
|
}
|
|
}
|
|
break;
|
|
case ACTTYPE_GPDATABLOCK:
|
|
{
|
|
bGPdata *gpd= (bGPdata *)act_channel;
|
|
|
|
/* toggle expand */
|
|
gpd->flag ^= GP_DATA_EXPAND;
|
|
}
|
|
break;
|
|
case ACTTYPE_GPLAYER:
|
|
{
|
|
bGPdata *gpd= (bGPdata *)channel_owner;
|
|
bGPDlayer *gpl= (bGPDlayer *)act_channel;
|
|
|
|
if (mval[0] >= (NAMEWIDTH-16)) {
|
|
/* toggle lock */
|
|
gpl->flag ^= GP_LAYER_LOCKED;
|
|
}
|
|
else if (mval[0] >= (NAMEWIDTH-32)) {
|
|
/* toggle hide */
|
|
gpl->flag ^= GP_LAYER_HIDE;
|
|
}
|
|
else {
|
|
/* select/deselect */
|
|
if (G.qual & LR_SHIFTKEY) {
|
|
select_gplayer_channel(gpd, gpl, SELECT_INVERT);
|
|
}
|
|
else {
|
|
deselect_gpencil_layers(data, 0);
|
|
select_gplayer_channel(gpd, gpl, SELECT_INVERT);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
allqueue(REDRAWIPO, 0);
|
|
allqueue(REDRAWVIEW3D, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWNLA, 0);
|
|
allqueue(REDRAWTIME, 0);
|
|
allqueue(REDRAWOOPS, 0);
|
|
allqueue(REDRAWBUTSALL, 0);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
/* ACTION CHANNEL RE-ORDERING */
|
|
|
|
/* make sure all action-channels belong to a group (and clear action's list) */
|
|
static void split_groups_action_temp (bAction *act, bActionGroup *tgrp)
|
|
{
|
|
bActionChannel *achan;
|
|
bActionGroup *agrp;
|
|
|
|
/* Separate action-channels into lists per group */
|
|
for (agrp= act->groups.first; agrp; agrp= agrp->next) {
|
|
if (agrp->channels.first) {
|
|
achan= agrp->channels.last;
|
|
act->chanbase.first= achan->next;
|
|
|
|
achan= agrp->channels.first;
|
|
achan->prev= NULL;
|
|
|
|
achan= agrp->channels.last;
|
|
achan->next= NULL;
|
|
}
|
|
}
|
|
|
|
/* Initialise memory for temp-group */
|
|
memset(tgrp, 0, sizeof(bActionGroup));
|
|
tgrp->flag |= (AGRP_EXPANDED|AGRP_TEMP);
|
|
strcpy(tgrp->name, "#TempGroup");
|
|
|
|
/* Move any action-channels not already moved, to the temp group */
|
|
if (act->chanbase.first) {
|
|
/* start of list */
|
|
achan= act->chanbase.first;
|
|
achan->prev= NULL;
|
|
tgrp->channels.first= achan;
|
|
act->chanbase.first= NULL;
|
|
|
|
/* end of list */
|
|
achan= act->chanbase.last;
|
|
achan->next= NULL;
|
|
tgrp->channels.last= achan;
|
|
act->chanbase.last= NULL;
|
|
}
|
|
|
|
/* Add temp-group to list */
|
|
BLI_addtail(&act->groups, tgrp);
|
|
}
|
|
|
|
/* link lists of channels that groups have */
|
|
static void join_groups_action_temp (bAction *act)
|
|
{
|
|
bActionGroup *agrp;
|
|
bActionChannel *achan;
|
|
|
|
for (agrp= act->groups.first; agrp; agrp= agrp->next) {
|
|
ListBase tempGroup;
|
|
|
|
/* add list of channels to action's channels */
|
|
tempGroup= agrp->channels;
|
|
addlisttolist(&act->chanbase, &agrp->channels);
|
|
agrp->channels= tempGroup;
|
|
|
|
/* clear moved flag */
|
|
agrp->flag &= ~AGRP_MOVED;
|
|
|
|
/* if temp-group... remove from list (but don't free as it's on the stack!) */
|
|
if (agrp->flag & AGRP_TEMP) {
|
|
BLI_remlink(&act->groups, agrp);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* clear "moved" flag from all achans */
|
|
for (achan= act->chanbase.first; achan; achan= achan->next)
|
|
achan->flag &= ~ACHAN_MOVED;
|
|
}
|
|
|
|
|
|
static short rearrange_actchannel_is_ok (Link *channel, short type)
|
|
{
|
|
if (type == ACTTYPE_GROUP) {
|
|
bActionGroup *agrp= (bActionGroup *)channel;
|
|
|
|
if (SEL_AGRP(agrp) && !(agrp->flag & AGRP_MOVED))
|
|
return 1;
|
|
}
|
|
else if (type == ACTTYPE_ACHAN) {
|
|
bActionChannel *achan= (bActionChannel *)channel;
|
|
|
|
if (VISIBLE_ACHAN(achan) && SEL_ACHAN(achan) && !(achan->flag & ACHAN_MOVED))
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static short rearrange_actchannel_after_ok (Link *channel, short type)
|
|
{
|
|
if (type == ACTTYPE_GROUP) {
|
|
bActionGroup *agrp= (bActionGroup *)channel;
|
|
|
|
if (agrp->flag & AGRP_TEMP)
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
static short rearrange_actchannel_top (ListBase *list, Link *channel, short type)
|
|
{
|
|
if (rearrange_actchannel_is_ok(channel, type)) {
|
|
/* take it out off the chain keep data */
|
|
BLI_remlink(list, channel);
|
|
|
|
/* make it first element */
|
|
BLI_insertlinkbefore(list, list->first, channel);
|
|
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static short rearrange_actchannel_up (ListBase *list, Link *channel, short type)
|
|
{
|
|
if (rearrange_actchannel_is_ok(channel, type)) {
|
|
Link *prev= channel->prev;
|
|
|
|
if (prev) {
|
|
/* take it out off the chain keep data */
|
|
BLI_remlink(list, channel);
|
|
|
|
/* push it up */
|
|
BLI_insertlinkbefore(list, prev, channel);
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static short rearrange_actchannel_down (ListBase *list, Link *channel, short type)
|
|
{
|
|
if (rearrange_actchannel_is_ok(channel, type)) {
|
|
Link *next = (channel->next) ? channel->next->next : NULL;
|
|
|
|
if (next) {
|
|
/* take it out off the chain keep data */
|
|
BLI_remlink(list, channel);
|
|
|
|
/* move it down */
|
|
BLI_insertlinkbefore(list, next, channel);
|
|
|
|
return 1;
|
|
}
|
|
else if (rearrange_actchannel_after_ok(list->last, type)) {
|
|
/* take it out off the chain keep data */
|
|
BLI_remlink(list, channel);
|
|
|
|
/* add at end */
|
|
BLI_addtail(list, channel);
|
|
|
|
return 1;
|
|
}
|
|
else {
|
|
/* take it out off the chain keep data */
|
|
BLI_remlink(list, channel);
|
|
|
|
/* add just before end */
|
|
BLI_insertlinkbefore(list, list->last, channel);
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static short rearrange_actchannel_bottom (ListBase *list, Link *channel, short type)
|
|
{
|
|
if (rearrange_actchannel_is_ok(channel, type)) {
|
|
if (rearrange_actchannel_after_ok(list->last, type)) {
|
|
/* take it out off the chain keep data */
|
|
BLI_remlink(list, channel);
|
|
|
|
/* add at end */
|
|
BLI_addtail(list, channel);
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Change the order of action-channels
|
|
* mode: REARRANGE_ACTCHAN_*
|
|
*/
|
|
void rearrange_action_channels (short mode)
|
|
{
|
|
bAction *act;
|
|
bActionChannel *achan, *chan;
|
|
bActionGroup *agrp, *grp;
|
|
bActionGroup tgrp;
|
|
|
|
void *data;
|
|
short datatype;
|
|
|
|
short (*rearrange_func)(ListBase *, Link *, short);
|
|
short do_channels = 1;
|
|
char undostr[60];
|
|
|
|
/* Get the active action, exit if none are selected */
|
|
data = get_action_context(&datatype);
|
|
if (data == NULL) return;
|
|
if (datatype != ACTCONT_ACTION) return;
|
|
act= (bAction *)data;
|
|
|
|
/* exit if invalid mode */
|
|
switch (mode) {
|
|
case REARRANGE_ACTCHAN_TOP:
|
|
strcpy(undostr, "Channel(s) to Top");
|
|
rearrange_func= rearrange_actchannel_top;
|
|
break;
|
|
case REARRANGE_ACTCHAN_UP:
|
|
strcpy(undostr, "Channel(s) Move Up");
|
|
rearrange_func= rearrange_actchannel_up;
|
|
break;
|
|
case REARRANGE_ACTCHAN_DOWN:
|
|
strcpy(undostr, "Channel(s) Move Down");
|
|
rearrange_func= rearrange_actchannel_down;
|
|
break;
|
|
case REARRANGE_ACTCHAN_BOTTOM:
|
|
strcpy(undostr, "Channel(s) to Bottom");
|
|
rearrange_func= rearrange_actchannel_bottom;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
/* make sure we're only operating with groups */
|
|
split_groups_action_temp(act, &tgrp);
|
|
|
|
/* rearrange groups first (and then, only consider channels if the groups weren't moved) */
|
|
#define GET_FIRST(list) ((mode > 0) ? (list.first) : (list.last))
|
|
#define GET_NEXT(item) ((mode > 0) ? (item->next) : (item->prev))
|
|
|
|
for (agrp= GET_FIRST(act->groups); agrp; agrp= grp) {
|
|
/* Get next group to consider */
|
|
grp= GET_NEXT(agrp);
|
|
|
|
/* try to do group first */
|
|
if (rearrange_func(&act->groups, (Link *)agrp, ACTTYPE_GROUP)) {
|
|
do_channels= 0;
|
|
agrp->flag |= AGRP_MOVED;
|
|
}
|
|
}
|
|
|
|
if (do_channels) {
|
|
for (agrp= GET_FIRST(act->groups); agrp; agrp= grp) {
|
|
/* Get next group to consider */
|
|
grp= GET_NEXT(agrp);
|
|
|
|
/* only consider action-channels if they're visible (group expanded) */
|
|
if (EXPANDED_AGRP(agrp)) {
|
|
for (achan= GET_FIRST(agrp->channels); achan; achan= chan) {
|
|
/* Get next channel to consider */
|
|
chan= GET_NEXT(achan);
|
|
|
|
/* Try to do channel */
|
|
if (rearrange_func(&agrp->channels, (Link *)achan, ACTTYPE_ACHAN))
|
|
achan->flag |= ACHAN_MOVED;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#undef GET_FIRST
|
|
#undef GET_NEXT
|
|
|
|
/* assemble lists into one list (and clear moved tags) */
|
|
join_groups_action_temp(act);
|
|
|
|
/* Undo + redraw */
|
|
BIF_undo_push(undostr);
|
|
allqueue(REDRAWACTION, 0);
|
|
}
|
|
|
|
/* ******************************************************************* */
|
|
/* CHANNEL VISIBILITY/FOLDING */
|
|
|
|
/* Expand all channels to show full hierachy */
|
|
void expand_all_action (void)
|
|
{
|
|
void *data;
|
|
short datatype;
|
|
|
|
bAction *act;
|
|
bActionChannel *achan;
|
|
bActionGroup *agrp;
|
|
short mode= 1;
|
|
|
|
/* Get the selected action, exit if none are selected */
|
|
data = get_action_context(&datatype);
|
|
if (data == NULL) return;
|
|
if (datatype != ACTCONT_ACTION) return;
|
|
act= (bAction *)data;
|
|
|
|
/* check if expand all, or close all */
|
|
for (agrp= act->groups.first; agrp; agrp= agrp->next) {
|
|
if (EXPANDED_AGRP(agrp)) {
|
|
mode= 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (mode == 0) {
|
|
for (achan= act->chanbase.first; achan; achan= achan->next) {
|
|
if (VISIBLE_ACHAN(achan)) {
|
|
if (EXPANDED_ACHAN(achan)) {
|
|
mode= 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* expand/collapse depending on mode */
|
|
for (agrp= act->groups.first; agrp; agrp= agrp->next) {
|
|
if (mode == 1)
|
|
agrp->flag |= AGRP_EXPANDED;
|
|
else
|
|
agrp->flag &= ~AGRP_EXPANDED;
|
|
}
|
|
|
|
for (achan=act->chanbase.first; achan; achan= achan->next) {
|
|
if (VISIBLE_ACHAN(achan)) {
|
|
if (mode == 1)
|
|
achan->flag |= (ACHAN_EXPANDED|ACHAN_SHOWIPO|ACHAN_SHOWCONS);
|
|
else
|
|
achan->flag &= ~(ACHAN_EXPANDED|ACHAN_SHOWIPO|ACHAN_SHOWCONS);
|
|
}
|
|
}
|
|
|
|
/* Cleanup and do redraws */
|
|
BIF_undo_push("Expand Action Hierachy");
|
|
allqueue(REDRAWACTION, 0);
|
|
}
|
|
|
|
/* Expands those groups which are hiding a selected actionchannel */
|
|
void expand_obscuregroups_action (void)
|
|
{
|
|
void *data;
|
|
short datatype;
|
|
|
|
bAction *act;
|
|
bActionChannel *achan;
|
|
|
|
/* Get the selected action, exit if none are selected */
|
|
data = get_action_context(&datatype);
|
|
if (data == NULL) return;
|
|
if (datatype != ACTCONT_ACTION) return;
|
|
act= (bAction *)data;
|
|
|
|
/* check if expand all, or close all */
|
|
for (achan= act->chanbase.first; achan; achan= achan->next) {
|
|
if (VISIBLE_ACHAN(achan) && SEL_ACHAN(achan)) {
|
|
if (achan->grp)
|
|
achan->grp->flag |= AGRP_EXPANDED;
|
|
}
|
|
}
|
|
|
|
/* Cleanup and do redraws */
|
|
BIF_undo_push("Show Group-Hidden Channels");
|
|
allqueue(REDRAWACTION, 0);
|
|
}
|
|
|
|
/* For visible channels, expand/collapse one level */
|
|
void openclose_level_action (short mode)
|
|
{
|
|
void *data;
|
|
short datatype;
|
|
|
|
bAction *act;
|
|
bActionChannel *achan;
|
|
bActionGroup *agrp;
|
|
|
|
/* Get the selected action, exit if none are selected */
|
|
data = get_action_context(&datatype);
|
|
if (data == NULL) return;
|
|
if (datatype != ACTCONT_ACTION) return;
|
|
act= (bAction *)data;
|
|
|
|
/* Abort if no operation required */
|
|
if (mode == 0) return;
|
|
|
|
/* Only affect selected channels */
|
|
for (achan= act->chanbase.first; achan; achan= achan->next) {
|
|
/* make sure if there is a group, it isn't about to be collapsed and is open */
|
|
if ( (achan->grp==NULL) || (EXPANDED_AGRP(achan->grp) && SEL_AGRP(achan->grp)==0) ) {
|
|
if (VISIBLE_ACHAN(achan) && SEL_ACHAN(achan)) {
|
|
if (EXPANDED_ACHAN(achan)) {
|
|
if (FILTER_IPO_ACHAN(achan) || FILTER_CON_ACHAN(achan)) {
|
|
if (mode < 0)
|
|
achan->flag &= ~(ACHAN_SHOWIPO|ACHAN_SHOWCONS);
|
|
}
|
|
else {
|
|
if (mode > 0)
|
|
achan->flag |= (ACHAN_SHOWIPO|ACHAN_SHOWCONS);
|
|
else
|
|
achan->flag &= ~ACHAN_EXPANDED;
|
|
}
|
|
}
|
|
else {
|
|
if (mode > 0)
|
|
achan->flag |= ACHAN_EXPANDED;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Expand/collapse selected groups */
|
|
for (agrp= act->groups.first; agrp; agrp= agrp->next) {
|
|
if (SEL_AGRP(agrp)) {
|
|
if (mode < 0)
|
|
agrp->flag &= ~AGRP_EXPANDED;
|
|
else
|
|
agrp->flag |= AGRP_EXPANDED;
|
|
}
|
|
}
|
|
|
|
/* Cleanup and do redraws */
|
|
BIF_undo_push("Expand/Collapse Action Level");
|
|
allqueue(REDRAWACTION, 0);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
/* ACTION MARKERS (PoseLib features) */
|
|
/* NOTE: yes, these duplicate code from edittime.c a bit, but these do a bit more...
|
|
* These could get merged with those someday if need be... (Aligorith, 20071230)
|
|
*/
|
|
|
|
/* Makes the given marker the active one
|
|
* - deselect indicates whether unactive ones should be deselected too
|
|
*/
|
|
void action_set_activemarker (bAction *act, TimeMarker *active, short deselect)
|
|
{
|
|
TimeMarker *marker;
|
|
int index= 0;
|
|
|
|
/* sanity checks */
|
|
if (act == NULL)
|
|
return;
|
|
act->active_marker= 0;
|
|
|
|
/* set appropriate flags for all markers */
|
|
for (marker=act->markers.first; marker; marker=marker->next, index++) {
|
|
/* only active may be active */
|
|
if (marker == active) {
|
|
act->active_marker= index + 1;
|
|
marker->flag |= (SELECT|ACTIVE);
|
|
}
|
|
else {
|
|
if (deselect)
|
|
marker->flag &= ~(SELECT|ACTIVE);
|
|
else
|
|
marker->flag &= ~ACTIVE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Adds a local marker to the active action */
|
|
void action_add_localmarker (bAction *act, int frame)
|
|
{
|
|
TimeMarker *marker;
|
|
char name[64];
|
|
|
|
/* sanity checks */
|
|
if (act == NULL)
|
|
return;
|
|
|
|
/* get name of marker */
|
|
sprintf(name, "Pose");
|
|
if (sbutton(name, 0, sizeof(name)-1, "Name: ") == 0)
|
|
return;
|
|
|
|
/* add marker to action - replaces any existing marker there */
|
|
for (marker= act->markers.first; marker; marker= marker->next) {
|
|
if (marker->frame == frame) {
|
|
BLI_strncpy(marker->name, name, sizeof(marker->name));
|
|
break;
|
|
}
|
|
}
|
|
if (marker == NULL) {
|
|
marker= MEM_callocN(sizeof(TimeMarker), "ActionMarker");
|
|
|
|
BLI_strncpy(marker->name, name, sizeof(marker->name));
|
|
marker->frame= frame;
|
|
|
|
BLI_addtail(&act->markers, marker);
|
|
}
|
|
|
|
/* validate the name */
|
|
BLI_uniquename(&act->markers, marker, "Pose", offsetof(TimeMarker, name), 64);
|
|
|
|
/* sets the newly added marker as the active one */
|
|
action_set_activemarker(act, marker, 1);
|
|
|
|
BIF_undo_push("Action Add Marker");
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWBUTSEDIT, 0);
|
|
}
|
|
|
|
/* Renames the active local marker to the active action */
|
|
void action_rename_localmarker (bAction *act)
|
|
{
|
|
TimeMarker *marker;
|
|
char name[64];
|
|
int val;
|
|
|
|
/* sanity checks */
|
|
if (act == NULL)
|
|
return;
|
|
|
|
/* get active marker to rename */
|
|
if (act->active_marker == 0)
|
|
return;
|
|
else
|
|
val= act->active_marker;
|
|
|
|
if (val <= 0) return;
|
|
marker= BLI_findlink(&act->markers, val-1);
|
|
if (marker == NULL) return;
|
|
|
|
/* get name of marker */
|
|
sprintf(name, marker->name);
|
|
if (sbutton(name, 0, sizeof(name)-1, "Name: ") == 0)
|
|
return;
|
|
|
|
/* copy then validate name */
|
|
BLI_strncpy(marker->name, name, sizeof(marker->name));
|
|
BLI_uniquename(&act->markers, marker, "Pose", offsetof(TimeMarker, name), 64);
|
|
|
|
/* undo and update */
|
|
BIF_undo_push("Action Rename Marker");
|
|
allqueue(REDRAWBUTSEDIT, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
}
|
|
|
|
/* Deletes all selected markers, and adjusts things as appropriate */
|
|
void action_remove_localmarkers (bAction *act)
|
|
{
|
|
TimeMarker *marker, *next;
|
|
|
|
/* sanity checks */
|
|
if (act == NULL)
|
|
return;
|
|
|
|
/* remove selected markers */
|
|
for (marker= act->markers.first; marker; marker= next) {
|
|
next= marker->next;
|
|
|
|
if (marker->flag & SELECT)
|
|
BLI_freelinkN(&act->markers, marker);
|
|
}
|
|
|
|
/* clear active just in case */
|
|
act->active_marker= 0;
|
|
|
|
/* undo and update */
|
|
BIF_undo_push("Action Remove Marker(s)");
|
|
allqueue(REDRAWBUTSEDIT, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
/* EVENT HANDLING */
|
|
|
|
void winqreadactionspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
|
|
{
|
|
extern void do_actionbuts(unsigned short event); // drawaction.c
|
|
SpaceAction *saction;
|
|
void *data;
|
|
short datatype;
|
|
float dx, dy;
|
|
int doredraw= 0;
|
|
int cfra;
|
|
short mval[2];
|
|
unsigned short event= evt->event;
|
|
short val= evt->val;
|
|
short mousebut = L_MOUSE;
|
|
|
|
if (curarea->win==0) return;
|
|
|
|
saction= curarea->spacedata.first;
|
|
if (!saction)
|
|
return;
|
|
|
|
data= get_action_context(&datatype);
|
|
|
|
if (val) {
|
|
if ( uiDoBlocks(&curarea->uiblocks, event, 1)!=UI_NOTHING ) event= 0;
|
|
|
|
/* swap mouse buttons based on user preference */
|
|
if (U.flag & USER_LMOUSESELECT) {
|
|
if (event == LEFTMOUSE) {
|
|
event = RIGHTMOUSE;
|
|
mousebut = L_MOUSE;
|
|
}
|
|
else if (event == RIGHTMOUSE) {
|
|
event = LEFTMOUSE;
|
|
mousebut = R_MOUSE;
|
|
}
|
|
}
|
|
|
|
getmouseco_areawin(mval);
|
|
|
|
switch(event) {
|
|
case UI_BUT_EVENT:
|
|
do_actionbuts(val); /* window itself */
|
|
break;
|
|
|
|
/* LEFTMOUSE and RIGHTMOUSE event codes can be swapped above,
|
|
* based on user preference USER_LMOUSESELECT
|
|
*/
|
|
case LEFTMOUSE:
|
|
if (view2dmove(LEFTMOUSE)) /* only checks for sliders */
|
|
break;
|
|
else if ((G.v2d->mask.xmin==0) || (mval[0] > ACTWIDTH)) {
|
|
/* moving time-marker / current frame */
|
|
do {
|
|
getmouseco_areawin(mval);
|
|
areamouseco_to_ipoco(G.v2d, mval, &dx, &dy);
|
|
|
|
cfra= (int)(dx+0.5f);
|
|
if (cfra < 1) cfra= 1;
|
|
|
|
if (cfra != CFRA) {
|
|
CFRA= cfra;
|
|
update_for_newframe();
|
|
force_draw_all(0);
|
|
}
|
|
else
|
|
PIL_sleep_ms(30);
|
|
}
|
|
while(get_mbut() & mousebut);
|
|
break;
|
|
}
|
|
/* passed on as selection */
|
|
case RIGHTMOUSE:
|
|
/* Clicking in the channel area */
|
|
if ((G.v2d->mask.xmin) && (mval[0] < NAMEWIDTH)) {
|
|
if (ELEM(datatype, ACTCONT_ACTION, ACTCONT_GPENCIL)) {
|
|
/* mouse is over action channels */
|
|
if (G.qual == LR_CTRLKEY)
|
|
numbuts_action();
|
|
else
|
|
mouse_actionchannels(mval);
|
|
}
|
|
else
|
|
numbuts_action();
|
|
}
|
|
else {
|
|
short select_mode= (G.qual & LR_SHIFTKEY)? SELECT_INVERT: SELECT_REPLACE;
|
|
|
|
/* Clicking in the vertical scrollbar selects
|
|
* all of the keys for that channel at that height
|
|
*/
|
|
if (IN_2D_VERT_SCROLL(mval))
|
|
selectall_action_keys(mval, 0, select_mode);
|
|
|
|
/* 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))
|
|
selectall_action_keys(mval, 1, select_mode);
|
|
|
|
/* Clicking in the main area of the action window
|
|
* selects keys and markers
|
|
*/
|
|
else if (G.qual & LR_ALTKEY) {
|
|
areamouseco_to_ipoco(G.v2d, mval, &dx, &dy);
|
|
|
|
/* sends a 1 for left and 0 for right */
|
|
selectkeys_leftright((dx < (float)CFRA), select_mode);
|
|
}
|
|
else
|
|
mouse_action(select_mode);
|
|
}
|
|
break;
|
|
|
|
case MIDDLEMOUSE:
|
|
case WHEELUPMOUSE:
|
|
case WHEELDOWNMOUSE:
|
|
view2dmove(event); /* in drawipo.c */
|
|
break;
|
|
|
|
case AKEY:
|
|
if (mval[0] < NAMEWIDTH) {
|
|
deselect_action_channels(1);
|
|
BIF_undo_push("(De)Select Action Channels");
|
|
allqueue(REDRAWVIEW3D, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWNLA, 0);
|
|
allqueue(REDRAWIPO, 0);
|
|
}
|
|
else if (mval[0] > ACTWIDTH) {
|
|
if (G.qual == LR_CTRLKEY) {
|
|
deselect_markers(1, 0);
|
|
BIF_undo_push("(De)Select Markers");
|
|
allqueue(REDRAWTIME, 0);
|
|
allqueue(REDRAWIPO, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWNLA, 0);
|
|
allqueue(REDRAWSOUND, 0);
|
|
}
|
|
else {
|
|
deselect_action_keys(1, 1);
|
|
BIF_undo_push("(De)Select Keys");
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWNLA, 0);
|
|
allqueue(REDRAWIPO, 0);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case BKEY:
|
|
if (G.qual & LR_CTRLKEY) {
|
|
borderselect_markers();
|
|
}
|
|
else {
|
|
if (mval[0] <= ACTWIDTH)
|
|
borderselect_actionchannels();
|
|
else
|
|
borderselect_action();
|
|
}
|
|
break;
|
|
|
|
case CKEY:
|
|
/* scroll the window so the current
|
|
* frame is in the center.
|
|
*/
|
|
center_currframe();
|
|
break;
|
|
|
|
case DKEY:
|
|
if (mval[0] > ACTWIDTH) {
|
|
if (G.qual == (LR_CTRLKEY|LR_SHIFTKEY))
|
|
duplicate_marker();
|
|
else if (G.qual == LR_SHIFTKEY)
|
|
duplicate_action_keys();
|
|
}
|
|
break;
|
|
|
|
case EKEY:
|
|
if (mval[0] >= ACTWIDTH)
|
|
transform_action_keys('e', 0);
|
|
break;
|
|
|
|
case GKEY:
|
|
/* Action Channel Groups */
|
|
if (G.qual == LR_SHIFTKEY)
|
|
action_groups_group(0);
|
|
else if (G.qual == (LR_CTRLKEY|LR_SHIFTKEY))
|
|
action_groups_group(1);
|
|
else if (G.qual == LR_ALTKEY)
|
|
action_groups_ungroup();
|
|
|
|
/* Transforms */
|
|
else {
|
|
if (mval[0] >= ACTWIDTH) {
|
|
if (G.qual == LR_CTRLKEY)
|
|
transform_markers('g', 0);
|
|
else
|
|
transform_action_keys('g', 0);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case HKEY:
|
|
if (G.qual & LR_SHIFTKEY) {
|
|
if (okee("Set Keys to Auto Handle"))
|
|
sethandles_action_keys(HD_AUTO);
|
|
}
|
|
else {
|
|
if (okee("Toggle Keys Aligned Handle"))
|
|
sethandles_action_keys(HD_ALIGN);
|
|
}
|
|
break;
|
|
|
|
case IKEY:
|
|
if (G.qual & LR_CTRLKEY) {
|
|
if (mval[0] < ACTWIDTH) {
|
|
deselect_action_channels(2);
|
|
BIF_undo_push("Inverse Action Channels");
|
|
allqueue(REDRAWVIEW3D, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWNLA, 0);
|
|
allqueue(REDRAWIPO, 0);
|
|
}
|
|
else if (G.qual & LR_SHIFTKEY) {
|
|
deselect_markers(0, 2);
|
|
BIF_undo_push("Inverse Markers");
|
|
allqueue(REDRAWMARKER, 0);
|
|
}
|
|
else {
|
|
deselect_action_keys(0, 2);
|
|
BIF_undo_push("Inverse Keys");
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWNLA, 0);
|
|
allqueue(REDRAWIPO, 0);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case KKEY:
|
|
if (G.qual == LR_ALTKEY)
|
|
markers_selectkeys_between();
|
|
else if (G.qual == LR_SHIFTKEY)
|
|
column_select_action_keys(2);
|
|
else if (G.qual == LR_CTRLKEY)
|
|
column_select_action_keys(3);
|
|
else
|
|
column_select_action_keys(1);
|
|
|
|
allqueue(REDRAWMARKER, 0);
|
|
break;
|
|
|
|
case LKEY:
|
|
/* poselib manipulation - only for actions */
|
|
if (datatype == ACTCONT_ACTION) {
|
|
if (G.qual == LR_SHIFTKEY)
|
|
action_add_localmarker(data, CFRA);
|
|
else if (G.qual == (LR_CTRLKEY|LR_SHIFTKEY))
|
|
action_rename_localmarker(data);
|
|
else if (G.qual == LR_ALTKEY)
|
|
action_remove_localmarkers(data);
|
|
else if (G.qual == LR_CTRLKEY) {
|
|
G.saction->flag |= SACTION_POSEMARKERS_MOVE;
|
|
transform_markers('g', 0);
|
|
G.saction->flag &= ~SACTION_POSEMARKERS_MOVE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MKEY:
|
|
if (G.qual & LR_SHIFTKEY) {
|
|
/* mirror keyframes */
|
|
if (data) {
|
|
if (G.saction->flag & SACTION_DRAWTIME)
|
|
val = pupmenu("Mirror Keys Over%t|Current Time%x1|Vertical Axis%x2|Horizontal Axis %x3|Selected Marker %x4");
|
|
else
|
|
val = pupmenu("Mirror Keys Over%t|Current Frame%x1|Vertical Axis%x2|Horizontal Axis %x3|Selected Marker %x4");
|
|
|
|
mirror_action_keys(val);
|
|
}
|
|
}
|
|
else {
|
|
/* marker operations */
|
|
if (G.qual == 0)
|
|
add_marker(CFRA);
|
|
else if (G.qual == LR_CTRLKEY)
|
|
rename_marker();
|
|
else
|
|
break;
|
|
allqueue(REDRAWMARKER, 0);
|
|
}
|
|
break;
|
|
|
|
case NKEY:
|
|
if (G.qual==0) {
|
|
/* panel will not always show useful info! */
|
|
if (mval[0] > ACTWIDTH) {
|
|
add_blockhandler(curarea, ACTION_HANDLER_PROPERTIES, UI_PNL_TO_MOUSE);
|
|
scrarea_queue_winredraw(curarea);
|
|
}
|
|
else
|
|
numbuts_action();
|
|
}
|
|
break;
|
|
|
|
case OKEY:
|
|
if (G.qual & LR_ALTKEY)
|
|
sample_action_keys();
|
|
else
|
|
clean_action();
|
|
break;
|
|
|
|
case PKEY:
|
|
if (G.qual == (LR_CTRLKEY|LR_ALTKEY)) /* set preview range to range of action */
|
|
action_previewrange_set(G.saction->action);
|
|
else if (G.qual & LR_CTRLKEY) /* set preview range */
|
|
anim_previewrange_set();
|
|
else if (G.qual & LR_ALTKEY) /* clear preview range */
|
|
anim_previewrange_clear();
|
|
|
|
allqueue(REDRAWMARKER, 0);
|
|
allqueue(REDRAWBUTSALL, 0);
|
|
break;
|
|
|
|
case SKEY:
|
|
if (mval[0]>=ACTWIDTH) {
|
|
if (G.qual == (LR_SHIFTKEY|LR_CTRLKEY)) {
|
|
if (data) {
|
|
snap_cfra_action();
|
|
}
|
|
}
|
|
else if (G.qual & LR_SHIFTKEY) {
|
|
if (data) {
|
|
if (G.saction->flag & SACTION_DRAWTIME)
|
|
val = pupmenu("Snap Keys To%t|Nearest Second%x4|Current Time%x2|Nearest Marker %x3");
|
|
else
|
|
val = pupmenu("Snap Keys To%t|Nearest Frame%x1|Current Frame%x2|Nearest Marker %x3");
|
|
|
|
snap_action_keys(val);
|
|
}
|
|
}
|
|
else {
|
|
transform_action_keys('s', 0);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TKEY:
|
|
if (G.qual & LR_SHIFTKEY)
|
|
action_set_ipo_flags(SET_IPO_POPUP, 0);
|
|
else if (G.qual & LR_CTRLKEY) {
|
|
val= pupmenu("Time value%t|Frames %x1|Seconds%x2");
|
|
|
|
if (val > 0) {
|
|
if (val == 2) saction->flag |= SACTION_DRAWTIME;
|
|
else saction->flag &= ~SACTION_DRAWTIME;
|
|
|
|
doredraw= 1;
|
|
}
|
|
}
|
|
else
|
|
transform_action_keys ('t', 0);
|
|
break;
|
|
|
|
case VKEY:
|
|
if (okee("Set Keys to Vector Handle"))
|
|
sethandles_action_keys(HD_VECT);
|
|
break;
|
|
|
|
case WKEY:
|
|
/* toggle/turn-on\off-based-on-setting */
|
|
if (G.qual) {
|
|
if (G.qual == (LR_CTRLKEY|LR_SHIFTKEY))
|
|
val= 1;
|
|
else if (G.qual == LR_ALTKEY)
|
|
val= 2;
|
|
else
|
|
val= 0;
|
|
|
|
setflag_action_channels(val);
|
|
}
|
|
break;
|
|
|
|
case PAGEUPKEY:
|
|
if (datatype == ACTCONT_ACTION) {
|
|
if (G.qual == (LR_CTRLKEY|LR_SHIFTKEY))
|
|
rearrange_action_channels(REARRANGE_ACTCHAN_TOP);
|
|
else if (G.qual == LR_SHIFTKEY)
|
|
rearrange_action_channels(REARRANGE_ACTCHAN_UP);
|
|
else if (G.qual == LR_CTRLKEY)
|
|
nextprev_action_keyframe(1);
|
|
else
|
|
nextprev_marker(1);
|
|
}
|
|
else if (datatype == ACTCONT_SHAPEKEY) {
|
|
/* only jump to markers possible (key channels can't be moved yet) */
|
|
if (G.qual == LR_CTRLKEY)
|
|
nextprev_action_keyframe(1);
|
|
else
|
|
nextprev_marker(1);
|
|
}
|
|
break;
|
|
case PAGEDOWNKEY:
|
|
if (datatype == ACTCONT_ACTION) {
|
|
if (G.qual == (LR_CTRLKEY|LR_SHIFTKEY))
|
|
rearrange_action_channels(REARRANGE_ACTCHAN_BOTTOM);
|
|
else if (G.qual == LR_SHIFTKEY)
|
|
rearrange_action_channels(REARRANGE_ACTCHAN_DOWN);
|
|
else if (G.qual == LR_CTRLKEY)
|
|
nextprev_action_keyframe(-1);
|
|
else
|
|
nextprev_marker(-1);
|
|
}
|
|
else if (datatype == ACTCONT_SHAPEKEY) {
|
|
/* only jump to markers possible (key channels can't be moved yet) */
|
|
if (G.qual == LR_CTRLKEY)
|
|
nextprev_action_keyframe(-1);
|
|
else
|
|
nextprev_marker(-1);
|
|
}
|
|
break;
|
|
|
|
case DELKEY:
|
|
case XKEY:
|
|
/* markers are incorported under shift-modifier (it does go against conventions, but oh well :/) */
|
|
if (G.qual == LR_SHIFTKEY) {
|
|
if (okee("Erase selected marker(s)?")) {
|
|
if (mval[0] >= NAMEWIDTH)
|
|
remove_marker();
|
|
}
|
|
}
|
|
else {
|
|
if (okee("Erase selected?")) {
|
|
if (mval[0] < NAMEWIDTH) {
|
|
if (datatype == ACTCONT_ACTION)
|
|
delete_action_channels();
|
|
else if (datatype == ACTCONT_GPENCIL)
|
|
delete_gpencil_layers();
|
|
}
|
|
else
|
|
delete_action_keys();
|
|
}
|
|
}
|
|
|
|
allqueue(REDRAWMARKER, 0);
|
|
|
|
break;
|
|
|
|
case ACCENTGRAVEKEY:
|
|
if (datatype == ACTCONT_ACTION) {
|
|
if (G.qual == LR_SHIFTKEY)
|
|
expand_obscuregroups_action();
|
|
else
|
|
expand_all_action();
|
|
}
|
|
break;
|
|
|
|
case PADPLUSKEY:
|
|
if (G.qual == LR_CTRLKEY) {
|
|
if (datatype == ACTCONT_ACTION)
|
|
openclose_level_action(1);
|
|
}
|
|
else {
|
|
view2d_zoom(G.v2d, 0.1154f, sa->winx, sa->winy);
|
|
test_view2d(G.v2d, sa->winx, sa->winy);
|
|
view2d_do_locks(curarea, V2D_LOCK_COPY);
|
|
|
|
doredraw= 1;
|
|
}
|
|
break;
|
|
case PADMINUS:
|
|
if (G.qual == LR_CTRLKEY) {
|
|
if (datatype == ACTCONT_ACTION)
|
|
openclose_level_action(-1);
|
|
}
|
|
else {
|
|
view2d_zoom(G.v2d, -0.15f, sa->winx, sa->winy);
|
|
test_view2d(G.v2d, sa->winx, sa->winy);
|
|
view2d_do_locks(curarea, V2D_LOCK_COPY);
|
|
|
|
doredraw= 1;
|
|
}
|
|
break;
|
|
|
|
case HOMEKEY:
|
|
do_action_buttons(B_ACTHOME); /* header */
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (doredraw) addqueue(curarea->win, REDRAW, 1);
|
|
}
|
|
|
|
/* **************************************************** */
|