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/space_nla/nla_channels.c
Campbell Barton e0ba6a4411 Cleanup: rename evt to event
Following naming convention of most operators.
2021-03-05 14:04:22 +11:00

904 lines
27 KiB
C

/*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung
* All rights reserved.
*/
/** \file
* \ingroup spnla
*/
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "DNA_anim_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
#include "BKE_anim_data.h"
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_nla.h"
#include "BKE_report.h"
#include "BKE_scene.h"
#include "BKE_screen.h"
#include "ED_anim_api.h"
#include "ED_keyframes_edit.h"
#include "ED_object.h"
#include "ED_screen.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "WM_api.h"
#include "WM_types.h"
#include "UI_interface.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
#include "UI_view2d.h"
#include "nla_intern.h" /* own include */
/* *********************************************** */
/* Operators for NLA channels-list which need to be different
* from the standard Animation Editor ones */
/* ******************** 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
* --> Most channels are now selection only.
*/
static int mouse_nla_channels(
bContext *C, bAnimContext *ac, float x, int channel_index, short selectmode)
{
ListBase anim_data = {NULL, NULL};
bAnimListElem *ale;
int filter;
View2D *v2d = &ac->region->v2d;
int notifierFlags = 0;
/* get the channel that was clicked on */
/* filter channels */
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
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 */
if (G.debug & G_DEBUG) {
printf("Error: animation channel (index = %d) not found in mouse_anim_channels()\n",
channel_index);
}
ANIM_animdata_freelist(&anim_data);
return 0;
}
/* action to take depends on what channel we've got */
/* WARNING: must keep this in sync with the equivalent function in anim_channels_edit.c */
switch (ale->type) {
case ANIMTYPE_SCENE: {
Scene *sce = (Scene *)ale->data;
AnimData *adt = sce->adt;
/* set selection status */
if (selectmode == SELECT_INVERT) {
/* swap select */
sce->flag ^= SCE_DS_SELECTED;
if (adt) {
adt->flag ^= ADT_UI_SELECTED;
}
}
else {
sce->flag |= SCE_DS_SELECTED;
if (adt) {
adt->flag |= ADT_UI_SELECTED;
}
}
notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
break;
}
case ANIMTYPE_OBJECT: {
ViewLayer *view_layer = ac->view_layer;
Base *base = (Base *)ale->data;
Object *ob = base->object;
AnimData *adt = ob->adt;
if (nlaedit_is_tweakmode_on(ac) == 0 && (base->flag & BASE_SELECTABLE)) {
/* set selection status */
if (selectmode == SELECT_INVERT) {
/* swap select */
ED_object_base_select(base, BA_INVERT);
if (adt) {
adt->flag ^= ADT_UI_SELECTED;
}
}
else {
/* deselect all */
/* TODO: should this deselect all other types of channels too? */
LISTBASE_FOREACH (Base *, b, &view_layer->object_bases) {
ED_object_base_select(b, BA_DESELECT);
if (b->object->adt) {
b->object->adt->flag &= ~(ADT_UI_SELECTED | ADT_UI_ACTIVE);
}
}
/* select object now */
ED_object_base_select(base, BA_SELECT);
if (adt) {
adt->flag |= ADT_UI_SELECTED;
}
}
/* change active object - regardless of whether it is now selected [T37883] */
ED_object_base_activate_with_mode_exit_if_needed(C, base); /* adds notifier */
if ((adt) && (adt->flag & ADT_UI_SELECTED)) {
adt->flag |= ADT_UI_ACTIVE;
}
/* notifiers - channel was selected */
notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
}
break;
}
case ANIMTYPE_FILLACTD: /* Action Expander */
case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
case ANIMTYPE_DSLAM:
case ANIMTYPE_DSCAM:
case ANIMTYPE_DSCACHEFILE:
case ANIMTYPE_DSCUR:
case ANIMTYPE_DSSKEY:
case ANIMTYPE_DSWOR:
case ANIMTYPE_DSNTREE:
case ANIMTYPE_DSPART:
case ANIMTYPE_DSMBALL:
case ANIMTYPE_DSARM:
case ANIMTYPE_DSMESH:
case ANIMTYPE_DSTEX:
case ANIMTYPE_DSLAT:
case ANIMTYPE_DSLINESTYLE:
case ANIMTYPE_DSSPK:
case ANIMTYPE_DSGPENCIL:
case ANIMTYPE_PALETTE:
case ANIMTYPE_DSHAIR:
case ANIMTYPE_DSPOINTCLOUD:
case ANIMTYPE_DSVOLUME:
case ANIMTYPE_DSSIMULATION: {
/* sanity checking... */
if (ale->adt) {
/* select/deselect */
if (selectmode == SELECT_INVERT) {
/* inverse selection status of this AnimData block only */
ale->adt->flag ^= ADT_UI_SELECTED;
}
else {
/* select AnimData block by itself */
ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_CLEAR);
ale->adt->flag |= ADT_UI_SELECTED;
}
/* set active? */
if ((ale->adt) && (ale->adt->flag & ADT_UI_SELECTED)) {
ale->adt->flag |= ADT_UI_ACTIVE;
}
}
notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
break;
}
case ANIMTYPE_NLATRACK: {
NlaTrack *nlt = (NlaTrack *)ale->data;
AnimData *adt = ale->adt;
short offset;
/* offset for start of channel (on LHS of channel-list) */
if (ale->id) {
/* special exception for materials and particles */
if (ELEM(GS(ale->id->name), ID_MA, ID_PA)) {
offset = 21 + NLACHANNEL_BUTTON_WIDTH;
}
else {
offset = 14;
}
}
else {
offset = 0;
}
if (x >= (v2d->cur.xmax - NLACHANNEL_BUTTON_WIDTH)) {
/* toggle protection (only if there's a toggle there) */
nlt->flag ^= NLATRACK_PROTECTED;
/* notifier flags - channel was edited */
notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
}
else if (x >= (v2d->cur.xmax - 2 * NLACHANNEL_BUTTON_WIDTH)) {
/* toggle mute */
nlt->flag ^= NLATRACK_MUTED;
/* notifier flags - channel was edited */
notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
ale->update |= ANIM_UPDATE_DEPS;
}
else if (x <= ((NLACHANNEL_BUTTON_WIDTH * 2) + offset)) {
/* toggle 'solo' */
BKE_nlatrack_solo_toggle(adt, nlt);
/* notifier flags - channel was edited */
notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
ale->update |= ANIM_UPDATE_DEPS;
}
else if (nlaedit_is_tweakmode_on(ac) == 0) {
/* set selection */
if (selectmode == SELECT_INVERT) {
/* inverse selection status of this F-Curve only */
nlt->flag ^= NLATRACK_SELECTED;
}
else {
/* select F-Curve by itself */
ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_CLEAR);
nlt->flag |= NLATRACK_SELECTED;
}
/* if NLA-Track is selected now,
* make NLA-Track the 'active' one in the visible list */
if (nlt->flag & NLATRACK_SELECTED) {
ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, nlt, ANIMTYPE_NLATRACK);
}
/* notifier flags - channel was selected */
notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
}
break;
}
case ANIMTYPE_NLAACTION: {
AnimData *adt = BKE_animdata_from_id(ale->id);
/* button region... */
if (x >= (v2d->cur.xmax - NLACHANNEL_BUTTON_WIDTH)) {
if (nlaedit_is_tweakmode_on(ac) == 0) {
/* 'push-down' action - only usable when not in TweakMode */
/* TODO: make this use the operator instead of calling the function directly
* however, calling the operator requires that we supply the args,
* and that works with proper buttons only */
BKE_nla_action_pushdown(adt, ID_IS_OVERRIDE_LIBRARY(ale->id));
}
else {
/* when in tweakmode, this button becomes the toggle for mapped editing */
adt->flag ^= ADT_NLA_EDIT_NOMAP;
}
/* changes to NLA-Action occurred */
notifierFlags |= ND_NLA_ACTCHANGE;
ale->update |= ANIM_UPDATE_DEPS;
}
/* OR rest of name... */
else {
/* NOTE: rest of NLA-Action name doubles for operating on the AnimData block
* - this is useful when there's no clear divider, and makes more sense in
* the case of users trying to use this to change actions
* - in tweakmode, clicking here gets us out of tweakmode, as changing selection
* while in tweakmode is really evil!
* - we disable "solo" flags too, to make it easier to work with stashed actions
* with less trouble
*/
if (nlaedit_is_tweakmode_on(ac)) {
/* exit tweakmode immediately */
nlaedit_disable_tweakmode(ac, true);
/* changes to NLA-Action occurred */
notifierFlags |= ND_NLA_ACTCHANGE;
ale->update |= ANIM_UPDATE_DEPS;
}
else {
/* select/deselect */
if (selectmode == SELECT_INVERT) {
/* inverse selection status of this AnimData block only */
adt->flag ^= ADT_UI_SELECTED;
}
else {
/* select AnimData block by itself */
ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_CLEAR);
adt->flag |= ADT_UI_SELECTED;
}
/* set active? */
if (adt->flag & ADT_UI_SELECTED) {
adt->flag |= ADT_UI_ACTIVE;
}
notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
}
}
break;
}
default:
if (G.debug & G_DEBUG) {
printf("Error: Invalid channel type in mouse_nla_channels()\n");
}
break;
}
/* free channels */
ANIM_animdata_update(ac, &anim_data);
ANIM_animdata_freelist(&anim_data);
/* return the notifier-flags set */
return notifierFlags;
}
/* ------------------- */
/* handle clicking */
static int nlachannels_mouseclick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
bAnimContext ac;
SpaceNla *snla;
ARegion *region;
View2D *v2d;
int channel_index;
int notifierFlags = 0;
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 */
snla = (SpaceNla *)ac.sl;
region = ac.region;
v2d = &region->v2d;
/* select mode is either replace (deselect all, then add) or add/extend */
if (RNA_boolean_get(op->ptr, "extend")) {
selectmode = SELECT_INVERT;
}
else {
selectmode = SELECT_REPLACE;
}
/* Figure out which channel user clicked in. */
UI_view2d_region_to_view(v2d, event->mval[0], event->mval[1], &x, &y);
UI_view2d_listview_view_to_cell(NLACHANNEL_NAMEWIDTH,
NLACHANNEL_STEP(snla),
0,
NLACHANNEL_FIRST_TOP(&ac),
x,
y,
NULL,
&channel_index);
/* handle mouse-click in the relevant channel then */
notifierFlags = mouse_nla_channels(C, &ac, x, channel_index, selectmode);
/* set notifier that things have changed */
WM_event_add_notifier(C, NC_ANIMATION | notifierFlags, NULL);
return OPERATOR_FINISHED;
}
void NLA_OT_channels_click(wmOperatorType *ot)
{
PropertyRNA *prop;
/* identifiers */
ot->name = "Mouse Click on NLA Channels";
ot->idname = "NLA_OT_channels_click";
ot->description = "Handle clicks to select NLA channels";
/* api callbacks */
ot->invoke = nlachannels_mouseclick_invoke;
ot->poll = ED_operator_nla_active;
/* flags */
ot->flag = OPTYPE_UNDO;
/* props */
prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", ""); /* SHIFTKEY */
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
/* *********************************************** */
/* Special Operators */
/* ******************** Action Push Down ******************************** */
static int nlachannels_pushdown_exec(bContext *C, wmOperator *op)
{
bAnimContext ac;
ID *id = NULL;
AnimData *adt = NULL;
int channel_index = RNA_int_get(op->ptr, "channel_index");
/* get editor data */
if (ANIM_animdata_get_context(C, &ac) == 0) {
return OPERATOR_CANCELLED;
}
/* get anim-channel to use (or more specifically, the animdata block behind it) */
if (channel_index == -1) {
PointerRNA adt_ptr = {NULL};
/* active animdata block */
if (nla_panel_context(C, &adt_ptr, NULL, NULL) == 0 || (adt_ptr.data == NULL)) {
BKE_report(op->reports,
RPT_ERROR,
"No active AnimData block to use "
"(select a data-block expander first or set the appropriate flags on an AnimData "
"block)");
return OPERATOR_CANCELLED;
}
id = adt_ptr.owner_id;
adt = adt_ptr.data;
}
else {
/* indexed channel */
ListBase anim_data = {NULL, NULL};
bAnimListElem *ale;
int filter;
/* filter channels */
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
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) {
BKE_reportf(op->reports, RPT_ERROR, "No animation channel found at index %d", channel_index);
ANIM_animdata_freelist(&anim_data);
return OPERATOR_CANCELLED;
}
if (ale->type != ANIMTYPE_NLAACTION) {
BKE_reportf(op->reports,
RPT_ERROR,
"Animation channel at index %d is not a NLA 'Active Action' channel",
channel_index);
ANIM_animdata_freelist(&anim_data);
return OPERATOR_CANCELLED;
}
/* grab AnimData from the channel */
adt = ale->adt;
id = ale->id;
/* we don't need anything here anymore, so free it all */
ANIM_animdata_freelist(&anim_data);
}
/* double-check that we are free to push down here... */
if (adt == NULL) {
BKE_report(op->reports, RPT_WARNING, "Internal Error - AnimData block is not valid");
return OPERATOR_CANCELLED;
}
if (nlaedit_is_tweakmode_on(&ac)) {
BKE_report(op->reports,
RPT_WARNING,
"Cannot push down actions while tweaking a strip's action, exit tweak mode first");
return OPERATOR_CANCELLED;
}
if (adt->action == NULL) {
BKE_report(op->reports, RPT_WARNING, "No active action to push down");
return OPERATOR_CANCELLED;
}
/* 'push-down' action - only usable when not in TweakMode */
BKE_nla_action_pushdown(adt, ID_IS_OVERRIDE_LIBRARY(id));
struct Main *bmain = CTX_data_main(C);
DEG_id_tag_update_ex(bmain, id, ID_RECALC_ANIMATION);
/* The action needs updating too, as FCurve modifiers are to be reevaluated. They won't extend
* beyond the NLA strip after pushing down to the NLA. */
DEG_id_tag_update_ex(bmain, &adt->action->id, ID_RECALC_ANIMATION);
/* set notifier that things have changed */
WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL);
return OPERATOR_FINISHED;
}
void NLA_OT_action_pushdown(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Push Down Action";
ot->idname = "NLA_OT_action_pushdown";
ot->description = "Push action down onto the top of the NLA stack as a new strip";
/* callbacks */
ot->exec = nlachannels_pushdown_exec;
ot->poll = nlaop_poll_tweakmode_off;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
ot->prop = RNA_def_int(ot->srna,
"channel_index",
-1,
-1,
INT_MAX,
"Channel Index",
"Index of NLA action channel to perform pushdown operation on",
0,
INT_MAX);
RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE);
}
/* ******************** Action Unlink ******************************** */
static bool nla_action_unlink_poll(bContext *C)
{
if (ED_operator_nla_active(C)) {
return nla_panel_context(C, NULL, NULL, NULL);
}
/* something failed... */
return false;
}
static int nla_action_unlink_exec(bContext *C, wmOperator *op)
{
PointerRNA adt_ptr;
AnimData *adt;
/* check context and also validity of pointer */
if (!nla_panel_context(C, &adt_ptr, NULL, NULL)) {
return OPERATOR_CANCELLED;
}
/* get animdata */
adt = adt_ptr.data;
if (adt == NULL) {
return OPERATOR_CANCELLED;
}
/* do unlinking */
if (adt->action) {
bool force_delete = RNA_boolean_get(op->ptr, "force_delete");
ED_animedit_unlink_action(C, adt_ptr.owner_id, adt, adt->action, op->reports, force_delete);
}
return OPERATOR_FINISHED;
}
static int nla_action_unlink_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
/* NOTE: this is hardcoded to match the behavior for the unlink button
* (in interface_templates.c) */
RNA_boolean_set(op->ptr, "force_delete", event->shift != 0);
return nla_action_unlink_exec(C, op);
}
void NLA_OT_action_unlink(wmOperatorType *ot)
{
PropertyRNA *prop;
/* identifiers */
ot->name = "Unlink Action";
ot->idname = "NLA_OT_action_unlink";
ot->description = "Unlink this action from the active action slot (and/or exit Tweak Mode)";
/* callbacks */
ot->invoke = nla_action_unlink_invoke;
ot->exec = nla_action_unlink_exec;
ot->poll = nla_action_unlink_poll;
/* properties */
prop = RNA_def_boolean(ot->srna,
"force_delete",
false,
"Force Delete",
"Clear Fake User and remove copy stashed in this data-block's NLA stack");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
/* ******************** Add Tracks Operator ***************************** */
/* Add NLA Tracks to the same AnimData block as a selected track, or above the selected tracks */
/* helper - add NLA Tracks alongside existing ones */
bool nlaedit_add_tracks_existing(bAnimContext *ac, bool above_sel)
{
ListBase anim_data = {NULL, NULL};
bAnimListElem *ale;
int filter;
AnimData *lastAdt = NULL;
bool added = false;
/* get a list of the (selected) NLA Tracks being shown in the NLA */
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL |
ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* add tracks... */
for (ale = anim_data.first; ale; ale = ale->next) {
if (ale->type == ANIMTYPE_NLATRACK) {
NlaTrack *nlt = (NlaTrack *)ale->data;
AnimData *adt = ale->adt;
const bool is_liboverride = ID_IS_OVERRIDE_LIBRARY(ale->id);
/* check if just adding a new track above this one,
* or whether we're adding a new one to the top of the stack that this one belongs to
*/
if (above_sel) {
/* just add a new one above this one */
BKE_nlatrack_add(adt, nlt, is_liboverride);
ale->update = ANIM_UPDATE_DEPS;
added = true;
}
else if ((lastAdt == NULL) || (adt != lastAdt)) {
/* add one track to the top of the owning AnimData's stack,
* then don't add anymore to this stack */
BKE_nlatrack_add(adt, NULL, is_liboverride);
lastAdt = adt;
ale->update = ANIM_UPDATE_DEPS;
added = true;
}
}
}
/* free temp data */
ANIM_animdata_update(ac, &anim_data);
ANIM_animdata_freelist(&anim_data);
return added;
}
/* helper - add NLA Tracks to empty (and selected) AnimData blocks */
bool nlaedit_add_tracks_empty(bAnimContext *ac)
{
ListBase anim_data = {NULL, NULL};
bAnimListElem *ale;
int filter;
bool added = false;
/* get a list of the selected AnimData blocks in the NLA */
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA |
ANIMFILTER_SEL | ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* check if selected AnimData blocks are empty, and add tracks if so... */
for (ale = anim_data.first; ale; ale = ale->next) {
AnimData *adt = ale->adt;
/* sanity check */
BLI_assert(adt->flag & ADT_UI_SELECTED);
/* ensure it is empty */
if (BLI_listbase_is_empty(&adt->nla_tracks)) {
/* add new track to this AnimData block then */
BKE_nlatrack_add(adt, NULL, ID_IS_OVERRIDE_LIBRARY(ale->id));
ale->update = ANIM_UPDATE_DEPS;
added = true;
}
}
/* cleanup */
ANIM_animdata_update(ac, &anim_data);
ANIM_animdata_freelist(&anim_data);
return added;
}
/* ----- */
static int nlaedit_add_tracks_exec(bContext *C, wmOperator *op)
{
bAnimContext ac;
bool above_sel = RNA_boolean_get(op->ptr, "above_selected");
bool op_done = false;
/* get editor data */
if (ANIM_animdata_get_context(C, &ac) == 0) {
return OPERATOR_CANCELLED;
}
/* perform adding in two passes - existing first so that we don't double up for empty */
op_done |= nlaedit_add_tracks_existing(&ac, above_sel);
op_done |= nlaedit_add_tracks_empty(&ac);
/* done? */
if (op_done) {
DEG_relations_tag_update(CTX_data_main(C));
/* set notifier that things have changed */
WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_ADDED, NULL);
/* done */
return OPERATOR_FINISHED;
}
/* failed to add any tracks */
BKE_report(
op->reports, RPT_WARNING, "Select an existing NLA Track or an empty action line first");
/* not done */
return OPERATOR_CANCELLED;
}
void NLA_OT_tracks_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Tracks";
ot->idname = "NLA_OT_tracks_add";
ot->description = "Add NLA-Tracks above/after the selected tracks";
/* api callbacks */
ot->exec = nlaedit_add_tracks_exec;
ot->poll = nlaop_poll_tweakmode_off;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
RNA_def_boolean(ot->srna,
"above_selected",
0,
"Above Selected",
"Add a new NLA Track above every existing selected one");
}
/* ******************** Delete Tracks Operator ***************************** */
/* Delete selected NLA Tracks */
static int nlaedit_delete_tracks_exec(bContext *C, wmOperator *UNUSED(op))
{
bAnimContext ac;
ListBase anim_data = {NULL, NULL};
bAnimListElem *ale;
int filter;
/* get editor data */
if (ANIM_animdata_get_context(C, &ac) == 0) {
return OPERATOR_CANCELLED;
}
/* get a list of the AnimData blocks being shown in the NLA */
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL |
ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* delete tracks */
for (ale = anim_data.first; ale; ale = ale->next) {
if (ale->type == ANIMTYPE_NLATRACK) {
NlaTrack *nlt = (NlaTrack *)ale->data;
AnimData *adt = ale->adt;
if (BKE_nlatrack_is_nonlocal_in_liboverride(ale->id, nlt)) {
/* No deletion of non-local tracks of override data. */
continue;
}
/* if track is currently 'solo', then AnimData should have its
* 'has solo' flag disabled
*/
if (nlt->flag & NLATRACK_SOLO) {
adt->flag &= ~ADT_NLA_SOLO_TRACK;
}
/* call delete on this track - deletes all strips too */
BKE_nlatrack_free(&adt->nla_tracks, nlt, true);
ale->update = ANIM_UPDATE_DEPS;
}
}
/* free temp data */
ANIM_animdata_update(&ac, &anim_data);
ANIM_animdata_freelist(&anim_data);
DEG_relations_tag_update(ac.bmain);
/* set notifier that things have changed */
WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_REMOVED, NULL);
/* done */
return OPERATOR_FINISHED;
}
void NLA_OT_tracks_delete(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Delete Tracks";
ot->idname = "NLA_OT_tracks_delete";
ot->description = "Delete selected NLA-Tracks and the strips they contain";
/* api callbacks */
ot->exec = nlaedit_delete_tracks_exec;
ot->poll = nlaop_poll_tweakmode_off;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* *********************************************** */
/* AnimData Related Operators */
/* ******************** Include Objects Operator ***************************** */
/* Include selected objects in NLA Editor, by giving them AnimData blocks
* NOTE: This doesn't help for non-object AnimData, where we do not have any effective
* selection mechanism in place. Unfortunately, this means that non-object AnimData
* once again becomes a second-class citizen here. However, at least for the most
* common use case, we now have a nice shortcut again.
*/
static int nlaedit_objects_add_exec(bContext *C, wmOperator *UNUSED(op))
{
bAnimContext ac;
SpaceNla *snla;
/* get editor data */
if (ANIM_animdata_get_context(C, &ac) == 0) {
return OPERATOR_CANCELLED;
}
/* ensure that filters are set so that the effect will be immediately visible */
snla = (SpaceNla *)ac.sl;
if (snla && snla->ads) {
snla->ads->filterflag &= ~ADS_FILTER_NLA_NOACT;
}
/* operate on selected objects... */
CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
/* ensure that object has AnimData... that's all */
BKE_animdata_add_id(&ob->id);
}
CTX_DATA_END;
/* set notifier that things have changed */
WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_EDITED, NULL);
/* done */
return OPERATOR_FINISHED;
}
void NLA_OT_selected_objects_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Include Selected Objects";
ot->idname = "NLA_OT_selected_objects_add";
ot->description = "Make selected objects appear in NLA Editor by adding Animation Data";
/* api callbacks */
ot->exec = nlaedit_objects_add_exec;
ot->poll = nlaop_poll_tweakmode_off;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* *********************************************** */