== Action Editor - Groups for Action Channels (Peach Request) ==
Now, you can assign Action Channels to named (folder-like) groups, which help to organise the channels (important for more complex rigs). These are collapsible, can be "protected", and show a "summary" of the keyframes in the channels the Group contains. They are drawn as bright-green (active) or a darker shade of green (not active) channels. * Each Action has its own set of Groups. * An Action-Channel can only occur in one Group at a time. It can also not occur in any group. * Action-Channels can be moved between Groups * Groups + grouped-channels always occur BEFORE un-grouped channels Important Hotkeys: * Shift-G : Adds the selected Action-Channels to the Active Group. This will create a new group if need be * Ctrl-Shift-G : Always adds a new group, and adds the selected Action-Channels to it * Alt-G : Removes selected Action-Channels from their groups * Ctrl-Shift-Alt-G : (Note: this will be removed soon) This is a simple debugging-hotkey I added, which just prints a list of the groups, channels, and their addresses... * NKey / Ctrl-LMB: While hovering over the name of a group, this shows a popup like for other channels, which allows the editing of the channel's name, etc. Assorted Notes: * Some tools may not work yet with this (Ctrl Numpad+/- for example) * Fixed some bugs in various places in Action Editor code * Added theme colours for group channels * The nomenclature of these tools may change in future when a better alternative is found * The ability to auto-assign action-channels to groups when they are keyframed will be coming up shortly
This commit is contained in:
@@ -44,7 +44,7 @@ struct ListBase;
|
||||
struct MemFile;
|
||||
|
||||
#define BLENDER_VERSION 245
|
||||
#define BLENDER_SUBVERSION 12
|
||||
#define BLENDER_SUBVERSION 13
|
||||
|
||||
#define BLENDER_MINVERSION 240
|
||||
#define BLENDER_MINSUBVERSION 0
|
||||
|
||||
@@ -178,7 +178,11 @@ void free_action (bAction *act)
|
||||
if (act->chanbase.first)
|
||||
BLI_freelistN(&act->chanbase);
|
||||
|
||||
/* Free pose-references */
|
||||
/* Free groups */
|
||||
if (act->groups.first)
|
||||
BLI_freelistN(&act->groups);
|
||||
|
||||
/* Free pose-references (aka local markers) */
|
||||
if (act->markers.first)
|
||||
BLI_freelistN(&act->markers);
|
||||
}
|
||||
@@ -187,19 +191,37 @@ bAction *copy_action (bAction *src)
|
||||
{
|
||||
bAction *dst = NULL;
|
||||
bActionChannel *dchan, *schan;
|
||||
bActionGroup *dgrp, *sgrp;
|
||||
|
||||
if (!src) return NULL;
|
||||
|
||||
dst= copy_libblock(src);
|
||||
|
||||
duplicatelist(&(dst->chanbase), &(src->chanbase));
|
||||
duplicatelist(&(dst->groups), &(src->groups));
|
||||
duplicatelist(&(dst->markers), &(src->markers));
|
||||
|
||||
for (dchan=dst->chanbase.first, schan=src->chanbase.first; dchan; dchan=dchan->next, schan=schan->next){
|
||||
for (dchan=dst->chanbase.first, schan=src->chanbase.first; dchan; dchan=dchan->next, schan=schan->next) {
|
||||
for (dgrp=dst->groups.first, sgrp=src->groups.first; dgrp && sgrp; dgrp=dgrp->next, sgrp=sgrp->next) {
|
||||
if (dchan->grp == sgrp) {
|
||||
dchan->grp= dgrp;
|
||||
|
||||
if (dgrp->channels.first == schan)
|
||||
dgrp->channels.first= dchan;
|
||||
if (dgrp->channels.last == schan)
|
||||
dgrp->channels.last= dchan;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dchan->ipo = copy_ipo(dchan->ipo);
|
||||
copy_constraint_channels(&dchan->constraintChannels, &schan->constraintChannels);
|
||||
}
|
||||
|
||||
dst->id.flag |= LIB_FAKEUSER;
|
||||
dst->id.us++;
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
||||
@@ -1859,12 +1859,24 @@ static void direct_link_bones(FileData *fd, Bone* bone)
|
||||
static void direct_link_action(FileData *fd, bAction *act)
|
||||
{
|
||||
bActionChannel *achan;
|
||||
bActionGroup *agrp;
|
||||
|
||||
link_list(fd, &act->chanbase);
|
||||
link_list(fd, &act->groups);
|
||||
link_list(fd, &act->markers);
|
||||
|
||||
for (achan = act->chanbase.first; achan; achan=achan->next)
|
||||
for (achan = act->chanbase.first; achan; achan=achan->next) {
|
||||
achan->grp= newdataadr(fd, achan->grp);
|
||||
|
||||
link_list(fd, &achan->constraintChannels);
|
||||
}
|
||||
|
||||
for (agrp = act->groups.first; agrp; agrp= agrp->next) {
|
||||
if (agrp->channels.first) {
|
||||
agrp->channels.first= newdataadr(fd, agrp->channels.first);
|
||||
agrp->channels.last= newdataadr(fd, agrp->channels.last);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void direct_link_armature(FileData *fd, bArmature *arm)
|
||||
|
||||
@@ -1750,6 +1750,7 @@ static void write_actions(WriteData *wd, ListBase *idbase)
|
||||
{
|
||||
bAction *act;
|
||||
bActionChannel *chan;
|
||||
bActionGroup *grp;
|
||||
TimeMarker *marker;
|
||||
|
||||
for(act=idbase->first; act; act= act->id.next) {
|
||||
@@ -1762,6 +1763,10 @@ static void write_actions(WriteData *wd, ListBase *idbase)
|
||||
write_constraint_channels(wd, &chan->constraintChannels);
|
||||
}
|
||||
|
||||
for (grp=act->groups.first; grp; grp=grp->next) {
|
||||
writestruct(wd, DATA, "bActionGroup", 1, grp);
|
||||
}
|
||||
|
||||
for (marker=act->markers.first; marker; marker=marker->next) {
|
||||
writestruct(wd, DATA, "TimeMarker", 1, marker);
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ struct Ipo;
|
||||
struct IpoCurve;
|
||||
struct gla2DDrawInfo;
|
||||
struct bAction;
|
||||
struct bActionGroup;
|
||||
struct Object;
|
||||
struct ListBase;
|
||||
|
||||
@@ -75,14 +76,16 @@ void draw_cfra_action(void);
|
||||
/* Channel Drawing */
|
||||
void draw_icu_channel(struct gla2DDrawInfo *di, struct IpoCurve *icu, float ypos);
|
||||
void draw_ipo_channel(struct gla2DDrawInfo *di, struct Ipo *ipo, float ypos);
|
||||
void draw_action_channel(struct gla2DDrawInfo *di, bAction *act, float ypos);
|
||||
void draw_object_channel(struct gla2DDrawInfo *di, Object *ob, float ypos);
|
||||
void draw_agroup_channel(struct gla2DDrawInfo *di, struct bActionGroup *agrp, float ypos);
|
||||
void draw_action_channel(struct gla2DDrawInfo *di, struct bAction *act, float ypos);
|
||||
void draw_object_channel(struct gla2DDrawInfo *di, struct Object *ob, float ypos);
|
||||
|
||||
/* Keydata Generation */
|
||||
void icu_to_keylist(struct IpoCurve *icu, ListBase *keys, ListBase *blocks);
|
||||
void ipo_to_keylist(struct Ipo *ipo, ListBase *keys, ListBase *blocks);
|
||||
void action_to_keylist(bAction *act, ListBase *keys, ListBase *blocks);
|
||||
void ob_to_keylist(Object *ob, ListBase *keys, ListBase *blocks);
|
||||
void agroup_to_keylist(struct bActionGroup *agrp, ListBase *keys, ListBase *blocks);
|
||||
void action_to_keylist(struct bAction *act, ListBase *keys, ListBase *blocks);
|
||||
void ob_to_keylist(struct Object *ob, ListBase *keys, ListBase *blocks);
|
||||
|
||||
#endif /* BDR_DRAWACTION_H */
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
/* Some types for easier type-testing */
|
||||
enum {
|
||||
ACTTYPE_NONE= 0,
|
||||
ACTTYPE_GROUP,
|
||||
ACTTYPE_ACHAN,
|
||||
ACTTYPE_CONCHAN,
|
||||
ACTTYPE_ICU,
|
||||
@@ -53,6 +54,10 @@ enum {
|
||||
};
|
||||
|
||||
/* Macros for easier/more consistant state testing */
|
||||
#define EDITABLE_AGRP(agrp) ((agrp->flag & AGRP_PROTECTED)==0)
|
||||
#define EXPANDED_AGRP(agrp) (agrp->flag & AGRP_EXPANDED)
|
||||
#define SEL_AGRP(agrp) ((agrp->flag & AGRP_SELECTED) || (agrp->flag & AGRP_ACTIVE))
|
||||
|
||||
#define VISIBLE_ACHAN(achan) ((achan->flag & ACHAN_HIDDEN)==0)
|
||||
#define EDITABLE_ACHAN(achan) ((VISIBLE_ACHAN(achan)) && ((achan->flag & ACHAN_PROTECTED)==0))
|
||||
#define EXPANDED_ACHAN(achan) ((VISIBLE_ACHAN(achan)) && (achan->flag & ACHAN_EXPANDED))
|
||||
@@ -81,7 +86,6 @@ enum {
|
||||
|
||||
/* constants for setting ipo-extrapolation type */
|
||||
enum {
|
||||
|
||||
SET_EXTEND_MENU = 9,
|
||||
SET_EXTEND_POPUP = 10,
|
||||
|
||||
@@ -91,9 +95,19 @@ enum {
|
||||
SET_EXTEND_CYCLICEXTRAPOLATION
|
||||
};
|
||||
|
||||
/* constants for channel rearranging */
|
||||
/* WARNING: don't change exising ones without modifying rearrange func accordingly */
|
||||
enum {
|
||||
REARRANGE_ACTCHAN_TOP= -2,
|
||||
REARRANGE_ACTCHAN_UP= -1,
|
||||
REARRANGE_ACTCHAN_DOWN= 1,
|
||||
REARRANGE_ACTCHAN_BOTTOM= 2
|
||||
};
|
||||
|
||||
|
||||
struct bAction;
|
||||
struct bActionChannel;
|
||||
struct bActionGroup;
|
||||
struct bPoseChannel;
|
||||
struct Object;
|
||||
struct Ipo;
|
||||
@@ -125,11 +139,14 @@ void free_actcopybuf(void);
|
||||
void copy_actdata(void);
|
||||
void paste_actdata(void);
|
||||
|
||||
/* Channel/strip operations */
|
||||
void up_sel_action(void);
|
||||
void down_sel_action(void);
|
||||
void top_sel_action(void);
|
||||
void bottom_sel_action(void);
|
||||
/* Group/Channel Operations */
|
||||
struct bActionGroup *get_active_actiongroup(struct bAction *act);
|
||||
void set_active_actiongroup(struct bAction *act, struct bActionGroup *agrp, short select);
|
||||
void action_groups_group(short add_group);
|
||||
void action_groups_ungroup(void);
|
||||
|
||||
/* Channel/Strip Operations */
|
||||
void rearrange_action_channels(short mode);
|
||||
|
||||
void expand_all_action(void);
|
||||
void openclose_level_action(short mode);
|
||||
|
||||
@@ -37,9 +37,12 @@
|
||||
/* FILTERED ACTION DATA - TYPES */
|
||||
|
||||
/* types of keyframe data in ActListElem */
|
||||
#define ALE_NONE 0
|
||||
#define ALE_IPO 1
|
||||
#define ALE_ICU 2
|
||||
typedef enum ALE_KEYTYPE {
|
||||
ALE_NONE = 0,
|
||||
ALE_IPO,
|
||||
ALE_ICU,
|
||||
ALE_GROUP
|
||||
} ALE_KEYTYPE;
|
||||
|
||||
/* This struct defines a structure used for quick access */
|
||||
typedef struct bActListElem {
|
||||
@@ -53,6 +56,8 @@ typedef struct bActListElem {
|
||||
void *key_data; /* motion data - ipo or ipo-curve */
|
||||
short datatype; /* type of motion data to expect */
|
||||
|
||||
struct bActionGroup *grp; /* action group that owns the channel */
|
||||
|
||||
void *owner; /* will either be an action channel or fake ipo-channel (for keys) */
|
||||
short ownertype; /* type of owner */
|
||||
} bActListElem;
|
||||
@@ -61,17 +66,21 @@ typedef struct bActListElem {
|
||||
/* FILTER ACTION DATA - METHODS/TYPES */
|
||||
|
||||
/* filtering flags - under what circumstances should a channel be added */
|
||||
#define ACTFILTER_VISIBLE 0x001 /* should channels be visible */
|
||||
#define ACTFILTER_SEL 0x002 /* should channels be selected */
|
||||
#define ACTFILTER_FOREDIT 0x004 /* does editable status matter */
|
||||
#define ACTFILTER_CHANNELS 0x008 /* do we only care that it is a channel */
|
||||
#define ACTFILTER_IPOKEYS 0x010 /* only channels referencing ipo's */
|
||||
#define ACTFILTER_ONLYICU 0x020 /* only reference ipo-curves */
|
||||
#define ACTFILTER_FORDRAWING 0x040 /* make list for interface drawing */
|
||||
typedef enum ACTFILTER_FLAGS {
|
||||
ACTFILTER_VISIBLE = (1<<0), /* should channels be visible */
|
||||
ACTFILTER_SEL = (1<<1), /* should channels be selected */
|
||||
ACTFILTER_FOREDIT = (1<<2), /* does editable status matter */
|
||||
ACTFILTER_CHANNELS = (1<<3), /* do we only care that it is a channel */
|
||||
ACTFILTER_IPOKEYS = (1<<4), /* only channels referencing ipo's */
|
||||
ACTFILTER_ONLYICU = (1<<5), /* only reference ipo-curves */
|
||||
ACTFILTER_FORDRAWING = (1<<6) /* make list for interface drawing */
|
||||
} ACTFILTER_FLAGS;
|
||||
|
||||
/* Action Editor - Main Data types */
|
||||
#define ACTCONT_NONE 0
|
||||
#define ACTCONT_ACTION 1
|
||||
#define ACTCONT_SHAPEKEY 2
|
||||
typedef enum ACTCONT_TYPES {
|
||||
ACTCONT_NONE = 0,
|
||||
ACTCONT_ACTION,
|
||||
ACTCONT_SHAPEKEY
|
||||
} ACTCONT_TYPES;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -37,6 +37,8 @@
|
||||
struct SpaceLink;
|
||||
struct Object;
|
||||
|
||||
/* -------------- Poses ----------------- */
|
||||
|
||||
/* PoseChannel stores the results of Actions (ipos) and transform information
|
||||
with respect to the restposition of Armature bones */
|
||||
typedef struct bPoseChannel {
|
||||
@@ -98,11 +100,37 @@ typedef struct bPose {
|
||||
float cyclic_offset[3]; /* result of match and cycles, applied in where_is_pose() */
|
||||
} bPose;
|
||||
|
||||
|
||||
/* ------------- Action ---------------- */
|
||||
|
||||
/* Action-Channel Group. These are stored as a list per-Action, and are only used to
|
||||
* group that Action's Action-Channels when displayed in the Action Editor.
|
||||
*
|
||||
* Even though all Action-Channels live in a big list per Action, each group they are in also
|
||||
* holds references to the achans within that list which belong to it. Care must be taken to
|
||||
* ensure that action-groups never end up being the sole 'owner' of a channel.
|
||||
*/
|
||||
typedef struct bActionGroup {
|
||||
struct bActionGroup *next, *prev;
|
||||
|
||||
int flag; /* settings for this action-group */
|
||||
int pad;
|
||||
char name[32]; /* name of the group */
|
||||
|
||||
ListBase channels; /* Note: this must not be touched by standard listbase functions */
|
||||
} bActionGroup;
|
||||
|
||||
/* Action Channels belong to Actions. They are linked with an IPO block, and can also own
|
||||
* Constraint Channels in certain situations.
|
||||
*
|
||||
* Action-Channels can only belong to one group at a time, but they still live the Action's
|
||||
* list of achans (to preserve backwards compatability, and also minimise the code
|
||||
* that would need to be recoded). Grouped achans are stored at the start of the list, according
|
||||
* to the position of the group in the list, and their position within the group.
|
||||
*/
|
||||
typedef struct bActionChannel {
|
||||
struct bActionChannel *next, *prev;
|
||||
bActionGroup *grp; /* Action Group this Action Channel belongs to */
|
||||
|
||||
struct Ipo *ipo; /* IPO block this action channel references */
|
||||
ListBase constraintChannels; /* Constraint Channels (when Action Channel represents an Object or Bone) */
|
||||
@@ -119,12 +147,16 @@ typedef struct bAction {
|
||||
ID id;
|
||||
|
||||
ListBase chanbase; /* Action Channels in this Action */
|
||||
ListBase groups; /* Action Groups in the Action */
|
||||
ListBase markers; /* TimeMarkers local to this Action for labelling 'poses' */
|
||||
|
||||
int active_marker; /* Index of active-marker (first marker = 1) */
|
||||
int pad;
|
||||
} bAction;
|
||||
|
||||
|
||||
/* ------------- Action Editor --------------------- */
|
||||
|
||||
/* Action Editor Space. This is defined here instead of in DNA_space_types.h */
|
||||
typedef struct SpaceAction {
|
||||
struct SpaceLink *next, *prev;
|
||||
@@ -143,6 +175,9 @@ typedef struct SpaceAction {
|
||||
float timeslide; /* for Time-Slide transform mode drawing - current frame? */
|
||||
} SpaceAction;
|
||||
|
||||
|
||||
/* -------------- Action Flags -------------- */
|
||||
|
||||
/* Action Channel flags */
|
||||
typedef enum ACHAN_FLAG {
|
||||
ACHAN_SELECTED = (1<<0),
|
||||
@@ -155,6 +190,19 @@ typedef enum ACHAN_FLAG {
|
||||
ACHAN_MOVED = (1<<31),
|
||||
} ACHAN_FLAG;
|
||||
|
||||
|
||||
/* Action Group flags */
|
||||
typedef enum AGRP_FLAG {
|
||||
AGRP_SELECTED = (1<<0),
|
||||
AGRP_ACTIVE = (1<<1),
|
||||
AGRP_PROTECTED = (1<<2),
|
||||
AGRP_EXPANDED = (1<<3),
|
||||
AGRP_TEMP = (1<<30),
|
||||
AGRP_MOVED = (1<<31)
|
||||
} AGRP_FLAG;
|
||||
|
||||
/* ------------ Action Editor Flags -------------- */
|
||||
|
||||
/* SpaceAction flag */
|
||||
typedef enum SACTION_FLAG {
|
||||
/* during transform */
|
||||
@@ -178,6 +226,9 @@ typedef enum SACTSNAP_MODES {
|
||||
/* snap to nearest marker */
|
||||
SACTSNAP_MARKER,
|
||||
} SACTSNAP_MODES;
|
||||
|
||||
|
||||
/* --------- Pose Flags --------------- */
|
||||
|
||||
/* Pose->flag */
|
||||
typedef enum POSE_FLAG {
|
||||
|
||||
@@ -94,6 +94,7 @@
|
||||
/* 'old' stuff": defines and types, and own include -------------------- */
|
||||
|
||||
#include "blendef.h"
|
||||
#include "interface.h"
|
||||
#include "mydevice.h"
|
||||
|
||||
/********************************** Slider Stuff **************************** */
|
||||
@@ -406,7 +407,7 @@ static void draw_channel_names(void)
|
||||
if (data == NULL) return;
|
||||
|
||||
/* Clip to the scrollable area */
|
||||
if(curarea->winx>SCROLLB+10 && curarea->winy>SCROLLH+10) {
|
||||
if (curarea->winx>SCROLLB+10 && curarea->winy>SCROLLH+10) {
|
||||
if(G.v2d->scroll) {
|
||||
ofsx= curarea->winrct.xmin;
|
||||
ofsy= curarea->winrct.ymin;
|
||||
@@ -434,16 +435,39 @@ static void draw_channel_names(void)
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
for (ale= act_data.first; ale; ale= ale->next) {
|
||||
short indent= 0, offset= 0, sel= 0;
|
||||
short indent= 0, offset= 0, sel= 0, group=0;
|
||||
int expand= -1, protect = -1, special= -1, mute = -1;
|
||||
char name[32];
|
||||
|
||||
/* determine what needs to be drawn */
|
||||
switch (ale->type) {
|
||||
case ACTTYPE_GROUP: /* action group */
|
||||
{
|
||||
bActionGroup *agrp= (bActionGroup *)ale->data;
|
||||
|
||||
group= 2;
|
||||
indent= 0;
|
||||
special= -1;
|
||||
|
||||
if (EXPANDED_AGRP(agrp))
|
||||
expand = ICON_TRIA_DOWN;
|
||||
else
|
||||
expand = ICON_TRIA_RIGHT;
|
||||
|
||||
if (EDITABLE_AGRP(agrp))
|
||||
protect = ICON_UNLOCKED;
|
||||
else
|
||||
protect = ICON_LOCKED;
|
||||
|
||||
sel = SEL_AGRP(agrp);
|
||||
sprintf(name, agrp->name);
|
||||
}
|
||||
break;
|
||||
case ACTTYPE_ACHAN: /* action channel */
|
||||
{
|
||||
bActionChannel *achan= (bActionChannel *)ale->data;
|
||||
|
||||
group= (ale->grp) ? 1 : 0;
|
||||
indent = 0;
|
||||
special = -1;
|
||||
|
||||
@@ -473,6 +497,7 @@ static void draw_channel_names(void)
|
||||
bConstraintChannel *conchan = (bConstraintChannel *)ale->data;
|
||||
|
||||
indent = 2;
|
||||
group= (ale->grp) ? 1 : 0;
|
||||
|
||||
if (EDITABLE_CONCHAN(conchan))
|
||||
protect = ICON_UNLOCKED;
|
||||
@@ -496,6 +521,7 @@ static void draw_channel_names(void)
|
||||
|
||||
indent = 2;
|
||||
protect = -1; // for now, until this can be supported by others
|
||||
group= (ale->grp) ? 1 : 0;
|
||||
|
||||
if (icu->flag & IPO_MUTE)
|
||||
mute = ICON_MUTE_IPO_ON;
|
||||
@@ -528,6 +554,7 @@ static void draw_channel_names(void)
|
||||
|
||||
indent = 1;
|
||||
special = geticon_ipo_blocktype(achan->ipo->blocktype);
|
||||
group= (ale->grp) ? 1 : 0;
|
||||
|
||||
if (FILTER_IPO_ACHAN(achan))
|
||||
expand = ICON_TRIA_DOWN;
|
||||
@@ -544,6 +571,7 @@ static void draw_channel_names(void)
|
||||
|
||||
indent = 1;
|
||||
special = ICON_CONSTRAINT;
|
||||
group= (ale->grp) ? 1 : 0;
|
||||
|
||||
if (FILTER_CON_ACHAN(achan))
|
||||
expand = ICON_TRIA_DOWN;
|
||||
@@ -558,9 +586,24 @@ static void draw_channel_names(void)
|
||||
|
||||
/* now, start drawing based on this information */
|
||||
/* draw backing strip behind channel name */
|
||||
BIF_ThemeColorShade(TH_HEADER, ((indent==0)?20: (indent==1)?-20: -40));
|
||||
offset = 7 * indent;
|
||||
glRectf(x+offset, y-CHANNELHEIGHT/2, (float)NAMEWIDTH, y+CHANNELHEIGHT/2);
|
||||
if (group == 2) {
|
||||
/* only for group-channels */
|
||||
if (ale->flag & AGRP_ACTIVE)
|
||||
BIF_ThemeColorShade(TH_GROUP_ACTIVE, 10);
|
||||
else
|
||||
BIF_ThemeColorShade(TH_GROUP, 20);
|
||||
uiSetRoundBox((expand == ICON_TRIA_DOWN)? (1):(1|8));
|
||||
gl_round_box(GL_POLYGON, x, y-CHANNELHEIGHT/2, (float)NAMEWIDTH, y+CHANNELHEIGHT/2, 8);
|
||||
|
||||
offset = 0;
|
||||
}
|
||||
else {
|
||||
/* for normal channels */
|
||||
BIF_ThemeColorShade(TH_HEADER, ((indent==0)?20: (indent==1)?-20: -40));
|
||||
indent += group;
|
||||
offset = 7 * indent;
|
||||
glRectf(x+offset, y-CHANNELHEIGHT/2, (float)NAMEWIDTH, y+CHANNELHEIGHT/2);
|
||||
}
|
||||
|
||||
/* draw expand/collapse triangle */
|
||||
if (expand > 0) {
|
||||
@@ -572,7 +615,7 @@ static void draw_channel_names(void)
|
||||
* only for expand widgets for Ipo and Constraint Channels
|
||||
*/
|
||||
if (special > 0) {
|
||||
offset = 24;
|
||||
offset = (group) ? 29 : 24;
|
||||
BIF_icon_draw(x+offset, y-CHANNELHEIGHT/2, special);
|
||||
offset += 17;
|
||||
}
|
||||
@@ -694,6 +737,12 @@ static void draw_channel_strips(void)
|
||||
if (ale->datatype != ALE_NONE) {
|
||||
/* determine if channel is selected */
|
||||
switch (ale->type) {
|
||||
case ACTTYPE_GROUP:
|
||||
{
|
||||
bActionGroup *agrp = (bActionGroup *)ale->data;
|
||||
sel = SEL_AGRP(agrp);
|
||||
}
|
||||
break;
|
||||
case ACTTYPE_ACHAN:
|
||||
{
|
||||
bActionChannel *achan = (bActionChannel *)ale->data;
|
||||
@@ -752,6 +801,9 @@ static void draw_channel_strips(void)
|
||||
y = 0.0;
|
||||
for (ale= act_data.first; ale; ale= ale->next) {
|
||||
switch (ale->datatype) {
|
||||
case ALE_GROUP:
|
||||
draw_agroup_channel(di, ale->data, y);
|
||||
break;
|
||||
case ALE_IPO:
|
||||
draw_ipo_channel(di, ale->key_data, y);
|
||||
break;
|
||||
@@ -1196,6 +1248,17 @@ void draw_icu_channel(gla2DDrawInfo *di, IpoCurve *icu, float ypos)
|
||||
BLI_freelistN(&blocks);
|
||||
}
|
||||
|
||||
void draw_agroup_channel(gla2DDrawInfo *di, bActionGroup *agrp, float ypos)
|
||||
{
|
||||
ListBase keys = {0, 0};
|
||||
ListBase blocks = {0, 0};
|
||||
|
||||
agroup_to_keylist(agrp, &keys, &blocks);
|
||||
draw_keylist(di, &keys, &blocks, ypos);
|
||||
BLI_freelistN(&keys);
|
||||
BLI_freelistN(&blocks);
|
||||
}
|
||||
|
||||
void draw_action_channel(gla2DDrawInfo *di, bAction *act, float ypos)
|
||||
{
|
||||
ListBase keys = {0, 0};
|
||||
@@ -1273,6 +1336,27 @@ void ipo_to_keylist(Ipo *ipo, ListBase *keys, ListBase *blocks)
|
||||
}
|
||||
}
|
||||
|
||||
void agroup_to_keylist(bActionGroup *agrp, ListBase *keys, ListBase *blocks)
|
||||
{
|
||||
bActionChannel *achan;
|
||||
bConstraintChannel *conchan;
|
||||
|
||||
if (agrp) {
|
||||
/* loop through action channels */
|
||||
for (achan= agrp->channels.first; achan && achan!=agrp->channels.last; achan= achan->next) {
|
||||
/* firstly, add keys from action channel's ipo block */
|
||||
if (achan->ipo)
|
||||
ipo_to_keylist(achan->ipo, keys, blocks);
|
||||
|
||||
/* then, add keys from constraint channels */
|
||||
for (conchan= achan->constraintChannels.first; conchan; conchan= conchan->next) {
|
||||
if (conchan->ipo)
|
||||
ipo_to_keylist(conchan->ipo, keys, blocks);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void action_to_keylist(bAction *act, ListBase *keys, ListBase *blocks)
|
||||
{
|
||||
bActionChannel *achan;
|
||||
@@ -1288,7 +1372,7 @@ void action_to_keylist(bAction *act, ListBase *keys, ListBase *blocks)
|
||||
/* then, add keys from constraint channels */
|
||||
for (conchan= achan->constraintChannels.first; conchan; conchan= conchan->next) {
|
||||
if (conchan->ipo)
|
||||
ipo_to_keylist(achan->ipo, keys, blocks);
|
||||
ipo_to_keylist(conchan->ipo, keys, blocks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,8 +198,25 @@ bActListElem *make_new_actlistelem (void *data, short datatype, void *owner, sho
|
||||
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;
|
||||
@@ -276,81 +293,116 @@ bActListElem *make_new_actlistelem (void *data, short datatype, void *owner, sho
|
||||
|
||||
/* ----------------------------------------- */
|
||||
|
||||
static void actdata_filter_action (ListBase *act_data, bAction *act, int filter_mode)
|
||||
static void actdata_filter_actionchannel (ListBase *act_data, bActionChannel *achan, int filter_mode)
|
||||
{
|
||||
bActListElem *ale;
|
||||
bActionChannel *achan;
|
||||
bConstraintChannel *conchan;
|
||||
IpoCurve *icu;
|
||||
|
||||
/* loop over action channels, performing the necessary checks */
|
||||
for (achan= act->chanbase.first; achan; achan= achan->next) {
|
||||
/* 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);
|
||||
}
|
||||
/* 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 {
|
||||
/* only consider selected channels - achan not selected */
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* only consider selected channels - achan not selected */
|
||||
return;
|
||||
}
|
||||
|
||||
/* check if expanded - if not, continue on to next action channel */
|
||||
if (EXPANDED_ACHAN(achan) == 0 && (filter_mode & ACTFILTER_ONLYICU)==0)
|
||||
return;
|
||||
|
||||
/* check if expanded - if not, continue on to next action channel */
|
||||
if (EXPANDED_ACHAN(achan) == 0 && (filter_mode & ACTFILTER_ONLYICU)==0)
|
||||
continue;
|
||||
|
||||
/* ipo channels */
|
||||
if (achan->ipo) {
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
/* ipo channels */
|
||||
if (achan->ipo) {
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* constraint channels */
|
||||
if (achan->constraintChannels.first) {
|
||||
/* include constraint-expand widget? */
|
||||
if ((filter_mode & ACTFILTER_CHANNELS) && (filter_mode & ACTFILTER_ONLYICU)==0) {
|
||||
ale= make_new_actlistelem(achan, ACTTYPE_FILLCON, 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);
|
||||
}
|
||||
|
||||
/* add constaint channels? */
|
||||
if (FILTER_CON_ACHAN(achan)) {
|
||||
/* 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_ONLYICU)==0) {
|
||||
ale= make_new_actlistelem(conchan, ACTTYPE_CONCHAN, 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)==0) {
|
||||
ale= make_new_actlistelem(achan, ACTTYPE_FILLCON, achan, ACTTYPE_ACHAN);
|
||||
if (ale) BLI_addtail(act_data, ale);
|
||||
}
|
||||
|
||||
/* add constaint channels? */
|
||||
if (FILTER_CON_ACHAN(achan)) {
|
||||
/* 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_ONLYICU)==0) {
|
||||
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;
|
||||
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)) {
|
||||
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;
|
||||
|
||||
/* filters here are a bit convulted...
|
||||
* - 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)
|
||||
*/
|
||||
if (!(filter_mode & ACTFILTER_VISIBLE) || EXPANDED_AGRP(agrp) ||
|
||||
(filter_mode & (ACTFILTER_IPOKEYS|ACTFILTER_ONLYICU)))
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* loop over action channels */
|
||||
for (achan=(lastchan)?lastchan->next:act->chanbase.first; achan; achan=achan->next) {
|
||||
actdata_filter_actionchannel(act_data, achan, filter_mode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,7 +502,6 @@ void actdata_filter (ListBase *act_data, int filter_mode, void *data, short data
|
||||
/* **************************************************** */
|
||||
/* 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
|
||||
@@ -641,6 +692,10 @@ static void *get_nearest_action_key (float *selx, short *sel, short *ret_type, b
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (ale->type == ACTTYPE_GROUP) {
|
||||
bActionGroup *agrp= (bActionGroup *)ale->data;
|
||||
agroup_to_keylist(agrp, &act_keys, NULL);
|
||||
}
|
||||
|
||||
/* loop through keyframes, finding one that was clicked on */
|
||||
for (ak= act_keys.first; ak; ak= ak->next) {
|
||||
@@ -707,6 +762,267 @@ void *get_action_context (short *datatype)
|
||||
}
|
||||
}
|
||||
|
||||
/* **************************************************** */
|
||||
/* 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;
|
||||
|
||||
/* 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);
|
||||
|
||||
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 (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);
|
||||
}
|
||||
|
||||
/* **************************************************** */
|
||||
/* TRANSFORM TOOLS */
|
||||
|
||||
@@ -1063,6 +1379,32 @@ void delete_action_channels (void)
|
||||
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!=agrp->channels.last; 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);
|
||||
@@ -1634,6 +1976,7 @@ static void numbuts_action ()
|
||||
void *act_channel;
|
||||
short chantype;
|
||||
|
||||
bActionGroup *agrp= NULL;
|
||||
bActionChannel *achan= NULL;
|
||||
bConstraintChannel *conchan= NULL;
|
||||
IpoCurve *icu= NULL;
|
||||
@@ -1739,6 +2082,18 @@ static void numbuts_action ()
|
||||
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 {
|
||||
/* nothing under-cursor */
|
||||
return;
|
||||
@@ -1777,6 +2132,15 @@ static void numbuts_action ()
|
||||
if (achan->ipo)
|
||||
achan->ipo->muteipo = mute;
|
||||
}
|
||||
else if (agrp) {
|
||||
strcpy(agrp->name, str);
|
||||
|
||||
if (expand) agrp->flag |= AGRP_EXPANDED;
|
||||
else agrp->flag &= ~AGRP_EXPANDED;
|
||||
|
||||
if (protect) agrp->flag |= AGRP_PROTECTED;
|
||||
else agrp->flag &= ~AGRP_PROTECTED;
|
||||
}
|
||||
|
||||
allqueue(REDRAWACTION, 0);
|
||||
allspace(REMAKEIPO, 0);
|
||||
@@ -1790,6 +2154,32 @@ static void numbuts_action ()
|
||||
/* **************************************************** */
|
||||
/* 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;
|
||||
@@ -1950,6 +2340,10 @@ void deselect_actionchannels (bAction *act, short test)
|
||||
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;
|
||||
@@ -1971,6 +2365,16 @@ void deselect_actionchannels (bAction *act, short test)
|
||||
/* 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 (sel)
|
||||
agrp->flag |= AGRP_SELECTED;
|
||||
else
|
||||
agrp->flag &= ~AGRP_SELECTED;
|
||||
}
|
||||
break;
|
||||
case ACTTYPE_ACHAN:
|
||||
{
|
||||
bActionChannel *achan= (bActionChannel *)ale->data;
|
||||
@@ -2484,6 +2888,18 @@ void borderselect_action (void)
|
||||
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!=agrp->channels.last; 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);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ACTEDIT_BORDERSEL_CHA: /* all in channel(s) */
|
||||
if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))) {
|
||||
@@ -2493,6 +2909,18 @@ void borderselect_action (void)
|
||||
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!=agrp->channels.last; 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default: /* any keyframe inside region defined by region */
|
||||
@@ -2503,6 +2931,18 @@ void borderselect_action (void)
|
||||
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!=agrp->channels.last; 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2529,6 +2969,7 @@ static void mouse_action (int selectmode)
|
||||
short datatype;
|
||||
|
||||
bAction *act= NULL;
|
||||
bActionGroup *agrp= NULL;
|
||||
bActionChannel *achan= NULL;
|
||||
bConstraintChannel *conchan= NULL;
|
||||
IpoCurve *icu= NULL;
|
||||
@@ -2611,6 +3052,9 @@ static void mouse_action (int selectmode)
|
||||
case ACTTYPE_ACHAN:
|
||||
achan= (bActionChannel *)act_channel;
|
||||
break;
|
||||
case ACTTYPE_GROUP:
|
||||
agrp= (bActionGroup *)act_channel;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
@@ -2623,9 +3067,16 @@ static void mouse_action (int selectmode)
|
||||
if (datatype == ACTCONT_ACTION) {
|
||||
deselect_action_channels(0);
|
||||
|
||||
achan->flag |= ACHAN_SELECTED;
|
||||
hilight_channel(act, achan, 1);
|
||||
select_poseelement_by_name(achan->name, 2); /* 2 is activate */
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2633,8 +3084,16 @@ static void mouse_action (int selectmode)
|
||||
select_icu_key(icu, selx, selectmode);
|
||||
else if (conchan)
|
||||
select_ipo_key(conchan->ipo, selx, selectmode);
|
||||
else
|
||||
else if (achan)
|
||||
select_ipo_key(achan->ipo, selx, selectmode);
|
||||
else if (agrp) {
|
||||
for (achan= agrp->channels.first; achan && achan!=agrp->channels.last; 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);
|
||||
}
|
||||
}
|
||||
|
||||
std_rmouse_transform(transform_action_keys);
|
||||
|
||||
@@ -2663,6 +3122,30 @@ static void mouse_actionchannels (short mval[])
|
||||
|
||||
/* action to take depends on what channel we've got */
|
||||
switch (chantype) {
|
||||
case ACTTYPE_GROUP:
|
||||
{
|
||||
bActionGroup *agrp= (bActionGroup *)act_channel;
|
||||
|
||||
if (mval[0] < 16) {
|
||||
/* 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) {
|
||||
select_action_group(act, agrp, SELECT_INVERT);
|
||||
}
|
||||
else {
|
||||
deselect_actionchannels(act, 0);
|
||||
select_action_group(act, agrp, SELECT_ADD);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ACTTYPE_ACHAN:
|
||||
{
|
||||
bActionChannel *achan= (bActionChannel *)act_channel;
|
||||
@@ -2780,167 +3263,292 @@ static void mouse_actionchannels (short mval[])
|
||||
/* **************************************************** */
|
||||
/* ACTION CHANNEL RE-ORDERING */
|
||||
|
||||
void top_sel_action ()
|
||||
/* make sure all action-channels belong to a group (and clear action's list) */
|
||||
static void split_groups_action_temp (bAction *act, bActionGroup *tgrp)
|
||||
{
|
||||
bAction *act;
|
||||
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;
|
||||
|
||||
/* Get the selected action, exit if none are selected */
|
||||
act = G.saction->action;
|
||||
if (!act) return;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
for (achan= act->chanbase.first; achan; achan= achan->next) {
|
||||
if (VISIBLE_ACHAN(achan)) {
|
||||
if (SEL_ACHAN(achan) && !(achan->flag & ACHAN_MOVED)) {
|
||||
/* take it out off the chain keep data */
|
||||
BLI_remlink (&act->chanbase, achan);
|
||||
/* make it first element */
|
||||
BLI_insertlinkbefore(&act->chanbase, act->chanbase.first, achan);
|
||||
achan->flag |= ACHAN_MOVED;
|
||||
/* restart with rest of list */
|
||||
achan= achan->next;
|
||||
/* 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);
|
||||
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, and channels */
|
||||
#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))
|
||||
agrp->flag |= AGRP_MOVED;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* clear temp flags */
|
||||
for (achan= act->chanbase.first; achan; achan= achan->next) {
|
||||
achan->flag = achan->flag & ~ACHAN_MOVED;
|
||||
}
|
||||
#undef GET_FIRST
|
||||
#undef GET_NEXT
|
||||
|
||||
/* Clean up and redraw stuff */
|
||||
remake_action_ipos(act);
|
||||
BIF_undo_push("Top Action channel");
|
||||
allspace(REMAKEIPO, 0);
|
||||
/* assemble lists into one list (and clear moved tags) */
|
||||
join_groups_action_temp(act);
|
||||
|
||||
/* Undo + redraw */
|
||||
BIF_undo_push(undostr);
|
||||
allqueue(REDRAWACTION, 0);
|
||||
allqueue(REDRAWIPO, 0);
|
||||
allqueue(REDRAWNLA, 0);
|
||||
}
|
||||
|
||||
void up_sel_action ()
|
||||
{
|
||||
bAction *act;
|
||||
bActionChannel *achan, *prev;
|
||||
|
||||
/* Get the selected action, exit if none are selected */
|
||||
act = G.saction->action;
|
||||
if (!act) return;
|
||||
|
||||
for (achan=act->chanbase.first; achan; achan= achan->next) {
|
||||
if (VISIBLE_ACHAN(achan)) {
|
||||
if (SEL_ACHAN(achan) && !(achan->flag & ACHAN_MOVED)) {
|
||||
prev = achan->prev;
|
||||
if (prev) {
|
||||
/* take it out off the chain keep data */
|
||||
BLI_remlink (&act->chanbase, achan);
|
||||
/* push it up */
|
||||
BLI_insertlinkbefore(&act->chanbase, prev, achan);
|
||||
achan->flag |= ACHAN_MOVED;
|
||||
/* restart with rest of list */
|
||||
achan= achan->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* clear temp flags */
|
||||
for (achan=act->chanbase.first; achan; achan= achan->next) {
|
||||
achan->flag = achan->flag & ~ACHAN_MOVED;
|
||||
}
|
||||
|
||||
/* Clean up and redraw stuff */
|
||||
remake_action_ipos(act);
|
||||
BIF_undo_push("Up Action channel");
|
||||
allspace(REMAKEIPO, 0);
|
||||
allqueue(REDRAWACTION, 0);
|
||||
allqueue(REDRAWIPO, 0);
|
||||
allqueue(REDRAWNLA, 0);
|
||||
}
|
||||
|
||||
void down_sel_action ()
|
||||
{
|
||||
bAction *act;
|
||||
bActionChannel *achan, *next;
|
||||
|
||||
/* Get the selected action, exit if none are selected */
|
||||
act = G.saction->action;
|
||||
if (!act) return;
|
||||
|
||||
for (achan= act->chanbase.last; achan; achan= achan->prev) {
|
||||
if (VISIBLE_ACHAN(achan)) {
|
||||
if (SEL_ACHAN(achan) && !(achan->flag & ACHAN_MOVED)) {
|
||||
next = achan->next;
|
||||
if (next) next = next->next;
|
||||
if (next) {
|
||||
/* take it out off the chain keep data */
|
||||
BLI_remlink (&act->chanbase, achan);
|
||||
/* move it down */
|
||||
BLI_insertlinkbefore(&act->chanbase, next, achan);
|
||||
achan->flag |= ACHAN_MOVED;
|
||||
}
|
||||
else {
|
||||
/* take it out off the chain keep data */
|
||||
BLI_remlink (&act->chanbase, achan);
|
||||
/* add at end */
|
||||
BLI_addtail(&act->chanbase, achan);
|
||||
achan->flag |= ACHAN_MOVED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* clear temp flags */
|
||||
for (achan= act->chanbase.first; achan; achan= achan->next) {
|
||||
achan->flag = achan->flag & ~ACHAN_MOVED;
|
||||
}
|
||||
|
||||
/* Clean up and redraw stuff */
|
||||
remake_action_ipos(act);
|
||||
BIF_undo_push("Down Action channel");
|
||||
allspace(REMAKEIPO, 0);
|
||||
allqueue(REDRAWACTION, 0);
|
||||
allqueue(REDRAWIPO, 0);
|
||||
allqueue(REDRAWNLA, 0);
|
||||
}
|
||||
|
||||
void bottom_sel_action ()
|
||||
{
|
||||
bAction *act;
|
||||
bActionChannel *achan;
|
||||
|
||||
/* Get the selected action, exit if none are selected */
|
||||
act = G.saction->action;
|
||||
if (!act) return;
|
||||
|
||||
for (achan=act->chanbase.last; achan; achan= achan->prev) {
|
||||
if (VISIBLE_ACHAN(achan)) {
|
||||
if (SEL_ACHAN(achan) && !(achan->flag & ACHAN_MOVED)) {
|
||||
/* take it out off the chain keep data */
|
||||
BLI_remlink(&act->chanbase, achan);
|
||||
/* add at end */
|
||||
BLI_addtail(&act->chanbase, achan);
|
||||
achan->flag |= ACHAN_MOVED;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* clear temp flags */
|
||||
for (achan=act->chanbase.first; achan; achan= achan->next) {
|
||||
achan->flag = achan->flag & ~ACHAN_MOVED;
|
||||
}
|
||||
|
||||
/* Clean up and redraw stuff */
|
||||
remake_action_ipos(act);
|
||||
BIF_undo_push("Bottom Action channel");
|
||||
allspace(REMAKEIPO, 0);
|
||||
allqueue(REDRAWACTION, 0);
|
||||
allqueue(REDRAWIPO, 0);
|
||||
allqueue(REDRAWNLA, 0);
|
||||
}
|
||||
|
||||
/* ******************************************************************* */
|
||||
/* CHANNEL VISIBILITY/FOLDING */
|
||||
|
||||
/* Expand all channels to show full hierachy */
|
||||
void expand_all_action (void)
|
||||
{
|
||||
bAction *act;
|
||||
bActionChannel *achan;
|
||||
short mode= 0;
|
||||
bActionGroup *agrp;
|
||||
short mode= 1;
|
||||
|
||||
/* Get the selected action, exit if none are selected */
|
||||
// TODO: really this should be done with the "action editor api" stuff, but this will suffice for now
|
||||
@@ -2948,15 +3556,32 @@ void expand_all_action (void)
|
||||
if (act == NULL) return;
|
||||
|
||||
/* check if expand all, or close all */
|
||||
for (achan=act->chanbase.first; achan; achan= achan->next) {
|
||||
if (VISIBLE_ACHAN(achan)) {
|
||||
if (EXPANDED_ACHAN(achan))
|
||||
mode= 1;
|
||||
break;
|
||||
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)
|
||||
@@ -2986,7 +3611,8 @@ void openclose_level_action (short mode)
|
||||
if (mode == 0) return;
|
||||
|
||||
/* Only affect selected channels */
|
||||
for (achan=act->chanbase.first; achan; achan= achan->next) {
|
||||
// FIXME: check for action-groups
|
||||
for (achan= act->chanbase.first; achan; achan= achan->next) {
|
||||
if (VISIBLE_ACHAN(achan) && SEL_ACHAN(achan)) {
|
||||
if (EXPANDED_ACHAN(achan)) {
|
||||
if (FILTER_IPO_ACHAN(achan) || FILTER_CON_ACHAN(achan)) {
|
||||
@@ -3255,12 +3881,41 @@ void winqreadactionspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
|
||||
break;
|
||||
|
||||
case GKEY:
|
||||
if (G.qual & LR_CTRLKEY) {
|
||||
transform_markers('g', 0);
|
||||
/* 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();
|
||||
|
||||
else if (G.qual == (LR_CTRLKEY|LR_SHIFTKEY|LR_ALTKEY)) {
|
||||
/* WARNING: this is a debug tool which should be removed once everything is stable... */
|
||||
bAction *act= G.saction->action;
|
||||
bActionGroup *agrp;
|
||||
bActionChannel *achan;
|
||||
|
||||
printf("Debug Action Grouping: \n");
|
||||
|
||||
printf("\tGroups: \n");
|
||||
for (agrp= act->groups.first; agrp; agrp= agrp->next) {
|
||||
printf("\t\tGroup \"%s\" : %p... start={%p} end={%p} \n", agrp->name, agrp, agrp->channels.first, agrp->channels.last);
|
||||
}
|
||||
|
||||
printf("\tAction Channels: \n");
|
||||
for (achan= act->chanbase.first; achan; achan= achan->next) {
|
||||
printf("\t\tAchan \"%s\" : %p... group={%p} \n", achan->name, achan, achan->grp);
|
||||
}
|
||||
}
|
||||
|
||||
/* Transforms */
|
||||
else {
|
||||
if (mval[0]>=ACTWIDTH)
|
||||
transform_action_keys('g', 0);
|
||||
if (mval[0] >= ACTWIDTH) {
|
||||
if (G.qual == LR_CTRLKEY)
|
||||
transform_markers('g', 0);
|
||||
else
|
||||
transform_action_keys('g', 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -3398,9 +4053,9 @@ void winqreadactionspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
|
||||
case PAGEUPKEY:
|
||||
if (datatype == ACTCONT_ACTION) {
|
||||
if (G.qual == (LR_CTRLKEY|LR_SHIFTKEY))
|
||||
top_sel_action();
|
||||
rearrange_action_channels(REARRANGE_ACTCHAN_TOP);
|
||||
else if (G.qual == LR_SHIFTKEY)
|
||||
up_sel_action();
|
||||
rearrange_action_channels(REARRANGE_ACTCHAN_UP);
|
||||
else if (G.qual == LR_CTRLKEY)
|
||||
nextprev_action_keyframe(1);
|
||||
else
|
||||
@@ -3417,9 +4072,9 @@ void winqreadactionspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
|
||||
case PAGEDOWNKEY:
|
||||
if (datatype == ACTCONT_ACTION) {
|
||||
if (G.qual == (LR_CTRLKEY|LR_SHIFTKEY))
|
||||
bottom_sel_action();
|
||||
rearrange_action_channels(REARRANGE_ACTCHAN_BOTTOM);
|
||||
else if (G.qual == LR_SHIFTKEY)
|
||||
down_sel_action();
|
||||
rearrange_action_channels(REARRANGE_ACTCHAN_DOWN);
|
||||
else if (G.qual == LR_CTRLKEY)
|
||||
nextprev_action_keyframe(-1);
|
||||
else
|
||||
@@ -3445,11 +4100,7 @@ void winqreadactionspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
|
||||
if (mval[0] >= NAMEWIDTH)
|
||||
remove_marker();
|
||||
|
||||
allqueue(REDRAWTIME, 0);
|
||||
allqueue(REDRAWIPO, 0);
|
||||
allqueue(REDRAWACTION, 0);
|
||||
allqueue(REDRAWNLA, 0);
|
||||
allqueue(REDRAWSOUND, 0);
|
||||
allqueue(REDRAWMARKER, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -133,6 +133,12 @@ enum {
|
||||
ACTMENU_KEY_CHANPOS_MOVE_CHANNEL_BOTTOM
|
||||
};
|
||||
|
||||
enum {
|
||||
ACTMENU_KEY_CHANGROUP_ADD_TOACTIVE = 0,
|
||||
ACTMENU_KEY_CHANGROUP_ADD_TONEW,
|
||||
ACTMENU_KEY_CHANGROUP_REMOVE
|
||||
};
|
||||
|
||||
enum {
|
||||
ACTMENU_KEY_TRANSFORM_MOVE = 0,
|
||||
ACTMENU_KEY_TRANSFORM_SCALE,
|
||||
@@ -466,35 +472,19 @@ static uiBlock *action_viewmenu(void *arg_unused)
|
||||
|
||||
static void do_action_selectmenu_columnmenu(void *arg, int event)
|
||||
{
|
||||
SpaceAction *saction;
|
||||
bAction *act;
|
||||
Key *key;
|
||||
|
||||
saction = curarea->spacedata.first;
|
||||
if (!saction) return;
|
||||
|
||||
act = saction->action;
|
||||
key = get_action_mesh_key();
|
||||
|
||||
#if 0 // actionrewite
|
||||
if (event == ACTMENU_SEL_COLUMN_MARKERSBETWEEN) {
|
||||
markers_selectkeys_between();
|
||||
switch (event) {
|
||||
case ACTMENU_SEL_COLUMN_MARKERSBETWEEN:
|
||||
markers_selectkeys_between();
|
||||
break;
|
||||
case ACTMENU_SEL_COLUMN_KEYS:
|
||||
column_select_action_keys(1);
|
||||
break;
|
||||
case ACTMENU_SEL_COLUMN_MARKERSCOLUMN:
|
||||
column_select_action_keys(2);
|
||||
break;
|
||||
}
|
||||
else if (ELEM(event, ACTMENU_SEL_COLUMN_KEYS, ACTMENU_SEL_COLUMN_MARKERSCOLUMN)) {
|
||||
if (act)
|
||||
column_select_actionkeys(act, event);
|
||||
else if (key)
|
||||
column_select_shapekeys(key, event);
|
||||
}
|
||||
else
|
||||
return;
|
||||
#endif // actionrewite
|
||||
|
||||
allqueue(REDRAWTIME, 0);
|
||||
allqueue(REDRAWIPO, 0);
|
||||
allqueue(REDRAWACTION, 0);
|
||||
allqueue(REDRAWNLA, 0);
|
||||
allqueue(REDRAWSOUND, 0);
|
||||
allqueue(REDRAWMARKER, 0);
|
||||
}
|
||||
|
||||
static uiBlock *action_selectmenu_columnmenu(void *arg_unused)
|
||||
@@ -871,16 +861,16 @@ static void do_action_keymenu_chanposmenu(void *arg, int event)
|
||||
switch(event)
|
||||
{
|
||||
case ACTMENU_KEY_CHANPOS_MOVE_CHANNEL_DOWN:
|
||||
down_sel_action();
|
||||
rearrange_action_channels(REARRANGE_ACTCHAN_DOWN);
|
||||
break;
|
||||
case ACTMENU_KEY_CHANPOS_MOVE_CHANNEL_UP:
|
||||
up_sel_action();
|
||||
rearrange_action_channels(REARRANGE_ACTCHAN_UP);
|
||||
break;
|
||||
case ACTMENU_KEY_CHANPOS_MOVE_CHANNEL_TOP:
|
||||
top_sel_action();
|
||||
rearrange_action_channels(REARRANGE_ACTCHAN_TOP);
|
||||
break;
|
||||
case ACTMENU_KEY_CHANPOS_MOVE_CHANNEL_BOTTOM:
|
||||
bottom_sel_action();
|
||||
rearrange_action_channels(REARRANGE_ACTCHAN_BOTTOM);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -923,6 +913,54 @@ static uiBlock *action_keymenu_chanposmenu(void *arg_unused)
|
||||
return block;
|
||||
}
|
||||
|
||||
static void do_action_keymenu_changroupmenu(void *arg, int event)
|
||||
{
|
||||
switch(event)
|
||||
{
|
||||
case ACTMENU_KEY_CHANGROUP_ADD_TOACTIVE:
|
||||
action_groups_group(0);
|
||||
break;
|
||||
case ACTMENU_KEY_CHANGROUP_ADD_TONEW:
|
||||
action_groups_group(1);
|
||||
break;
|
||||
case ACTMENU_KEY_CHANGROUP_REMOVE:
|
||||
action_groups_ungroup();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uiBlock *action_keymenu_changroupmenu(void *arg_unused)
|
||||
{
|
||||
uiBlock *block;
|
||||
short yco= 0, menuwidth=120;
|
||||
|
||||
block= uiNewBlock(&curarea->uiblocks, "action_keymenu_changroupmenu",
|
||||
UI_EMBOSSP, UI_HELV, G.curscreen->mainwin);
|
||||
uiBlockSetButmFunc(block, do_action_keymenu_changroupmenu, NULL);
|
||||
|
||||
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1,
|
||||
"Add to Active Group|Shift G", 0, yco-=20,
|
||||
menuwidth, 19, NULL, 0.0, 0.0, 0,
|
||||
ACTMENU_KEY_CHANGROUP_ADD_TOACTIVE, "");
|
||||
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1,
|
||||
"Add To New Group|Ctrl Shift G", 0, yco-=20,
|
||||
menuwidth, 19, NULL, 0.0, 0.0, 0,
|
||||
ACTMENU_KEY_CHANGROUP_ADD_TONEW, "");
|
||||
|
||||
uiDefBut(block, SEPR, 0, "", 0, yco-=6,
|
||||
menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
|
||||
|
||||
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1,
|
||||
"Remove From Group|Alt G", 0, yco-=20,
|
||||
menuwidth, 19, NULL, 0.0, 0.0, 0,
|
||||
ACTMENU_KEY_CHANGROUP_REMOVE, "");
|
||||
|
||||
uiBlockSetDirection(block, UI_RIGHT);
|
||||
uiTextBoundsBlock(block, 60);
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
static void do_action_keymenu_snapmenu(void *arg, int event)
|
||||
{
|
||||
switch(event)
|
||||
@@ -1125,6 +1163,14 @@ static uiBlock *action_keymenu(void *arg_unused)
|
||||
uiDefIconTextBlockBut(block, action_keymenu_intpolmenu,
|
||||
NULL, ICON_RIGHTARROW_THIN,
|
||||
"Interpolation Mode", 0, yco-=20, 120, 20, "");
|
||||
|
||||
uiDefBut(block, SEPR, 0, "", 0, yco-=6,
|
||||
menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
|
||||
|
||||
uiDefIconTextBlockBut(block, action_keymenu_changroupmenu,
|
||||
NULL, ICON_RIGHTARROW_THIN,
|
||||
"Channel Grouping", 0, yco-=20, 120, 20, "");
|
||||
|
||||
uiDefIconTextBlockBut(block, action_keymenu_chanposmenu,
|
||||
NULL, ICON_RIGHTARROW_THIN,
|
||||
"Channel Ordering", 0, yco-=20, 120, 20, "");
|
||||
|
||||
@@ -453,6 +453,8 @@ void BIF_InitTheme(void)
|
||||
SETCOL(btheme->tact.hilite, 17, 27, 60, 100); // bar
|
||||
SETCOL(btheme->tact.strip_select, 0xff, 0xff, 0xaa, 255);
|
||||
SETCOL(btheme->tact.strip, 0xe4, 0x9c, 0xc6, 255);
|
||||
SETCOL(btheme->tact.group, 0x39, 0x7d, 0x1b, 255);
|
||||
SETCOL(btheme->tact.group_active, 0x7d, 0xe9, 0x60, 255);
|
||||
|
||||
/* space nla */
|
||||
btheme->tnla= btheme->tv3d;
|
||||
@@ -643,6 +645,8 @@ char *BIF_ThemeColorsPup(int spacetype)
|
||||
str += sprintf(str, "View Sliders %%x%d|", TH_SHADE1);
|
||||
str += sprintf(str, "Channels %%x%d|", TH_SHADE2);
|
||||
str += sprintf(str, "Channels Selected %%x%d|", TH_HILITE);
|
||||
str += sprintf(str, "Channel Group %%x%d|", TH_GROUP);
|
||||
str += sprintf(str, "Active Channel Group %%x%d|", TH_GROUP_ACTIVE);
|
||||
str += sprintf(str, "Long Key %%x%d|", TH_STRIP);
|
||||
str += sprintf(str, "Long Key selected %%x%d|", TH_STRIP_SELECT);
|
||||
str += sprintf(str, "Current Frame %%x%d", TH_CFRAME);
|
||||
|
||||
@@ -396,6 +396,14 @@ static void init_userdef_file(void)
|
||||
SETCOL(btheme->ttime.cframe, 0x60, 0xc0, 0x40, 255);
|
||||
}
|
||||
}
|
||||
if ((G.main->versionfile < 245) || (G.main->versionfile == 245 && G.main->subversionfile < 13)) {
|
||||
bTheme *btheme;
|
||||
for (btheme= U.themes.first; btheme; btheme= btheme->next) {
|
||||
/* action channel groups (recolour anyway) */
|
||||
SETCOL(btheme->tact.group, 0x39, 0x7d, 0x1b, 255);
|
||||
SETCOL(btheme->tact.group_active, 0x7d, 0xe9, 0x60, 255);
|
||||
}
|
||||
}
|
||||
|
||||
/* GL Texture Garbage Collection (variable abused above!) */
|
||||
if (U.textimeout == 0) {
|
||||
|
||||
Reference in New Issue
Block a user