This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/editors/animation/anim_channels.c

1776 lines
47 KiB
C
Raw Normal View History

/**
2009-06-23 00:09:26 +00:00
* $Id$
*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Joshua Leung
*
* ***** END GPL LICENSE BLOCK *****
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <float.h>
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
#include "BLI_arithb.h"
#include "DNA_listBase.h"
#include "DNA_anim_types.h"
#include "DNA_action_types.h"
#include "DNA_armature_types.h"
#include "DNA_camera_types.h"
#include "DNA_curve_types.h"
#include "DNA_object_types.h"
#include "DNA_screen_types.h"
#include "DNA_scene_types.h"
#include "DNA_space_types.h"
#include "DNA_constraint_types.h"
#include "DNA_key_types.h"
#include "DNA_lamp_types.h"
#include "DNA_material_types.h"
#include "DNA_userdef_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_windowmanager_types.h"
#include "DNA_world_types.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "BKE_action.h"
#include "BKE_depsgraph.h"
#include "BKE_ipo.h"
#include "BKE_key.h"
#include "BKE_material.h"
#include "BKE_object.h"
#include "BKE_context.h"
#include "BKE_utildefines.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "UI_view2d.h"
#include "ED_anim_api.h"
#include "ED_keyframes_edit.h" // XXX move the select modes out of there!
#include "ED_screen.h"
#include "ED_space_api.h"
#include "WM_api.h"
#include "WM_types.h"
/* ************************************************************************** */
/* CHANNELS API */
/* -------------------------- Exposed API ----------------------------------- */
/* Set the given animation-channel as the active one for the active context */
NLA SoC: Operators for 'tweaking' strip actions (TAB-Key for both) In this commit, I've introduced the mechanism by which actions already referenced by strips used in the NLA can be edited (or 'tweaked'). To use, simply select a strip you wish to edit, and hit that TAB key to start tweaking that strip's action, and hit TAB again once you're done. What happens when you enter 'tweak mode': 1) The action of the active strip temporarily becomes the 'active action' of the AnimData block. You are now able to edit this in one of the Animation Editors (DopeSheet/Action, Graph Editors) as per normal (i.e. sliding keyframes around, inserting keyframes, etc.). The 'action-line' will therefore get drawn immediately above the active track containing the active strip, so that it's clear that that's what we're editing. 2) All the NLA-tracks (and all the strips within them) that occur after the track that the active strip lived in get disabled while you're in tweakmode. This is equivalent to travelling back to an earlier state in a construction history stack. 3) The active NLA track also gets disabled while in tweakmode, since it would otherwise interfere with the correct functioning of the tweaking for the action of interest. 4) The 'real' active action (i.e. the one displaced by the active strip's action) gets put into temp storage, and will be restored after you exit tweakmode. 5) Any strips which also reference the action being tweaked will get highlighted in red shading to indicate that you may be making some changes to the action which you don't really want to make for the other users too. Please note though, that this is only a rough prototype of this functionality, with some niceties still to come. i.e.: * NLA-tracks after the active track should still get drawn above the 'tweaking action line', but perhaps with different appearance? * Various tools will still need awareness of this to prevent corrupting operations from taking place. How to proceed is still undecided... * When exiting tweak-mode, the strip the action came from still needs some form of syncing with the modified action... there are a few tricky issues here that will need to be solved * Evaluation code doesn't totally take this into account yet... --- Also, fixed a number of bugs with various code (notably selection, and also a few drawing bugs)
2009-06-05 05:18:07 +00:00
void ANIM_set_active_channel (bAnimContext *ac, void *data, short datatype, int filter, void *channel_data, short channel_type)
{
ListBase anim_data = {NULL, NULL};
bAnimListElem *ale;
/* try to build list of filtered items */
NLA SoC: Operators for 'tweaking' strip actions (TAB-Key for both) In this commit, I've introduced the mechanism by which actions already referenced by strips used in the NLA can be edited (or 'tweaked'). To use, simply select a strip you wish to edit, and hit that TAB key to start tweaking that strip's action, and hit TAB again once you're done. What happens when you enter 'tweak mode': 1) The action of the active strip temporarily becomes the 'active action' of the AnimData block. You are now able to edit this in one of the Animation Editors (DopeSheet/Action, Graph Editors) as per normal (i.e. sliding keyframes around, inserting keyframes, etc.). The 'action-line' will therefore get drawn immediately above the active track containing the active strip, so that it's clear that that's what we're editing. 2) All the NLA-tracks (and all the strips within them) that occur after the track that the active strip lived in get disabled while you're in tweakmode. This is equivalent to travelling back to an earlier state in a construction history stack. 3) The active NLA track also gets disabled while in tweakmode, since it would otherwise interfere with the correct functioning of the tweaking for the action of interest. 4) The 'real' active action (i.e. the one displaced by the active strip's action) gets put into temp storage, and will be restored after you exit tweakmode. 5) Any strips which also reference the action being tweaked will get highlighted in red shading to indicate that you may be making some changes to the action which you don't really want to make for the other users too. Please note though, that this is only a rough prototype of this functionality, with some niceties still to come. i.e.: * NLA-tracks after the active track should still get drawn above the 'tweaking action line', but perhaps with different appearance? * Various tools will still need awareness of this to prevent corrupting operations from taking place. How to proceed is still undecided... * When exiting tweak-mode, the strip the action came from still needs some form of syncing with the modified action... there are a few tricky issues here that will need to be solved * Evaluation code doesn't totally take this into account yet... --- Also, fixed a number of bugs with various code (notably selection, and also a few drawing bugs)
2009-06-05 05:18:07 +00:00
ANIM_animdata_filter(ac, &anim_data, filter, data, datatype);
if (anim_data.first == NULL)
return;
/* only clear the 'active' flag for the channels of the same type */
for (ale= anim_data.first; ale; ale= ale->next) {
/* skip if types don't match */
if (channel_type != ale->type)
continue;
/* flag to set depends on type */
switch (ale->type) {
case ANIMTYPE_GROUP:
{
bActionGroup *agrp= (bActionGroup *)ale->data;
ACHANNEL_SET_FLAG(agrp, ACHANNEL_SETFLAG_CLEAR, AGRP_ACTIVE);
}
break;
case ANIMTYPE_FCURVE:
{
FCurve *fcu= (FCurve *)ale->data;
ACHANNEL_SET_FLAG(fcu, ACHANNEL_SETFLAG_CLEAR, FCURVE_ACTIVE);
}
break;
case ANIMTYPE_NLATRACK:
{
NlaTrack *nlt= (NlaTrack *)ale->data;
ACHANNEL_SET_FLAG(nlt, ACHANNEL_SETFLAG_CLEAR, NLATRACK_ACTIVE);
}
break;
}
}
/* set active flag */
if (channel_data) {
switch (channel_type) {
case ANIMTYPE_GROUP:
{
bActionGroup *agrp= (bActionGroup *)channel_data;
agrp->flag |= AGRP_ACTIVE;
}
break;
case ANIMTYPE_FCURVE:
{
FCurve *fcu= (FCurve *)channel_data;
fcu->flag |= FCURVE_ACTIVE;
}
break;
case ANIMTYPE_NLATRACK:
{
NlaTrack *nlt= (NlaTrack *)channel_data;
NLA SoC: Operators for 'tweaking' strip actions (TAB-Key for both) In this commit, I've introduced the mechanism by which actions already referenced by strips used in the NLA can be edited (or 'tweaked'). To use, simply select a strip you wish to edit, and hit that TAB key to start tweaking that strip's action, and hit TAB again once you're done. What happens when you enter 'tweak mode': 1) The action of the active strip temporarily becomes the 'active action' of the AnimData block. You are now able to edit this in one of the Animation Editors (DopeSheet/Action, Graph Editors) as per normal (i.e. sliding keyframes around, inserting keyframes, etc.). The 'action-line' will therefore get drawn immediately above the active track containing the active strip, so that it's clear that that's what we're editing. 2) All the NLA-tracks (and all the strips within them) that occur after the track that the active strip lived in get disabled while you're in tweakmode. This is equivalent to travelling back to an earlier state in a construction history stack. 3) The active NLA track also gets disabled while in tweakmode, since it would otherwise interfere with the correct functioning of the tweaking for the action of interest. 4) The 'real' active action (i.e. the one displaced by the active strip's action) gets put into temp storage, and will be restored after you exit tweakmode. 5) Any strips which also reference the action being tweaked will get highlighted in red shading to indicate that you may be making some changes to the action which you don't really want to make for the other users too. Please note though, that this is only a rough prototype of this functionality, with some niceties still to come. i.e.: * NLA-tracks after the active track should still get drawn above the 'tweaking action line', but perhaps with different appearance? * Various tools will still need awareness of this to prevent corrupting operations from taking place. How to proceed is still undecided... * When exiting tweak-mode, the strip the action came from still needs some form of syncing with the modified action... there are a few tricky issues here that will need to be solved * Evaluation code doesn't totally take this into account yet... --- Also, fixed a number of bugs with various code (notably selection, and also a few drawing bugs)
2009-06-05 05:18:07 +00:00
nlt->flag |= NLATRACK_ACTIVE;
}
break;
}
}
/* clean up */
BLI_freelistN(&anim_data);
}
/* Deselect all animation channels
* - data: pointer to datatype, as contained in bAnimContext
* - datatype: the type of data that 'data' represents (eAnimCont_Types)
* - test: check if deselecting instead of selecting
* - sel: eAnimChannels_SetFlag;
*/
void ANIM_deselect_anim_channels (void *data, short datatype, short test, short sel)
{
ListBase anim_data = {NULL, NULL};
bAnimListElem *ale;
int filter;
/* filter data */
filter= ANIMFILTER_VISIBLE;
ANIM_animdata_filter(NULL, &anim_data, filter, data, datatype);
/* See if we should be selecting or deselecting */
if (test) {
for (ale= anim_data.first; ale; ale= ale->next) {
if (sel == 0)
break;
switch (ale->type) {
case ANIMTYPE_SCENE:
if (ale->flag & SCE_DS_SELECTED)
sel= ACHANNEL_SETFLAG_CLEAR;
break;
case ANIMTYPE_OBJECT:
if (ale->flag & SELECT)
sel= ACHANNEL_SETFLAG_CLEAR;
break;
case ANIMTYPE_FILLACTD:
if (ale->flag & ACT_SELECTED)
sel= ACHANNEL_SETFLAG_CLEAR;
break;
case ANIMTYPE_GROUP:
if (ale->flag & AGRP_SELECTED)
sel= ACHANNEL_SETFLAG_CLEAR;
break;
case ANIMTYPE_FCURVE:
if (ale->flag & FCURVE_SELECTED)
sel= ACHANNEL_SETFLAG_CLEAR;
break;
case ANIMTYPE_NLATRACK:
if (ale->flag & NLATRACK_SELECTED)
sel= ACHANNEL_SETFLAG_CLEAR;
break;
}
}
}
/* Now set the flags */
for (ale= anim_data.first; ale; ale= ale->next) {
switch (ale->type) {
case ANIMTYPE_SCENE:
{
Scene *scene= (Scene *)ale->data;
ACHANNEL_SET_FLAG(scene, sel, SCE_DS_SELECTED);
}
break;
case ANIMTYPE_OBJECT:
{
Base *base= (Base *)ale->data;
Object *ob= base->object;
ACHANNEL_SET_FLAG(base, sel, SELECT);
ACHANNEL_SET_FLAG(ob, sel, SELECT);
}
break;
case ANIMTYPE_FILLACTD:
{
bAction *act= (bAction *)ale->data;
ACHANNEL_SET_FLAG(act, sel, ACT_SELECTED);
}
break;
case ANIMTYPE_GROUP:
{
bActionGroup *agrp= (bActionGroup *)ale->data;
ACHANNEL_SET_FLAG(agrp, sel, AGRP_SELECTED);
agrp->flag &= ~AGRP_ACTIVE;
}
break;
case ANIMTYPE_FCURVE:
{
FCurve *fcu= (FCurve *)ale->data;
ACHANNEL_SET_FLAG(fcu, sel, FCURVE_SELECTED);
fcu->flag &= ~FCURVE_ACTIVE;
}
break;
case ANIMTYPE_NLATRACK:
{
NlaTrack *nlt= (NlaTrack *)ale->data;
ACHANNEL_SET_FLAG(nlt, sel, NLATRACK_SELECTED);
nlt->flag &= ~NLATRACK_ACTIVE;
}
break;
}
}
/* Cleanup */
BLI_freelistN(&anim_data);
}
/* ************************************************************************** */
/* OPERATORS */
/* ****************** Operator Utilities ********************************** */
/* poll callback for being in an Animation Editor channels list region */
int animedit_poll_channels_active (bContext *C)
{
ScrArea *sa= CTX_wm_area(C);
/* channels region test */
// TODO: could enhance with actually testing if channels region?
if (ELEM(NULL, sa, CTX_wm_region(C)))
return 0;
/* animation editor test */
if (ELEM3(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA) == 0)
return 0;
return 1;
}
/* poll callback for Animation Editor channels list region + not in NLA-tweakmode for NLA */
int animedit_poll_channels_nla_tweakmode_off (bContext *C)
{
ScrArea *sa= CTX_wm_area(C);
Scene *scene = CTX_data_scene(C);
/* channels region test */
// TODO: could enhance with actually testing if channels region?
if (ELEM(NULL, sa, CTX_wm_region(C)))
return 0;
/* animation editor test */
if (ELEM3(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA) == 0)
return 0;
/* NLA TweakMode test */
if (sa->spacetype == SPACE_NLA) {
if ((scene == NULL) || (scene->flag & SCE_NLA_EDIT_ON))
return 0;
}
return 1;
}
/* ****************** Rearrange Channels Operator ******************* */
/* This operator only works for Action Editor mode for now, as having it elsewhere makes things difficult */
#if 0 // XXX old animation system - needs to be updated for new system...
/* 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
};
/* make sure all action-channels belong to a group (and clear action's list) */
static void split_groups_action_temp (bAction *act, bActionGroup *tgrp)
{
bActionChannel *achan;
bActionGroup *agrp;
/* Separate action-channels into lists per group */
for (agrp= act->groups.first; agrp; agrp= agrp->next) {
if (agrp->channels.first) {
achan= agrp->channels.last;
act->chanbase.first= achan->next;
achan= agrp->channels.first;
achan->prev= NULL;
achan= agrp->channels.last;
achan->next= NULL;
}
}
/* Initialise memory for temp-group */
memset(tgrp, 0, sizeof(bActionGroup));
tgrp->flag |= (AGRP_EXPANDED|AGRP_TEMP);
strcpy(tgrp->name, "#TempGroup");
/* Move any action-channels not already moved, to the temp group */
if (act->chanbase.first) {
/* start of list */
achan= act->chanbase.first;
achan->prev= NULL;
tgrp->channels.first= achan;
act->chanbase.first= NULL;
/* end of list */
achan= act->chanbase.last;
achan->next= NULL;
tgrp->channels.last= achan;
act->chanbase.last= NULL;
}
/* Add temp-group to list */
BLI_addtail(&act->groups, tgrp);
}
/* link lists of channels that groups have */
static void join_groups_action_temp (bAction *act)
{
bActionGroup *agrp;
bActionChannel *achan;
for (agrp= act->groups.first; agrp; agrp= agrp->next) {
ListBase tempGroup;
/* add list of channels to action's channels */
tempGroup= agrp->channels;
addlisttolist(&act->chanbase, &agrp->channels);
agrp->channels= tempGroup;
/* clear moved flag */
agrp->flag &= ~AGRP_MOVED;
/* if temp-group... remove from list (but don't free as it's on the stack!) */
if (agrp->flag & AGRP_TEMP) {
BLI_remlink(&act->groups, agrp);
break;
}
}
/* clear "moved" flag from all achans */
for (achan= act->chanbase.first; achan; achan= achan->next)
achan->flag &= ~ACHAN_MOVED;
}
static short rearrange_actchannel_is_ok (Link *channel, short type)
{
if (type == ANIMTYPE_GROUP) {
bActionGroup *agrp= (bActionGroup *)channel;
if (SEL_AGRP(agrp) && !(agrp->flag & AGRP_MOVED))
return 1;
}
else if (type == ANIMTYPE_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 == ANIMTYPE_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_*
*/
static void rearrange_action_channels (bAnimContext *ac, short mode)
{
bAction *act;
bActionChannel *achan, *chan;
bActionGroup *agrp, *grp;
bActionGroup tgrp;
short (*rearrange_func)(ListBase *, Link *, short);
short do_channels = 1;
/* Get the active action, exit if none are selected */
act= (bAction *)ac->data;
/* exit if invalid mode */
switch (mode) {
case REARRANGE_ACTCHAN_TOP:
rearrange_func= rearrange_actchannel_top;
break;
case REARRANGE_ACTCHAN_UP:
rearrange_func= rearrange_actchannel_up;
break;
case REARRANGE_ACTCHAN_DOWN:
rearrange_func= rearrange_actchannel_down;
break;
case REARRANGE_ACTCHAN_BOTTOM:
rearrange_func= rearrange_actchannel_bottom;
break;
default:
return;
}
/* make sure we're only operating with groups */
split_groups_action_temp(act, &tgrp);
/* rearrange groups first (and then, only consider channels if the groups weren't moved) */
#define GET_FIRST(list) ((mode > 0) ? (list.first) : (list.last))
#define GET_NEXT(item) ((mode > 0) ? (item->next) : (item->prev))
for (agrp= GET_FIRST(act->groups); agrp; agrp= grp) {
/* Get next group to consider */
grp= GET_NEXT(agrp);
/* try to do group first */
if (rearrange_func(&act->groups, (Link *)agrp, ANIMTYPE_GROUP)) {
do_channels= 0;
agrp->flag |= AGRP_MOVED;
}
}
if (do_channels) {
for (agrp= GET_FIRST(act->groups); agrp; agrp= grp) {
/* Get next group to consider */
grp= GET_NEXT(agrp);
/* only consider action-channels if they're visible (group expanded) */
if (EXPANDED_AGRP(agrp)) {
for (achan= GET_FIRST(agrp->channels); achan; achan= chan) {
/* Get next channel to consider */
chan= GET_NEXT(achan);
/* Try to do channel */
if (rearrange_func(&agrp->channels, (Link *)achan, ANIMTYPE_ACHAN))
achan->flag |= ACHAN_MOVED;
}
}
}
}
#undef GET_FIRST
#undef GET_NEXT
/* assemble lists into one list (and clear moved tags) */
join_groups_action_temp(act);
}
/* ------------------- */
static int animchannels_rearrange_exec(bContext *C, wmOperator *op)
{
bAnimContext ac;
short mode;
/* get editor data - only for Action Editor (for now) */
if (ANIM_animdata_get_context(C, &ac) == 0)
return OPERATOR_CANCELLED;
if (ac.datatype != ANIMCONT_ACTION)
return OPERATOR_PASS_THROUGH;
/* get mode, then rearrange channels */
mode= RNA_enum_get(op->ptr, "direction");
rearrange_action_channels(&ac, mode);
/* set notifier tha things have changed */
ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_CHANNELS);
return OPERATOR_FINISHED;
}
void ANIM_OT_channels_move_up (wmOperatorType *ot)
{
/* identifiers */
ot->name= "Move Channel(s) Up";
ot->idname= "ANIM_OT_channels_move_up";
/* api callbacks */
ot->exec= animchannels_rearrange_exec;
ot->poll= ED_operator_areaactive;
/* flags */
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
/* props */
RNA_def_enum(ot->srna, "direction", NULL /* XXX add enum for this */, REARRANGE_ACTCHAN_UP, "Direction", "");
}
void ANIM_OT_channels_move_down (wmOperatorType *ot)
{
/* identifiers */
ot->name= "Move Channel(s) Down";
ot->idname= "ANIM_OT_channels_move_down";
/* api callbacks */
ot->exec= animchannels_rearrange_exec;
ot->poll= ED_operator_areaactive;
/* flags */
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
/* props */
RNA_def_enum(ot->srna, "direction", NULL /* XXX add enum for this */, REARRANGE_ACTCHAN_DOWN, "Direction", "");
}
void ANIM_OT_channels_move_top (wmOperatorType *ot)
{
/* identifiers */
ot->name= "Move Channel(s) to Top";
ot->idname= "ANIM_OT_channels_move_to_top";
/* api callbacks */
ot->exec= animchannels_rearrange_exec;
ot->poll= ED_operator_areaactive;
/* flags */
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
/* props */
RNA_def_enum(ot->srna, "direction", NULL /* XXX add enum for this */, REARRANGE_ACTCHAN_TOP, "Direction", "");
}
void ANIM_OT_channels_move_bottom (wmOperatorType *ot)
{
/* identifiers */
ot->name= "Move Channel(s) to Bottom";
ot->idname= "ANIM_OT_channels_move_to_bottom";
/* api callbacks */
ot->exec= animchannels_rearrange_exec;
ot->poll= ED_operator_areaactive;
/* flags */
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
/* props */
RNA_def_enum(ot->srna, "direction", NULL /* XXX add enum for this */, REARRANGE_ACTCHAN_BOTTOM, "Direction", "");
}
#endif // XXX old animation system - needs to be updated for new system...
/* ******************** Toggle Channel Visibility Operator *********************** */
static int animchannels_visibility_toggle_exec(bContext *C, wmOperator *op)
{
bAnimContext ac;
ListBase anim_data = {NULL, NULL};
bAnimListElem *ale;
int filter;
short vis= ACHANNEL_SETFLAG_ADD;
/* get editor data */
if (ANIM_animdata_get_context(C, &ac) == 0)
return OPERATOR_CANCELLED;
/* filter data */
filter= (ANIMFILTER_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_CURVESONLY);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* See if we should be making showing all selected or hiding */
for (ale= anim_data.first; ale; ale= ale->next) {
if (vis == ACHANNEL_SETFLAG_CLEAR)
break;
if (ale->flag & FCURVE_VISIBLE)
vis= ACHANNEL_SETFLAG_CLEAR;
}
/* Now set the flags */
for (ale= anim_data.first; ale; ale= ale->next) {
FCurve *fcu= (FCurve *)ale->data;
ACHANNEL_SET_FLAG(fcu, vis, FCURVE_VISIBLE);
}
/* cleanup */
BLI_freelistN(&anim_data);
/* set notifier tha things have changed */
ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_CHANNELS);
return OPERATOR_FINISHED;
}
void ANIM_OT_channels_visibility_toggle (wmOperatorType *ot)
{
/* identifiers */
ot->name= "Toggle Visibility";
ot->idname= "ANIM_OT_channels_visibility_toggle";
/* api callbacks */
ot->exec= animchannels_visibility_toggle_exec;
ot->poll= ED_operator_ipo_active;
/* flags */
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
}
/* ********************** Set Flags Operator *********************** */
enum {
// ACHANNEL_SETTING_SELECT = 0,
ACHANNEL_SETTING_PROTECT = 1,
ACHANNEL_SETTING_MUTE,
ACHANNEL_SETTING_VISIBLE,
ACHANNEL_SETTING_EXPAND,
} eAnimChannel_Settings;
/* defines for setting animation-channel flags */
EnumPropertyItem prop_animchannel_setflag_types[] = {
{ACHANNEL_SETFLAG_CLEAR, "DISABLE", 0, "Disable", ""},
{ACHANNEL_SETFLAG_ADD, "ENABLE", 0, "Enable", ""},
{ACHANNEL_SETFLAG_TOGGLE, "TOGGLE", 0, "Toggle", ""},
{0, NULL, 0, NULL, NULL}
};
/* defines for set animation-channel settings */
EnumPropertyItem prop_animchannel_settings_types[] = {
{ACHANNEL_SETTING_PROTECT, "PROTECT", 0, "Protect", ""},
{ACHANNEL_SETTING_MUTE, "MUTE", 0, "Mute", ""},
{0, NULL, 0, NULL, NULL}
};
/* ------------------- */
/* macro to be used in setflag_anim_channels */
#define ASUBCHANNEL_SEL_OK(ale) ( (onlysel == 0) || \
((ale->id) && (GS(ale->id->name)==ID_OB) && (((Object *)ale->id)->flag & SELECT)) )
/* Set/clear a particular flag (setting) for all selected + visible channels
* setting: the setting to modify
* mode: eAnimChannels_SetFlag
* onlysel: only selected channels get the flag set
*/
static void setflag_anim_channels (bAnimContext *ac, short setting, short mode, short onlysel)
{
ListBase anim_data = {NULL, NULL};
bAnimListElem *ale;
int filter;
/* filter data */
filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CHANNELS);
if (onlysel) filter |= ANIMFILTER_SEL;
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* affect selected channels */
for (ale= anim_data.first; ale; ale= ale->next) {
switch (ale->type) {
case ANIMTYPE_OBJECT:
{
Base *base= (Base *)ale->data;
Object *ob= base->object;
if (setting == ACHANNEL_SETTING_EXPAND) {
// XXX - settings should really be moved out of ob->nlaflag
if (mode == ACHANNEL_SETFLAG_TOGGLE) ob->nlaflag ^= OB_ADS_COLLAPSED;
else if (mode == ACHANNEL_SETFLAG_ADD) ob->nlaflag &= ~OB_ADS_COLLAPSED;
else ob->nlaflag |= OB_ADS_COLLAPSED;
}
}
break;
case ANIMTYPE_FILLACTD:
{
bAction *act= (bAction *)ale->data;
if (ASUBCHANNEL_SEL_OK(ale)) {
if (setting == ACHANNEL_SETTING_EXPAND) {
ACHANNEL_SET_FLAG_NEG(act, mode, ACT_COLLAPSED);
}
}
}
break;
case ANIMTYPE_FILLDRIVERS:
{
AnimData *adt= (AnimData *)ale->data;
if (ASUBCHANNEL_SEL_OK(ale)) {
if (setting == ACHANNEL_SETTING_EXPAND) {
ACHANNEL_SET_FLAG_NEG(adt, mode, ADT_DRIVERS_COLLAPSED);
}
}
}
break;
case ANIMTYPE_FILLMATD:
{
Object *ob= (Object *)ale->data;
// XXX - settings should really be moved out of ob->nlaflag
if ((onlysel == 0) || (ob->flag & SELECT)) {
if (setting == ACHANNEL_SETTING_EXPAND) {
if (mode == ACHANNEL_SETFLAG_TOGGLE) ob->nlaflag ^= OB_ADS_SHOWMATS;
else if (mode == ACHANNEL_SETFLAG_ADD) ob->nlaflag |= OB_ADS_SHOWMATS;
else ob->nlaflag &= ~OB_ADS_SHOWMATS;
}
}
}
break;
case ANIMTYPE_DSMAT:
{
Material *ma= (Material *)ale->data;
if (ASUBCHANNEL_SEL_OK(ale)) {
if (setting == ACHANNEL_SETTING_EXPAND) {
ACHANNEL_SET_FLAG(ma, mode, MA_DS_EXPAND);
}
}
}
break;
case ANIMTYPE_DSLAM:
{
Lamp *la= (Lamp *)ale->data;
if (ASUBCHANNEL_SEL_OK(ale)) {
if (setting == ACHANNEL_SETTING_EXPAND) {
ACHANNEL_SET_FLAG(la, mode, LA_DS_EXPAND);
}
}
}
break;
case ANIMTYPE_DSCAM:
{
Camera *ca= (Camera *)ale->data;
if (ASUBCHANNEL_SEL_OK(ale)) {
if (setting == ACHANNEL_SETTING_EXPAND) {
ACHANNEL_SET_FLAG(ca, mode, CAM_DS_EXPAND);
}
}
}
break;
case ANIMTYPE_DSCUR:
{
Curve *cu= (Curve *)ale->data;
if (ASUBCHANNEL_SEL_OK(ale)) {
if (setting == ACHANNEL_SETTING_EXPAND) {
ACHANNEL_SET_FLAG(cu, mode, CU_DS_EXPAND);
}
}
}
break;
case ANIMTYPE_DSSKEY:
{
Key *key= (Key *)ale->data;
if (ASUBCHANNEL_SEL_OK(ale)) {
if (setting == ACHANNEL_SETTING_EXPAND) {
ACHANNEL_SET_FLAG(key, mode, KEYBLOCK_DS_EXPAND);
}
}
}
break;
case ANIMTYPE_DSWOR:
{
World *wo= (World *)ale->data;
if (ASUBCHANNEL_SEL_OK(ale)) {
if (setting == ACHANNEL_SETTING_EXPAND) {
ACHANNEL_SET_FLAG(wo, mode, WO_DS_EXPAND);
}
}
}
break;
case ANIMTYPE_GROUP:
{
bActionGroup *agrp= (bActionGroup *)ale->data;
switch (setting) {
case ACHANNEL_SETTING_PROTECT:
ACHANNEL_SET_FLAG(agrp, mode, AGRP_PROTECTED);
break;
case ACHANNEL_SETTING_EXPAND:
ACHANNEL_SET_FLAG(agrp, mode, AGRP_EXPANDED);
break;
case ACHANNEL_SETTING_MUTE:
ACHANNEL_SET_FLAG(agrp, mode, AGRP_MUTED);
break;
case ACHANNEL_SETTING_VISIBLE:
ACHANNEL_SET_FLAG_NEG(agrp, mode, AGRP_NOTVISIBLE);
break;
}
}
break;
case ANIMTYPE_FCURVE:
{
FCurve *fcu= (FCurve *)ale->data;
switch (setting) {
case ACHANNEL_SETTING_MUTE:
ACHANNEL_SET_FLAG(fcu, mode, FCURVE_MUTED);
break;
case ACHANNEL_SETTING_PROTECT:
ACHANNEL_SET_FLAG(fcu, mode, FCURVE_PROTECTED);
break;
case ACHANNEL_SETTING_VISIBLE:
ACHANNEL_SET_FLAG(fcu, mode, FCURVE_VISIBLE);
break;
}
}
break;
case ANIMTYPE_GPLAYER:
{
bGPDlayer *gpl= (bGPDlayer *)ale->data;
switch (setting) {
case ACHANNEL_SETTING_MUTE:
ACHANNEL_SET_FLAG(gpl, mode, GP_LAYER_HIDE);
break;
case ACHANNEL_SETTING_PROTECT:
ACHANNEL_SET_FLAG(gpl, mode, GP_LAYER_LOCKED);
break;
}
}
break;
}
}
BLI_freelistN(&anim_data);
}
/* ------------------- */
static int animchannels_setflag_exec(bContext *C, wmOperator *op)
{
bAnimContext ac;
short mode, setting;
/* get editor data */
if (ANIM_animdata_get_context(C, &ac) == 0)
return OPERATOR_CANCELLED;
/* mode (eAnimChannels_SetFlag), setting (eAnimChannel_Settings) */
mode= RNA_enum_get(op->ptr, "mode");
setting= RNA_enum_get(op->ptr, "type");
/* modify setting */
setflag_anim_channels(&ac, setting, mode, 1);
/* set notifier tha things have changed */
ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_CHANNELS);
return OPERATOR_FINISHED;
}
void ANIM_OT_channels_setting_enable (wmOperatorType *ot)
{
/* identifiers */
ot->name= "Enable Channel Setting";
ot->idname= "ANIM_OT_channels_setting_enable";
/* api callbacks */
ot->invoke= WM_menu_invoke;
ot->exec= animchannels_setflag_exec;
ot->poll= animedit_poll_channels_active;
/* flags */
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
/* props */
/* flag-setting mode */
RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_ADD, "Mode", "");
/* setting to set */
RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
}
void ANIM_OT_channels_setting_disable (wmOperatorType *ot)
{
/* identifiers */
ot->name= "Disable Channel Setting";
ot->idname= "ANIM_OT_channels_setting_disable";
/* api callbacks */
ot->invoke= WM_menu_invoke;
ot->exec= animchannels_setflag_exec;
ot->poll= animedit_poll_channels_active;
/* flags */
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
/* props */
/* flag-setting mode */
RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_CLEAR, "Mode", "");
/* setting to set */
RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
}
void ANIM_OT_channels_setting_toggle (wmOperatorType *ot)
{
/* identifiers */
ot->name= "Toggle Channel Setting";
ot->idname= "ANIM_OT_channels_setting_toggle";
/* api callbacks */
ot->invoke= WM_menu_invoke;
ot->exec= animchannels_setflag_exec;
ot->poll= animedit_poll_channels_active;
/* flags */
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
/* props */
/* flag-setting mode */
RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_TOGGLE, "Mode", "");
/* setting to set */
RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
}
// XXX currently, this is a separate operator, but perhaps we could in future specify in keymaps whether to call invoke or exec?
void ANIM_OT_channels_editable_toggle (wmOperatorType *ot)
{
/* identifiers */
ot->name= "Toggle Channel Editability";
ot->idname= "ANIM_OT_channels_editable_toggle";
/* api callbacks */
ot->exec= animchannels_setflag_exec;
ot->poll= animedit_poll_channels_active;
/* flags */
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
/* props */
/* flag-setting mode */
RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_TOGGLE, "Mode", "");
/* setting to set */
RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, ACHANNEL_SETTING_PROTECT, "Type", "");
}
/* ********************** Expand Channels Operator *********************** */
static int animchannels_expand_exec (bContext *C, wmOperator *op)
{
bAnimContext ac;
short onlysel= 1;
/* get editor data */
if (ANIM_animdata_get_context(C, &ac) == 0)
return OPERATOR_CANCELLED;
/* only affect selected channels? */
if (RNA_boolean_get(op->ptr, "all"))
onlysel= 0;
/* modify setting */
setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_ADD, onlysel);
/* set notifier that things have changed */
ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_CHANNELS);
return OPERATOR_FINISHED;
}
void ANIM_OT_channels_expand (wmOperatorType *ot)
{
/* identifiers */
ot->name= "Expand Channels";
ot->idname= "ANIM_OT_channels_expand";
/* api callbacks */
ot->exec= animchannels_expand_exec;
ot->poll= animedit_poll_channels_active;
/* flags */
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
/* props */
RNA_def_boolean(ot->srna, "all", 0, "All", "Expand all channels (not just selected ones)");
}
/* ********************** Collapse Channels Operator *********************** */
static int animchannels_collapse_exec (bContext *C, wmOperator *op)
{
bAnimContext ac;
short onlysel= 1;
/* get editor data */
if (ANIM_animdata_get_context(C, &ac) == 0)
return OPERATOR_CANCELLED;
/* only affect selected channels? */
if (RNA_boolean_get(op->ptr, "all"))
onlysel= 0;
/* modify setting */
setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_CLEAR, onlysel);
/* set notifier that things have changed */
ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_CHANNELS);
return OPERATOR_FINISHED;
}
void ANIM_OT_channels_collapse (wmOperatorType *ot)
{
/* identifiers */
ot->name= "Collapse Channels";
ot->idname= "ANIM_OT_channels_collapse";
/* api callbacks */
ot->exec= animchannels_collapse_exec;
ot->poll= animedit_poll_channels_active;
/* flags */
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
/* props */
RNA_def_boolean(ot->srna, "all", 0, "All", "Collapse all channels (not just selected ones)");
}
/* ********************** Select All Operator *********************** */
static int animchannels_deselectall_exec(bContext *C, wmOperator *op)
{
bAnimContext ac;
/* get editor data */
if (ANIM_animdata_get_context(C, &ac) == 0)
return OPERATOR_CANCELLED;
/* 'standard' behaviour - check if selected, then apply relevant selection */
if (RNA_boolean_get(op->ptr, "invert"))
ANIM_deselect_anim_channels(ac.data, ac.datatype, 0, ACHANNEL_SETFLAG_TOGGLE);
else
ANIM_deselect_anim_channels(ac.data, ac.datatype, 1, ACHANNEL_SETFLAG_ADD);
/* set notifier tha things have changed */
ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_CHANNELS);
return OPERATOR_FINISHED;
}
void ANIM_OT_channels_select_all_toggle (wmOperatorType *ot)
{
/* identifiers */
ot->name= "Select All";
ot->idname= "ANIM_OT_channels_select_all_toggle";
/* api callbacks */
ot->exec= animchannels_deselectall_exec;
ot->poll= animedit_poll_channels_nla_tweakmode_off;
/* flags */
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
/* props */
RNA_def_boolean(ot->srna, "invert", 0, "Invert", "");
}
/* ******************** Borderselect Operator *********************** */
static void borderselect_anim_channels (bAnimContext *ac, rcti *rect, short selectmode)
{
ListBase anim_data = {NULL, NULL};
bAnimListElem *ale;
int filter;
View2D *v2d= &ac->ar->v2d;
rctf rectf;
float ymin=0, ymax=(float)(-ACHANNEL_HEIGHT);
/* convert border-region to view coordinates */
UI_view2d_region_to_view(v2d, rect->xmin, rect->ymin+2, &rectf.xmin, &rectf.ymin);
UI_view2d_region_to_view(v2d, rect->xmax, rect->ymax-2, &rectf.xmax, &rectf.ymax);
/* filter data */
filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CHANNELS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* loop over data, doing border select */
for (ale= anim_data.first; ale; ale= ale->next) {
ymin= ymax - ACHANNEL_STEP;
/* if channel is within border-select region, alter it */
if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))) {
/* only the following types can be selected */
switch (ale->type) {
case ANIMTYPE_OBJECT: /* object */
{
Base *base= (Base *)ale->data;
Object *ob= base->object;
ACHANNEL_SET_FLAG(base, selectmode, SELECT);
ACHANNEL_SET_FLAG(ob, selectmode, SELECT);
}
break;
case ANIMTYPE_GROUP: /* action group */
{
bActionGroup *agrp= (bActionGroup *)ale->data;
ACHANNEL_SET_FLAG(agrp, selectmode, AGRP_SELECTED);
agrp->flag &= ~AGRP_ACTIVE;
}
break;
case ANIMTYPE_FCURVE: /* F-Curve channel */
{
FCurve *fcu = (FCurve *)ale->data;
ACHANNEL_SET_FLAG(fcu, selectmode, FCURVE_SELECTED);
}
break;
case ANIMTYPE_GPLAYER: /* grease-pencil layer */
{
bGPDlayer *gpl = (bGPDlayer *)ale->data;
ACHANNEL_SET_FLAG(gpl, selectmode, GP_LAYER_SELECT);
}
break;
case ANIMTYPE_NLATRACK: /* nla-track */
{
NlaTrack *nlt= (NlaTrack *)ale->data;
ACHANNEL_SET_FLAG(nlt, selectmode, NLATRACK_SELECTED);
}
break;
}
}
/* set minimum extent to be the maximum of the next channel */
ymax= ymin;
}
/* cleanup */
BLI_freelistN(&anim_data);
}
/* ------------------- */
static int animchannels_borderselect_exec(bContext *C, wmOperator *op)
{
bAnimContext ac;
rcti rect;
short selectmode=0;
int event;
/* get editor data */
if (ANIM_animdata_get_context(C, &ac) == 0)
return OPERATOR_CANCELLED;
/* get settings from operator */
rect.xmin= RNA_int_get(op->ptr, "xmin");
rect.ymin= RNA_int_get(op->ptr, "ymin");
rect.xmax= RNA_int_get(op->ptr, "xmax");
rect.ymax= RNA_int_get(op->ptr, "ymax");
event= RNA_int_get(op->ptr, "event_type");
if (event == LEFTMOUSE) // FIXME... hardcoded
selectmode = ACHANNEL_SETFLAG_ADD;
else
selectmode = ACHANNEL_SETFLAG_CLEAR;
/* apply borderselect animation channels */
borderselect_anim_channels(&ac, &rect, selectmode);
return OPERATOR_FINISHED;
}
void ANIM_OT_channels_select_border(wmOperatorType *ot)
{
/* identifiers */
ot->name= "Border Select";
ot->idname= "ANIM_OT_channels_select_border";
/* api callbacks */
ot->invoke= WM_border_select_invoke;
ot->exec= animchannels_borderselect_exec;
ot->modal= WM_border_select_modal;
ot->poll= animedit_poll_channels_nla_tweakmode_off;
/* flags */
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
/* rna */
RNA_def_int(ot->srna, "event_type", 0, INT_MIN, INT_MAX, "Event Type", "", INT_MIN, INT_MAX);
RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
}
/* ******************** Mouse-Click Operator *********************** */
/* Depending on the channel that was clicked on, the mouse click will activate whichever
* part of the channel is relevant.
*
* NOTE: eventually, this should probably be phased out when many of these things are replaced with buttons
*/
static void mouse_anim_channels (bAnimContext *ac, float x, int channel_index, short selectmode)
{
ListBase anim_data = {NULL, NULL};
bAnimListElem *ale;
int filter;
/* get the channel that was clicked on */
/* filter channels */
filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CHANNELS);
filter= ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* get channel from index */
ale= BLI_findlink(&anim_data, channel_index);
if (ale == NULL) {
/* channel not found */
printf("Error: animation channel (index = %d) not found in mouse_anim_channels() \n", channel_index);
BLI_freelistN(&anim_data);
return;
}
/* selectmode -1 is a special case for ActionGroups only, which selects all of the channels underneath it only... */
// TODO: should this feature be extended to work with other channel types too?
if ((selectmode == -1) && (ale->type != ANIMTYPE_GROUP)) {
/* normal channels should not behave normally in this case */
BLI_freelistN(&anim_data);
return;
}
/* action to take depends on what channel we've got */
switch (ale->type) {
case ANIMTYPE_SCENE:
{
Scene *sce= (Scene *)ale->data;
if (x < 16) {
/* toggle expand */
sce->flag ^= SCE_DS_COLLAPSED;
}
else {
/* set selection status */
if (selectmode == SELECT_INVERT) {
/* swap select */
sce->flag ^= SCE_DS_SELECTED;
}
else {
sce->flag |= SCE_DS_SELECTED;
}
}
}
break;
case ANIMTYPE_OBJECT:
{
bDopeSheet *ads= (bDopeSheet *)ac->data;
Scene *sce= (Scene *)ads->source;
Base *base= (Base *)ale->data;
Object *ob= base->object;
if (x < 16) {
/* toggle expand */
ob->nlaflag ^= OB_ADS_COLLAPSED; // XXX
}
else {
/* set selection status */
if (selectmode == SELECT_INVERT) {
/* swap select */
base->flag ^= SELECT;
ob->flag= base->flag;
}
else {
Base *b;
/* deleselect all */
for (b= sce->base.first; b; b= b->next) {
b->flag &= ~SELECT;
b->object->flag= b->flag;
}
/* select object now */
base->flag |= SELECT;
ob->flag |= SELECT;
}
/* xxx should be ED_base_object_activate(), but we need context pointer for that... */
//set_active_base(base);
}
}
break;
case ANIMTYPE_FILLACTD:
{
bAction *act= (bAction *)ale->data;
act->flag ^= ACT_COLLAPSED;
}
break;
case ANIMTYPE_FILLDRIVERS:
{
AnimData *adt= (AnimData* )ale->data;
adt->flag ^= ADT_DRIVERS_COLLAPSED;
}
break;
case ANIMTYPE_FILLMATD:
{
Object *ob= (Object *)ale->data;
ob->nlaflag ^= OB_ADS_SHOWMATS; // XXX
}
break;
case ANIMTYPE_DSMAT:
{
Material *ma= (Material *)ale->data;
ma->flag ^= MA_DS_EXPAND;
}
break;
case ANIMTYPE_DSLAM:
{
Lamp *la= (Lamp *)ale->data;
la->flag ^= LA_DS_EXPAND;
}
break;
case ANIMTYPE_DSCAM:
{
Camera *ca= (Camera *)ale->data;
ca->flag ^= CAM_DS_EXPAND;
}
break;
case ANIMTYPE_DSCUR:
{
Curve *cu= (Curve *)ale->data;
cu->flag ^= CU_DS_EXPAND;
}
break;
case ANIMTYPE_DSSKEY:
{
Key *key= (Key *)ale->data;
key->flag ^= KEYBLOCK_DS_EXPAND;
}
break;
case ANIMTYPE_DSWOR:
{
World *wo= (World *)ale->data;
wo->flag ^= WO_DS_EXPAND;
}
break;
case ANIMTYPE_GROUP:
{
bActionGroup *agrp= (bActionGroup *)ale->data;
short offset= (ELEM3(ac->datatype, ANIMCONT_DOPESHEET, ANIMCONT_FCURVES, ANIMCONT_DRIVERS))? 18 : 0;
if ((x < (offset+17)) && (agrp->channels.first)) {
/* toggle expand */
agrp->flag ^= AGRP_EXPANDED;
}
else if ((x < (offset+32)) && (ac->spacetype==SPACE_IPO)) {
/* toggle visibility (of grouped F-Curves in Graph editor) */
agrp->flag ^= AGRP_NOTVISIBLE;
}
else if (x >= (ACHANNEL_NAMEWIDTH-ACHANNEL_BUTTON_WIDTH)) {
/* toggle protection/locking */
agrp->flag ^= AGRP_PROTECTED;
}
else if (x >= (ACHANNEL_NAMEWIDTH-2*ACHANNEL_BUTTON_WIDTH)) {
/* toggle mute */
agrp->flag ^= AGRP_MUTED;
}
else {
/* select/deselect group */
if (selectmode == SELECT_INVERT) {
/* inverse selection status of this group only */
agrp->flag ^= AGRP_SELECTED;
}
else if (selectmode == -1) {
/* select all in group (and deselect everthing else) */
FCurve *fcu;
/* deselect all other channels */
ANIM_deselect_anim_channels(ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
/* only select channels in group and group itself */
for (fcu= agrp->channels.first; fcu && fcu->grp==agrp; fcu= fcu->next)
fcu->flag |= FCURVE_SELECTED;
agrp->flag |= AGRP_SELECTED;
}
else {
/* select group by itself */
ANIM_deselect_anim_channels(ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
agrp->flag |= AGRP_SELECTED;
}
/* if group is selected now, make group the 'active' one in the visible list */
if (agrp->flag & AGRP_SELECTED)
NLA SoC: Operators for 'tweaking' strip actions (TAB-Key for both) In this commit, I've introduced the mechanism by which actions already referenced by strips used in the NLA can be edited (or 'tweaked'). To use, simply select a strip you wish to edit, and hit that TAB key to start tweaking that strip's action, and hit TAB again once you're done. What happens when you enter 'tweak mode': 1) The action of the active strip temporarily becomes the 'active action' of the AnimData block. You are now able to edit this in one of the Animation Editors (DopeSheet/Action, Graph Editors) as per normal (i.e. sliding keyframes around, inserting keyframes, etc.). The 'action-line' will therefore get drawn immediately above the active track containing the active strip, so that it's clear that that's what we're editing. 2) All the NLA-tracks (and all the strips within them) that occur after the track that the active strip lived in get disabled while you're in tweakmode. This is equivalent to travelling back to an earlier state in a construction history stack. 3) The active NLA track also gets disabled while in tweakmode, since it would otherwise interfere with the correct functioning of the tweaking for the action of interest. 4) The 'real' active action (i.e. the one displaced by the active strip's action) gets put into temp storage, and will be restored after you exit tweakmode. 5) Any strips which also reference the action being tweaked will get highlighted in red shading to indicate that you may be making some changes to the action which you don't really want to make for the other users too. Please note though, that this is only a rough prototype of this functionality, with some niceties still to come. i.e.: * NLA-tracks after the active track should still get drawn above the 'tweaking action line', but perhaps with different appearance? * Various tools will still need awareness of this to prevent corrupting operations from taking place. How to proceed is still undecided... * When exiting tweak-mode, the strip the action came from still needs some form of syncing with the modified action... there are a few tricky issues here that will need to be solved * Evaluation code doesn't totally take this into account yet... --- Also, fixed a number of bugs with various code (notably selection, and also a few drawing bugs)
2009-06-05 05:18:07 +00:00
ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, agrp, ANIMTYPE_GROUP);
}
}
break;
case ANIMTYPE_FCURVE:
{
FCurve *fcu= (FCurve *)ale->data;
short offset;
if (ac->datatype != ANIMCONT_ACTION) {
/* for now, special case for materials */
if (ale->ownertype == ANIMTYPE_DSMAT)
offset= 21;
else
offset= 18;
}
else
offset = 0;
if (x >= (ACHANNEL_NAMEWIDTH-ACHANNEL_BUTTON_WIDTH)) {
/* toggle protection (only if there's a toggle there) */
if (fcu->bezt)
fcu->flag ^= FCURVE_PROTECTED;
}
else if (x >= (ACHANNEL_NAMEWIDTH-2*ACHANNEL_BUTTON_WIDTH)) {
/* toggle mute */
fcu->flag ^= FCURVE_MUTED;
}
else if ((x < (offset+17)) && (ac->spacetype==SPACE_IPO)) {
/* toggle visibility */
fcu->flag ^= FCURVE_VISIBLE;
}
else {
/* select/deselect */
if (selectmode == SELECT_INVERT) {
/* inverse selection status of this F-Curve only */
fcu->flag ^= FCURVE_SELECTED;
}
else {
/* select F-Curve by itself */
ANIM_deselect_anim_channels(ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
fcu->flag |= FCURVE_SELECTED;
}
/* if F-Curve is selected now, make F-Curve the 'active' one in the visible list */
if (fcu->flag & FCURVE_SELECTED)
NLA SoC: Operators for 'tweaking' strip actions (TAB-Key for both) In this commit, I've introduced the mechanism by which actions already referenced by strips used in the NLA can be edited (or 'tweaked'). To use, simply select a strip you wish to edit, and hit that TAB key to start tweaking that strip's action, and hit TAB again once you're done. What happens when you enter 'tweak mode': 1) The action of the active strip temporarily becomes the 'active action' of the AnimData block. You are now able to edit this in one of the Animation Editors (DopeSheet/Action, Graph Editors) as per normal (i.e. sliding keyframes around, inserting keyframes, etc.). The 'action-line' will therefore get drawn immediately above the active track containing the active strip, so that it's clear that that's what we're editing. 2) All the NLA-tracks (and all the strips within them) that occur after the track that the active strip lived in get disabled while you're in tweakmode. This is equivalent to travelling back to an earlier state in a construction history stack. 3) The active NLA track also gets disabled while in tweakmode, since it would otherwise interfere with the correct functioning of the tweaking for the action of interest. 4) The 'real' active action (i.e. the one displaced by the active strip's action) gets put into temp storage, and will be restored after you exit tweakmode. 5) Any strips which also reference the action being tweaked will get highlighted in red shading to indicate that you may be making some changes to the action which you don't really want to make for the other users too. Please note though, that this is only a rough prototype of this functionality, with some niceties still to come. i.e.: * NLA-tracks after the active track should still get drawn above the 'tweaking action line', but perhaps with different appearance? * Various tools will still need awareness of this to prevent corrupting operations from taking place. How to proceed is still undecided... * When exiting tweak-mode, the strip the action came from still needs some form of syncing with the modified action... there are a few tricky issues here that will need to be solved * Evaluation code doesn't totally take this into account yet... --- Also, fixed a number of bugs with various code (notably selection, and also a few drawing bugs)
2009-06-05 05:18:07 +00:00
ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, fcu, ANIMTYPE_FCURVE);
}
}
break;
case ANIMTYPE_GPDATABLOCK:
{
bGPdata *gpd= (bGPdata *)ale->data;
/* toggle expand */
gpd->flag ^= GP_DATA_EXPAND;
}
break;
case ANIMTYPE_GPLAYER:
{
#if 0 // XXX future of this is unclear
bGPdata *gpd= (bGPdata *)ale->owner;
bGPDlayer *gpl= (bGPDlayer *)ale->data;
if (x >= (ACHANNEL_NAMEWIDTH-16)) {
/* toggle lock */
gpl->flag ^= GP_LAYER_LOCKED;
}
else if (x >= (ACHANNEL_NAMEWIDTH-32)) {
/* toggle hide */
gpl->flag ^= GP_LAYER_HIDE;
}
else {
/* select/deselect */
//if (G.qual & LR_SHIFTKEY) {
//select_gplayer_channel(gpd, gpl, SELECT_INVERT);
//}
//else {
//deselect_gpencil_layers(data, 0);
//select_gplayer_channel(gpd, gpl, SELECT_INVERT);
//}
}
#endif // XXX future of this is unclear
}
break;
case ANIMTYPE_SHAPEKEY:
/* TODO: shapekey channels cannot be selected atm... */
break;
default:
printf("Error: Invalid channel type in mouse_anim_channels() \n");
}
/* free channels */
BLI_freelistN(&anim_data);
}
/* ------------------- */
/* handle clicking */
static int animchannels_mouseclick_invoke(bContext *C, wmOperator *op, wmEvent *event)
{
bAnimContext ac;
Scene *scene;
ARegion *ar;
View2D *v2d;
int mval[2], channel_index;
short selectmode;
float x, y;
/* get editor data */
if (ANIM_animdata_get_context(C, &ac) == 0)
return OPERATOR_CANCELLED;
/* get useful pointers from animation context data */
scene= ac.scene;
ar= ac.ar;
v2d= &ar->v2d;
/* get mouse coordinates (in region coordinates) */
mval[0]= (event->x - ar->winrct.xmin);
mval[1]= (event->y - ar->winrct.ymin);
/* select mode is either replace (deselect all, then add) or add/extend */
if (RNA_boolean_get(op->ptr, "extend"))
selectmode= SELECT_INVERT;
else if (RNA_boolean_get(op->ptr, "children_only"))
selectmode= -1; /* this is a bit of a special case for ActionGroups only... should it be removed or extended to all instead? */
else
selectmode= SELECT_REPLACE;
/* figure out which channel user clicked in
* Note: although channels technically start at y= ACHANNEL_FIRST, we need to adjust by half a channel's height
* so that the tops of channels get caught ok. Since ACHANNEL_FIRST is really ACHANNEL_HEIGHT, we simply use
* ACHANNEL_HEIGHT_HALF.
*/
UI_view2d_region_to_view(v2d, mval[0], mval[1], &x, &y);
UI_view2d_listview_view_to_cell(v2d, ACHANNEL_NAMEWIDTH, ACHANNEL_STEP, 0, (float)ACHANNEL_HEIGHT_HALF, x, y, NULL, &channel_index);
/* handle mouse-click in the relevant channel then */
mouse_anim_channels(&ac, x, channel_index, selectmode);
/* set notifier tha things have changed */
ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_CHANNELS);
return OPERATOR_FINISHED;
}
void ANIM_OT_channels_click (wmOperatorType *ot)
{
/* identifiers */
ot->name= "Mouse Click on Channels";
ot->idname= "ANIM_OT_channels_click";
/* api callbacks */
ot->invoke= animchannels_mouseclick_invoke;
ot->poll= animedit_poll_channels_active;
/* flags */
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
/* id-props */
RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", ""); // SHIFTKEY
RNA_def_boolean(ot->srna, "children_only", 0, "Select Children Only", ""); // CTRLKEY|SHIFTKEY
}
/* ************************************************************************** */
/* Operator Registration */
void ED_operatortypes_animchannels(void)
{
WM_operatortype_append(ANIM_OT_channels_select_all_toggle);
WM_operatortype_append(ANIM_OT_channels_select_border);
WM_operatortype_append(ANIM_OT_channels_click);
WM_operatortype_append(ANIM_OT_channels_setting_enable);
WM_operatortype_append(ANIM_OT_channels_setting_disable);
WM_operatortype_append(ANIM_OT_channels_setting_toggle);
// XXX does this need to be a separate operator?
WM_operatortype_append(ANIM_OT_channels_editable_toggle);
// XXX these need to be updated for new system... todo...
//WM_operatortype_append(ANIM_OT_channels_move_up);
//WM_operatortype_append(ANIM_OT_channels_move_down);
//WM_operatortype_append(ANIM_OT_channels_move_top);
//WM_operatortype_append(ANIM_OT_channels_move_bottom);
WM_operatortype_append(ANIM_OT_channels_expand);
WM_operatortype_append(ANIM_OT_channels_collapse);
WM_operatortype_append(ANIM_OT_channels_visibility_toggle);
}
void ED_keymap_animchannels(wmWindowManager *wm)
{
ListBase *keymap = WM_keymap_listbase(wm, "Animation_Channels", 0, 0);
/* selection */
/* click-select */
// XXX for now, only leftmouse....
WM_keymap_add_item(keymap, "ANIM_OT_channels_click", LEFTMOUSE, KM_PRESS, 0, 0);
RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_click", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0)->ptr, "extend", 1);
RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_click", LEFTMOUSE, KM_PRESS, KM_CTRL|KM_SHIFT, 0)->ptr, "children_only", 1);
/* deselect all */
WM_keymap_add_item(keymap, "ANIM_OT_channels_select_all_toggle", AKEY, KM_PRESS, 0, 0);
RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_select_all_toggle", IKEY, KM_PRESS, KM_CTRL, 0)->ptr, "invert", 1);
/* borderselect */
WM_keymap_add_item(keymap, "ANIM_OT_channels_select_border", BKEY, KM_PRESS, 0, 0);
/* settings */
WM_keymap_add_item(keymap, "ANIM_OT_channels_setting_toggle", WKEY, KM_PRESS, KM_SHIFT, 0);
WM_keymap_add_item(keymap, "ANIM_OT_channels_setting_enable", WKEY, KM_PRESS, KM_CTRL|KM_SHIFT, 0);
WM_keymap_add_item(keymap, "ANIM_OT_channels_setting_disable", WKEY, KM_PRESS, KM_ALT, 0);
/* settings - specialised hotkeys */
WM_keymap_add_item(keymap, "ANIM_OT_channels_editable_toggle", TABKEY, KM_PRESS, 0, 0);
/* expand/collapse */
WM_keymap_add_item(keymap, "ANIM_OT_channels_expand", PADPLUSKEY, KM_PRESS, 0, 0);
WM_keymap_add_item(keymap, "ANIM_OT_channels_collapse", PADMINUS, KM_PRESS, 0, 0);
RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_expand", PADPLUSKEY, KM_PRESS, KM_CTRL, 0)->ptr, "all", 1);
RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_collapse", PADMINUS, KM_PRESS, KM_CTRL, 0)->ptr, "all", 1);
/* rearranging - actions only */
//WM_keymap_add_item(keymap, "ANIM_OT_channels_move_up", PAGEUPKEY, KM_PRESS, KM_SHIFT, 0);
//WM_keymap_add_item(keymap, "ANIM_OT_channels_move_down", PAGEDOWNKEY, KM_PRESS, KM_SHIFT, 0);
//WM_keymap_add_item(keymap, "ANIM_OT_channels_move_to_top", PAGEUPKEY, KM_PRESS, KM_CTRL|KM_SHIFT, 0);
//WM_keymap_add_item(keymap, "ANIM_OT_channels_move_to_bottom", PAGEDOWNKEY, KM_PRESS, KM_CTRL|KM_SHIFT, 0);
/* Graph Editor only */
WM_keymap_add_item(keymap, "ANIM_OT_channels_visibility_toggle", VKEY, KM_PRESS, 0, 0);
}
/* ************************************************************************** */