diff options
author | Joshua Leung <aligorith@gmail.com> | 2008-01-18 02:02:11 +0300 |
---|---|---|
committer | Joshua Leung <aligorith@gmail.com> | 2008-01-18 02:02:11 +0300 |
commit | 44c31bb04523799bf918bb4e519c0ebad4c0b9b1 (patch) | |
tree | a026a4738100cc7c8eaa2ff025b70b210c49cbfd /source/blender | |
parent | 65d61c2cb65ec2d7e92985582932744bf0d1620a (diff) |
== 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
Diffstat (limited to 'source/blender')
-rw-r--r-- | source/blender/blenkernel/BKE_blender.h | 2 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/action.c | 26 | ||||
-rw-r--r-- | source/blender/blenloader/intern/readfile.c | 14 | ||||
-rw-r--r-- | source/blender/blenloader/intern/writefile.c | 5 | ||||
-rw-r--r-- | source/blender/include/BDR_drawaction.h | 11 | ||||
-rw-r--r-- | source/blender/include/BIF_editaction.h | 29 | ||||
-rw-r--r-- | source/blender/include/BSE_editaction_types.h | 35 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_action_types.h | 51 | ||||
-rw-r--r-- | source/blender/src/drawaction.c | 98 | ||||
-rw-r--r-- | source/blender/src/editaction.c | 1055 | ||||
-rw-r--r-- | source/blender/src/header_action.c | 108 | ||||
-rw-r--r-- | source/blender/src/resources.c | 4 | ||||
-rw-r--r-- | source/blender/src/usiblender.c | 8 |
13 files changed, 1179 insertions, 267 deletions
diff --git a/source/blender/blenkernel/BKE_blender.h b/source/blender/blenkernel/BKE_blender.h index 243425db139..f856b870289 100644 --- a/source/blender/blenkernel/BKE_blender.h +++ b/source/blender/blenkernel/BKE_blender.h @@ -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 diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index 61668ddb0eb..d85b61675a6 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -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; } diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 5712f8f03fc..4a21d1fc6fa 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -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) diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index fdea98c7ab2..b5c48716036 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -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); } diff --git a/source/blender/include/BDR_drawaction.h b/source/blender/include/BDR_drawaction.h index 673b13672c1..1753f4ecf42 100644 --- a/source/blender/include/BDR_drawaction.h +++ b/source/blender/include/BDR_drawaction.h @@ -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 */ diff --git a/source/blender/include/BIF_editaction.h b/source/blender/include/BIF_editaction.h index 938d6d3d672..bea1edcc2ee 100644 --- a/source/blender/include/BIF_editaction.h +++ b/source/blender/include/BIF_editaction.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); diff --git a/source/blender/include/BSE_editaction_types.h b/source/blender/include/BSE_editaction_types.h index 48f0bc39ffc..ae61b09a13a 100644 --- a/source/blender/include/BSE_editaction_types.h +++ b/source/blender/include/BSE_editaction_types.h @@ -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 diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h index 1f591894a90..ea145e7ef79 100644 --- a/source/blender/makesdna/DNA_action_types.h +++ b/source/blender/makesdna/DNA_action_types.h @@ -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 { diff --git a/source/blender/src/drawaction.c b/source/blender/src/drawaction.c index 05a50fe4f16..b25473989be 100644 --- a/source/blender/src/drawaction.c +++ b/source/blender/src/drawaction.c @@ -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); } } } diff --git a/source/blender/src/editaction.c b/source/blender/src/editaction.c index 311f2a653d5..91242b4f3fb 100644 --- a/source/blender/src/editaction.c +++ b/source/blender/src/editaction.c @@ -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) { @@ -708,6 +763,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 */ /* main call to start transforming keyframes */ @@ -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; - /* 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)) { - /* 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; - } + /* 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; } } - /* clear temp flags */ - for (achan= act->chanbase.first; achan; achan= achan->next) { - achan->flag = achan->flag & ~ACHAN_MOVED; + + /* 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; } - /* Clean up and redraw stuff */ - remake_action_ipos(act); - BIF_undo_push("Top Action channel"); - allspace(REMAKEIPO, 0); - allqueue(REDRAWACTION, 0); - allqueue(REDRAWIPO, 0); - allqueue(REDRAWNLA, 0); + /* Add temp-group to list */ + BLI_addtail(&act->groups, tgrp); } -void up_sel_action () +/* link lists of channels that groups have */ +static void join_groups_action_temp (bAction *act) { - bAction *act; - bActionChannel *achan, *prev; - - /* Get the selected action, exit if none are selected */ - act = G.saction->action; - if (!act) return; + bActionGroup *agrp; + bActionChannel *achan; - 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; - } - } + 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 temp flags */ - for (achan=act->chanbase.first; achan; achan= achan->next) { - achan->flag = achan->flag & ~ACHAN_MOVED; + + /* 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; } - /* 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); + return 0; } -void down_sel_action () +static short rearrange_actchannel_after_ok (Link *channel, short type) { - bAction *act; - bActionChannel *achan, *next; + if (type == ACTTYPE_GROUP) { + bActionGroup *agrp= (bActionGroup *)channel; + + if (agrp->flag & AGRP_TEMP) + return 0; + } - /* Get the selected action, exit if none are selected */ - act = G.saction->action; - if (!act) return; + 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; + } - 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; - } - } + 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; } } - /* clear temp flags */ - for (achan= act->chanbase.first; achan; achan= achan->next) { - achan->flag = achan->flag & ~ACHAN_MOVED; + + 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; + } } - /* 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); + return 0; } -void bottom_sel_action () +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; + bActionChannel *achan, *chan; + bActionGroup *agrp, *grp; + bActionGroup tgrp; - /* Get the selected action, exit if none are selected */ - act = G.saction->action; - if (!act) return; + void *data; + short datatype; - 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; - } - } + 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; } - /* clear temp flags */ - for (achan=act->chanbase.first; achan; achan= achan->next) { - achan->flag = achan->flag & ~ACHAN_MOVED; + + /* 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; + } + } } + #undef GET_FIRST + #undef GET_NEXT - /* Clean up and redraw stuff */ - remake_action_ipos(act); - BIF_undo_push("Bottom 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); } +/* ******************************************************************* */ +/* 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; diff --git a/source/blender/src/header_action.c b/source/blender/src/header_action.c index c9edd6cd1f0..f9a3fb08bd8 100644 --- a/source/blender/src/header_action.c +++ b/source/blender/src/header_action.c @@ -134,6 +134,12 @@ enum { }; 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, ACTMENU_KEY_TRANSFORM_SLIDE, @@ -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(); - } - 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); + 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 - 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, ""); diff --git a/source/blender/src/resources.c b/source/blender/src/resources.c index 25295f4cd3c..0d22ce8744e 100644 --- a/source/blender/src/resources.c +++ b/source/blender/src/resources.c @@ -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); diff --git a/source/blender/src/usiblender.c b/source/blender/src/usiblender.c index 147f5a79137..dd6d3eb6d15 100644 --- a/source/blender/src/usiblender.c +++ b/source/blender/src/usiblender.c @@ -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) { |