WIP: just adding in the Phab DIFF to local repo to easily see what's left to refactor #2

Closed
Nate Rupsis wants to merge 1 commits from NLA-refactor-diff into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
21 changed files with 9899 additions and 38 deletions

View File

@ -179,7 +179,7 @@ void BKE_nlastrips_clear_metastrip(ListBase *strips, struct NlaStrip *strip);
* Add the given NLA-Strip to the given Meta-Strip, assuming that the
* strip isn't attached to any list of strips
*/
bool BKE_nlameta_add_strip(struct NlaStrip *mstrip, struct NlaStrip *strip);
bool BKE_nlameta_try_add_strip(struct NlaStrip *mstrip, struct NlaStrip *strip);
/**
* Adjust the settings of NLA-Strips contained within a Meta-Strip (recursively),
* until the Meta-Strips children all fit within the Meta-Strip's new dimensions

View File

@ -0,0 +1,437 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2009 Blender Foundation, Joshua Leung. All rights reserved. */
#pragma once
/** \file
* \ingroup bke
*/
/** Temp constant defined for these functions only. */
#define NLASTRIP_MIN_LEN_THRESH 0.1f
#include "DNA_listBase.h"
#ifdef __cplusplus
extern "C" {
#endif
struct AnimData;
struct LibraryForeachIDData;
struct Main;
struct NlaStrip;
struct NlaTrack;
struct Scene;
struct Speaker;
struct bAction;
struct BlendDataReader;
struct BlendExpander;
struct BlendLibReader;
struct BlendWriter;
struct PointerRNA;
struct PropertyRNA;
/* ----------------------------- */
/* Data Management */
/**
* Frees the given NLA strip, and calls #BKE_nlastrip_remove_and_free to
* remove and free all children strips.
*/
void BKE_nlastrip_free(struct NlaStrip *strip, bool do_id_user);
/**
* Remove the given NLA track from the set of NLA tracks, free the track's data,
* and the track itself.
*/
void BKE_nlatrack_free(ListBase *tracks, struct NlaTrack *nlt, bool do_id_user);
/**
* Free the elements of type NLA Tracks provided in the given list, but do not free
* the list itself since that is not free-standing
*/
void BKE_nla_tracks_free(ListBase *tracks, bool do_id_user);
/**
* Copy NLA strip
*
* \param use_same_action: When true, the existing action is used (instead of being duplicated)
* \param flag: Control ID pointers management, see LIB_ID_CREATE_.../LIB_ID_COPY_...
* flags in BKE_lib_id.h
*/
struct NlaStrip *BKE_nlastrip_copy(struct Main *bmain,
struct NlaStrip *strip,
bool use_same_action,
int flag);
/**
* Copy a single NLA Track.
* \param flag: Control ID pointers management, see LIB_ID_CREATE_.../LIB_ID_COPY_...
* flags in BKE_lib_id.h
*/
struct NlaTrack *BKE_nlatrack_copy(struct Main *bmain,
struct NlaTrack *nlt,
bool use_same_actions,
int flag);
/**
* Copy all NLA data.
* \param flag: Control ID pointers management, see LIB_ID_CREATE_.../LIB_ID_COPY_...
* flags in BKE_lib_id.h
*/
void BKE_nla_tracks_copy(struct Main *bmain, ListBase *dst, const ListBase *src, int flag);
/**
* Copy NLA tracks from #adt_source to #adt_dest, and update the active track/strip pointers to
* point at those copies.
*/
void BKE_nla_tracks_copy_from_adt(struct Main *bmain,
struct AnimData *adt_dest,
const struct AnimData *adt_source,
int flag);
/**
* Add a NLA Track to the given AnimData.
* \param prev: NLA-Track to add the new one after.
*/
struct NlaTrack *BKE_nlatrack_add(struct AnimData *adt,
struct NlaTrack *prev,
bool is_liboverride);
/**
* Create a NLA Strip referencing the given Action.
*/
struct NlaStrip *BKE_nlastrip_new(struct bAction *act);
/*
* Removes the given NLA strip from the list of strips provided.
*/
void BKE_nlastrip_remove(ListBase *strips, struct NlaStrip *strip);
/*
* Removes the given NLA strip from the list of strips provided, and frees it's memory.
*/
void BKE_nlastrip_remove_and_free(ListBase *strips, struct NlaStrip *strip, const bool do_id_user);
/**
* Add new NLA-strip to the top of the NLA stack - i.e.
* into the last track if space, or a new one otherwise.
*/
struct NlaStrip *BKE_nlastack_add_strip(struct AnimData *adt,
struct bAction *act,
bool is_liboverride);
/**
* Add a NLA Strip referencing the given speaker's sound.
*/
struct NlaStrip *BKE_nla_add_soundstrip(struct Main *bmain,
struct Scene *scene,
struct Speaker *speaker);
/**
* Callback used by lib_query to walk over all ID usages
* (mimics `foreach_id` callback of #IDTypeInfo structure).
*/
void BKE_nla_strip_foreach_id(struct NlaStrip *strip, struct LibraryForeachIDData *data);
/* ----------------------------- */
/* API */
/**
* Check if there is any space in the given list to add the given strip.
*/
bool BKE_nlastrips_has_space(ListBase *strips, float start, float end);
/**
* Rearrange the strips in the track so that they are always in order
* (usually only needed after a strip has been moved)
*/
void BKE_nlastrips_sort_strips(ListBase *strips);
/**
* Add the given NLA-Strip to the given list of strips, assuming that it
* isn't currently a member of another list, NULL, or conflicting with existing
* strips position.
*/
void BKE_nlastrips_add_strip_unsafe(ListBase *strips, struct NlaStrip *strip);
/**
* NULL checks incoming strip and verifies no overlap / invalid
* configuration against other strips in NLA Track before calling
* #BKE_nlastrips_add_strip_unsafe.
*/
bool BKE_nlastrips_add_strip(ListBase *strips, struct NlaStrip *strip);
/**
* Convert 'islands' (i.e. continuous string of) selected strips to be
* contained within 'Meta-Strips' which act as strips which contain strips.
*
* \param is_temp: are the meta-strips to be created 'temporary' ones used for transforms?
*/
void BKE_nlastrips_make_metas(ListBase *strips, bool is_temp);
/**
* Remove meta-strips (i.e. flatten the list of strips) from the top-level of the list of strips.
*
* \param only_sel: only consider selected meta-strips, otherwise all meta-strips are removed
* \param only_temp: only remove the 'temporary' meta-strips used for transforms
*/
void BKE_nlastrips_clear_metas(ListBase *strips, bool only_sel, bool only_temp);
/**
* Split a meta-strip into a set of normal strips.
*/
void BKE_nlastrips_clear_metastrip(ListBase *strips, struct NlaStrip *strip);
/**
* Add the given NLA-Strip to the given Meta-Strip, assuming that the
* strip isn't attached to any list of strips
*/
bool BKE_nlameta_add_strip(struct NlaStrip *mstrip, struct NlaStrip *strip);
/**
* Adjust the settings of NLA-Strips contained within a Meta-Strip (recursively),
* until the Meta-Strips children all fit within the Meta-Strip's new dimensions
*/
void BKE_nlameta_flush_transforms(struct NlaStrip *mstrip);
/* ............ */
/**
* Find the active NLA-track for the given stack.
*/
struct NlaTrack *BKE_nlatrack_find_active(ListBase *tracks);
/**
* Make the given NLA-track the active one for the given stack. If no track is provided,
* this function can be used to simply deactivate all the NLA tracks in the given stack too.
*/
void BKE_nlatrack_set_active(ListBase *tracks, struct NlaTrack *nlt);
/**
* Get the NLA Track that the active action/action strip comes from,
* since this info is not stored in AnimData. It also isn't as simple
* as just using the active track, since multiple tracks may have been
* entered at the same time.
*/
struct NlaTrack *BKE_nlatrack_find_tweaked(struct AnimData *adt);
/**
* Toggle the 'solo' setting for the given NLA-track, making sure that it is the only one
* that has this status in its AnimData block.
*/
void BKE_nlatrack_solo_toggle(struct AnimData *adt, struct NlaTrack *nlt);
/**
* Check if there is any space in the given track to add a strip of the given length.
*/
bool BKE_nlatrack_has_space(struct NlaTrack *nlt, float start, float end);
/**
* Rearrange the strips in the track so that they are always in order
* (usually only needed after a strip has been moved).
*/
void BKE_nlatrack_sort_strips(struct NlaTrack *nlt);
/**
* Add the given NLA-Strip to the given NLA-Track.
* Calls #BKE_nlastrips_add_strip to check if strip can be added.
*/
bool BKE_nlatrack_add_strip(struct NlaTrack *nlt, struct NlaStrip *strip, bool is_liboverride);
/**
* Remove the NLA-Strip from the given NLA-Track.
*/
void BKE_nlatrack_remove_strip(struct NlaTrack *track, struct NlaStrip *strip);
/**
* Get the extents of the given NLA-Track including gaps between strips,
* returning whether this succeeded or not
*/
bool BKE_nlatrack_get_bounds(struct NlaTrack *nlt, float bounds[2]);
/**
* Check whether given NLA track is not local (i.e. from linked data) when the object is a library
* override.
*
* \param nlt: May be NULL, in which case we consider it as a non-local track case.
*/
bool BKE_nlatrack_is_nonlocal_in_liboverride(const struct ID *id, const struct NlaTrack *nlt);
/* ............ */
/**
* Compute the left-hand-side 'frame limit' of that strip, in its NLA track.
*
* \details This is either :
* - the end frame of the previous strip, if the strip's track contains another strip on it left
* - the macro MINFRAMEF, if no strips are to the left of this strip in its track
*
* \param strip: The strip to compute the left-hand-side 'frame limit' of.
* \return The beginning frame of the previous strip, or MINFRAMEF if no strips are next in that
* track.
*/
float BKE_nlastrip_compute_frame_from_previous_strip(struct NlaStrip *strip);
/**
* Compute the right-hand-side 'frame limit' of that strip, in its NLA track.
*
* \details This is either :
*
* - the begin frame of the next strip, if the strip's track contains another strip on it right
* - the macro MAXFRAMEF, if no strips are to the right of this strip in its track
*
* \param strip: The strip to compute the right-hand-side 'frame limit' of.
* \return The beginning frame of the next strip, or MAXFRAMEF if no strips are next in that track.
*/
float BKE_nlastrip_compute_frame_to_next_strip(struct NlaStrip *strip);
/**
* Returns the next strip in this strip's NLA track, or a null pointer.
*
* \param strip: The strip to find the next trip from.
* \param check_transitions: Whether or not to skip transitions.
* \return The next strip in the track, or NULL if none are present.
*/
struct NlaStrip *BKE_nlastrip_next_in_track(struct NlaStrip *strip, bool skip_transitions);
/**
* Returns the previous strip in this strip's NLA track, or a null pointer.
*
* \param strip: The strip to find the previous trip from.
* \param check_transitions: Whether or not to skip transitions.
* \return The previous strip in the track, or NULL if none are present.
*/
struct NlaStrip *BKE_nlastrip_prev_in_track(struct NlaStrip *strip, bool skip_transitions);
/* ............ */
/**
* Find the active NLA-strip within the given track.
*/
struct NlaStrip *BKE_nlastrip_find_active(struct NlaTrack *nlt);
/**
* Make the given NLA-Strip the active one within the given block.
*/
void BKE_nlastrip_set_active(struct AnimData *adt, struct NlaStrip *strip);
/**
* Does the given NLA-strip fall within the given bounds (times)?.
*/
bool BKE_nlastrip_within_bounds(struct NlaStrip *strip, float min, float max);
/**
* Return the distance from the given frame to the NLA strip, measured in frames.
* If the given frame intersects the NLA strip, the distance is zero.
*/
float BKE_nlastrip_distance_to_frame(const struct NlaStrip *strip, float timeline_frame);
/**
* Recalculate the start and end frames for the current strip, after changing
* the extents of the action or the mapping (repeats or scale factor) info.
*/
void BKE_nlastrip_recalculate_bounds(struct NlaStrip *strip);
/**
* Recalculate the start and end frames for the strip to match the bounds of its action such that
* the overall NLA animation result is unchanged.
*/
void BKE_nlastrip_recalculate_bounds_sync_action(struct NlaStrip *strip);
/**
* Recalculate the blend-in and blend-out values after a strip transform update.
*/
void BKE_nlastrip_recalculate_blend(struct NlaStrip *strip);
/**
* Find (and set) a unique name for a strip from the whole AnimData block
* Uses a similar method to the BLI method, but is implemented differently
* as we need to ensure that the name is unique over several lists of tracks,
* not just a single track.
*/
void BKE_nlastrip_validate_name(struct AnimData *adt, struct NlaStrip *strip);
/* ............ */
/**
* Check if the given NLA-Track has any strips with own F-Curves.
*/
bool BKE_nlatrack_has_animated_strips(struct NlaTrack *nlt);
/**
* Check if given NLA-Tracks have any strips with own F-Curves.
*/
bool BKE_nlatracks_have_animated_strips(ListBase *tracks);
/**
* Validate the NLA-Strips 'control' F-Curves based on the flags set.
*/
void BKE_nlastrip_validate_fcurves(struct NlaStrip *strip);
/**
* Check if the given RNA pointer + property combo should be handled by
* NLA strip curves or not.
*/
bool BKE_nlastrip_has_curves_for_property(const struct PointerRNA *ptr,
const struct PropertyRNA *prop);
/**
* Ensure that auto-blending and other settings are set correctly.
*/
void BKE_nla_validate_state(struct AnimData *adt);
/* ............ */
/**
* Check if an action is "stashed" in the NLA already
*
* The criteria for this are:
* 1) The action in question lives in a "stash" track.
* 2) We only check first-level strips. That is, we will not check inside meta strips.
*/
bool BKE_nla_action_is_stashed(struct AnimData *adt, struct bAction *act);
/**
* "Stash" an action (i.e. store it as a track/layer in the NLA, but non-contributing)
* to retain it in the file for future uses.
*/
bool BKE_nla_action_stash(struct AnimData *adt, bool is_liboverride);
/* ............ */
/**
* For the given AnimData block, add the active action to the NLA
* stack (i.e. 'push-down' action). The UI should only allow this
* for normal editing only (i.e. not in edit-mode for some strip's action),
* so no checks for this are performed.
*
* TODO: maybe we should have checks for this too.
*/
void BKE_nla_action_pushdown(struct AnimData *adt, bool is_liboverride);
/**
* Find the active strip + track combination, and set them up as the tweaking track,
* and return if successful or not.
*/
bool BKE_nla_tweakmode_enter(struct AnimData *adt);
/**
* Exit tweak-mode for this AnimData block.
*/
void BKE_nla_tweakmode_exit(struct AnimData *adt);
/* ----------------------------- */
/* Time Mapping */
/* time mapping conversion modes */
enum eNlaTime_ConvertModes {
/* convert from global time to strip time - for evaluation */
NLATIME_CONVERT_EVAL = 0,
/* convert from global time to strip time - for editing corrections */
/* XXX: old 0 invert. */
NLATIME_CONVERT_UNMAP,
/* convert from strip time to global time */
/* XXX: old 1 invert. */
NLATIME_CONVERT_MAP,
};
/**
* Non clipped mapping for strip-time <-> global time:
* `mode = eNlaTime_ConvertModes -> NLATIME_CONVERT_*`
*
* Public API method - perform this mapping using the given AnimData block
* and perform any necessary sanity checks on the value
*/
float BKE_nla_tweakedit_remap(struct AnimData *adt, float cframe, short mode);
/* ----------------------------- */
/* .blend file API */
void BKE_nla_blend_write(struct BlendWriter *writer, struct ListBase *tracks);
void BKE_nla_blend_read_data(struct BlendDataReader *reader, struct ListBase *tracks);
void BKE_nla_blend_read_lib(struct BlendLibReader *reader, struct ID *id, struct ListBase *tracks);
void BKE_nla_blend_read_expand(struct BlendExpander *expander, struct ListBase *tracks);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,145 @@
***************
*** 30,44 ****
/* ----------------------------- */
/* Data Management */
- /**
- * Remove the given NLA strip from the NLA track it occupies, free the strip's data,
- * and the strip itself.
- */
void BKE_nlastrip_free(ListBase *strips, struct NlaStrip *strip, bool do_id_user);
- /**
- * Remove the given NLA track from the set of NLA tracks, free the track's data,
- * and the track itself.
- */
void BKE_nlatrack_free(ListBase *tracks, struct NlaTrack *nlt, bool do_id_user);
/**
* Free the elements of type NLA Tracks provided in the given list, but do not free
--- 30,38 ----
/* ----------------------------- */
/* Data Management */
+ /* Free the strip's data and the strip itself. */
void BKE_nlastrip_free(ListBase *strips, struct NlaStrip *strip, bool do_id_user);
+ /* Free the track's data and the track itself. */
void BKE_nlatrack_free(ListBase *tracks, struct NlaTrack *nlt, bool do_id_user);
/**
* Free the elements of type NLA Tracks provided in the given list, but do not free
***************
*** 82,98 ****
const struct AnimData *adt_source,
int flag);
/**
* Add a NLA Track to the given AnimData.
* \param prev: NLA-Track to add the new one after.
*/
- struct NlaTrack *BKE_nlatrack_add(struct AnimData *adt,
- struct NlaTrack *prev,
- bool is_liboverride);
/**
* Create a NLA Strip referencing the given Action.
*/
struct NlaStrip *BKE_nlastrip_new(struct bAction *act);
/**
* Add new NLA-strip to the top of the NLA stack - i.e.
* into the last track if space, or a new one otherwise.
--- 76,121 ----
const struct AnimData *adt_source,
int flag);
+
+ struct NlaTrack *BKE_nlatrack_new();
+ void BKE_nlatrack_remove(ListBase *tracks, struct NlaTrack *nlt);
+ void BKE_nlatrack_remove_and_free(ListBase *tracks, struct NlaTrack *nlt, bool do_id_user);
+
+ void BKE_nlatrack_insert_after(ListBase *nla_tracks,
+ struct NlaTrack *prev,
+ struct NlaTrack *new_track,
+ bool is_liboverride);
+
+ void BKE_nlatrack_insert_before(ListBase *nla_tracks,
+ struct NlaTrack *next,
+ struct NlaTrack *new_track,
+ bool is_liboverride);
/**
* Add a NLA Track to the given AnimData.
* \param prev: NLA-Track to add the new one after.
*/
+ struct NlaTrack *BKE_nlatrack_new_after_and_set_active(ListBase *nla_tracks,
+ struct NlaTrack *prev,
+ bool is_liboverride);
+
+ struct NlaTrack *BKE_nlatrack_new_before_and_set_active(ListBase *nla_tracks,
+ struct NlaTrack *next,
+ bool is_liboverride);
+
+ struct NlaTrack *BKE_nlatrack_new_tail_and_set_active(ListBase *nla_tracks,
+ const bool is_liboverride);
+ struct NlaTrack *BKE_nlatrack_new_head_and_set_active(ListBase *nla_tracks,
+ bool is_liboverride);
+
/**
* Create a NLA Strip referencing the given Action.
*/
struct NlaStrip *BKE_nlastrip_new(struct bAction *act);
+
+ void BKE_nlatrack_remove_strip(struct NlaTrack *track, struct NlaStrip *strip);
+ void BKE_nlastrip_remove(ListBase *strips, struct NlaStrip *strip);
+ void BKE_nlastrip_remove_and_free(ListBase *strips, struct NlaStrip *strip, const bool do_id_user);
+
/**
* Add new NLA-strip to the top of the NLA stack - i.e.
* into the last track if space, or a new one otherwise.
***************
*** 126,136 ****
*/
void BKE_nlastrips_sort_strips(ListBase *strips);
/**
* Add the given NLA-Strip to the given list of strips, assuming that it
* isn't currently a member of another list
*/
- bool BKE_nlastrips_add_strip(ListBase *strips, struct NlaStrip *strip);
/**
* Convert 'islands' (i.e. continuous string of) selected strips to be
--- 149,161 ----
*/
void BKE_nlastrips_sort_strips(ListBase *strips);
+ bool BKE_nlastrips_add_strip(ListBase *strips, struct NlaStrip *strip);
/**
* Add the given NLA-Strip to the given list of strips, assuming that it
* isn't currently a member of another list
*/
+ void BKE_nlastrips_add_strip(ListBase *strips, struct NlaStrip *strip);
+ bool BKE_nlastrips_try_add_strip(ListBase *strips, struct NlaStrip *strip);
/**
* Convert 'islands' (i.e. continuous string of) selected strips to be
***************
*** 222,232 ****
*/
void BKE_nlatrack_sort_strips(struct NlaTrack *nlt);
- /**
- * Add the given NLA-Strip to the given NLA-Track, assuming that it
- * isn't currently attached to another one.
*/
- bool BKE_nlatrack_add_strip(struct NlaTrack *nlt, struct NlaStrip *strip, bool is_liboverride);
/**
* Get the extents of the given NLA-Track including gaps between strips,
--- 247,257 ----
*/
void BKE_nlatrack_sort_strips(struct NlaTrack *nlt);
+ void BKE_nlatrack_add_strip(struct NlaTrack *nlt, struct NlaStrip *strip);
+ /** Compared to non-try version, this function does checks (NULL, track flags, whether track has
+ * space for strip, etc).
*/
+ bool BKE_nlatrack_try_add_strip(struct NlaTrack *nlt, struct NlaStrip *strip, bool is_liboverride);
/**
* Get the extents of the given NLA-Track including gaps between strips,

View File

@ -2001,12 +2001,12 @@ static void nlastrips_to_animdata(ID *id, ListBase *strips)
/* Try to add this strip to the current NLA-Track
* (i.e. the 'last' one on the stack at the moment). */
if (BKE_nlatrack_add_strip(nlt, strip, false) == 0) {
if (!BKE_nlatrack_try_add_strip(nlt, strip, false)) {
/* trying to add to the current failed (no space),
* so add a new track to the stack, and add to that...
*/
nlt = BKE_nlatrack_add(adt, NULL, false);
BKE_nlatrack_add_strip(nlt, strip, false);
nlt = BKE_nlatrack_new_tail_and_set_active(&adt->nla_tracks, false);
BKE_nlatrack_add_strip(nlt, strip);
}
/* ensure that strip has a name */

View File

@ -61,7 +61,7 @@ static void nla_tweakmode_find_active(const ListBase /* NlaTrack */ *nla_tracks,
/* Freeing ------------------------------------------- */
void BKE_nlastrip_free(NlaStrip *strip, const bool do_id_user)
void BKE_nlastrip_free(ListBase *strips, NlaStrip *strip, bool do_id_user)
{
NlaStrip *cs, *csn;
@ -73,7 +73,7 @@ void BKE_nlastrip_free(NlaStrip *strip, const bool do_id_user)
/* free child-strips */
for (cs = strip->strips.first; cs; cs = csn) {
csn = cs->next;
BKE_nlastrip_remove_and_free(&strip->strips, cs, do_id_user);
BKE_nlastrip_free(&strip->strips, cs, do_id_user);
}
/* remove reference to action */
@ -866,7 +866,7 @@ void BKE_nlastrips_clear_metastrip(ListBase *strips, NlaStrip *strip)
}
/* free the meta-strip now */
BKE_nlastrip_remove_and_free(strips, strip, true);
BKE_nlastrip_free(strips, strip, true);
}
void BKE_nlastrips_clear_metas(ListBase *strips, bool only_sel, bool only_temp)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,478 @@
***************
*** 91,106 ****
/* free own F-Modifiers */
free_fmodifiers(&strip->modifiers);
- MEM_freeN(strip);
- }
-
- void BKE_nlatrack_remove_strip(NlaTrack *track, NlaStrip *strip)
- {
- BLI_assert(track);
- BKE_nlastrip_remove(&track->strips, strip);
}
- void BKE_nlatrack_free(NlaTrack *nlt, const bool do_id_user)
{
NlaStrip *strip, *stripn;
--- 91,106 ----
/* free own F-Modifiers */
free_fmodifiers(&strip->modifiers);
+ /* free the strip itself */
+ if (strips) {
+ BLI_freelinkN(strips, strip);
+ }
+ else {
+ MEM_freeN(strip);
+ }
}
+ void BKE_nlatrack_free(ListBase *tracks, NlaTrack *nlt, bool do_id_user)
{
NlaStrip *strip, *stripn;
***************
*** 112,152 ****
/* free strips */
for (strip = nlt->strips.first; strip; strip = stripn) {
stripn = strip->next;
- BKE_nlastrip_remove_and_free(&nlt->strips, strip, do_id_user);
}
/* free NLA track itself now */
- MEM_freeN(nlt);
- }
-
- void BKE_nlastrip_remove(ListBase *strips, NlaStrip *strip)
- {
- BLI_assert(strips);
- BLI_remlink(strips, strip);
- }
-
- void BKE_nlastrip_remove_and_free(ListBase *strips, NlaStrip *strip, const bool do_id_user)
- {
- BKE_nlastrip_remove(strips, strip);
- BKE_nlastrip_free(strip, do_id_user);
- }
-
- void BKE_nlatrack_remove(ListBase *tracks, NlaTrack *nlt)
- {
- BLI_assert(tracks);
- BLI_remlink(tracks, nlt);
- }
-
- /* Remove the given NLA track from the set of NLA tracks, free the track's data,
- * and the track itself.
- */
- void BKE_nlatrack_remove_and_free(ListBase *tracks, NlaTrack *nlt, const bool do_id_user)
- {
- BKE_nlatrack_remove(tracks, nlt);
- BKE_nlatrack_free(nlt, do_id_user);
}
- void BKE_nla_tracks_free(ListBase *tracks, const bool do_id_user)
{
NlaTrack *nlt, *nltn;
--- 112,130 ----
/* free strips */
for (strip = nlt->strips.first; strip; strip = stripn) {
stripn = strip->next;
+ BKE_nlastrip_free(&nlt->strips, strip, do_id_user);
}
/* free NLA track itself now */
+ if (tracks) {
+ BLI_freelinkN(tracks, nlt);
+ }
+ else {
+ MEM_freeN(nlt);
+ }
}
+ void BKE_nla_tracks_free(ListBase *tracks, bool do_id_user)
{
NlaTrack *nlt, *nltn;
***************
*** 158,164 ****
/* free tracks one by one */
for (nlt = tracks->first; nlt; nlt = nltn) {
nltn = nlt->next;
- BKE_nlatrack_remove_and_free(tracks, nlt, do_id_user);
}
/* clear the list's pointers to be safe */
--- 136,142 ----
/* free tracks one by one */
for (nlt = tracks->first; nlt; nlt = nltn) {
nltn = nlt->next;
+ BKE_nlatrack_free(tracks, nlt, do_id_user);
}
/* clear the list's pointers to be safe */
***************
*** 324,355 ****
/* Adding ------------------------------------------- */
- NlaTrack *BKE_nlatrack_new()
{
/* allocate new track */
- NlaTrack *nlt = MEM_callocN(sizeof(NlaTrack), "NlaTrack");
/* set settings requiring the track to not be part of the stack yet */
nlt->flag = NLATRACK_SELECTED | NLATRACK_OVERRIDELIBRARY_LOCAL;
-
- return nlt;
- }
-
- void BKE_nlatrack_insert_after(ListBase *nla_tracks,
- NlaTrack *prev,
- NlaTrack *new_track,
- const bool is_liboverride)
- {
- BLI_assert(!ELEM(NULL, nla_tracks, new_track));
-
- /** If NULL, then caller intends to insert a new head. But, tracks are not allowed to be placed
- * before library overrides. So it must inserted after the last override. */
- if (prev == NULL) {
- NlaTrack *first_track = (NlaTrack *)nla_tracks->first;
- if (first_track != NULL && (first_track->flag & NLATRACK_OVERRIDELIBRARY_LOCAL) == 0) {
- prev = first_track;
- }
- }
/* In liboverride case, we only add local tracks after all those coming from the linked data,
* so we need to find the first local track. */
--- 302,322 ----
/* Adding ------------------------------------------- */
+ NlaTrack *BKE_nlatrack_add(AnimData *adt, NlaTrack *prev, const bool is_liboverride)
{
+ NlaTrack *nlt;
+
+ /* sanity checks */
+ if (adt == NULL) {
+ return NULL;
+ }
+
/* allocate new track */
+ nlt = MEM_callocN(sizeof(NlaTrack), "NlaTrack");
/* set settings requiring the track to not be part of the stack yet */
nlt->flag = NLATRACK_SELECTED | NLATRACK_OVERRIDELIBRARY_LOCAL;
+ nlt->index = BLI_listbase_count(&adt->nla_tracks);
/* In liboverride case, we only add local tracks after all those coming from the linked data,
* so we need to find the first local track. */
***************
*** 361,441 ****
prev = first_local != NULL ? first_local->prev : NULL;
}
/* Add track to stack, and make it the active one. */
- BLI_insertlinkafter(nla_tracks, prev, new_track);
- new_track->index = BLI_findindex(nla_tracks, new_track);
/* must have unique name, but we need to seed this */
- strcpy(new_track->name, "NlaTrack");
- BLI_uniquename(nla_tracks,
- new_track,
- DATA_("NlaTrack"),
- '.',
- offsetof(NlaTrack, name),
- sizeof(new_track->name));
- }
-
- void BKE_nlatrack_insert_before(ListBase *nla_tracks,
- NlaTrack *next,
- NlaTrack *new_track,
- const bool is_liboverride)
- {
- if (is_liboverride) {
-
- /** Currently, all library override tracks are assumed to be grouped together at the start of
- * the list. So we can only add the new track after the last library track. */
- if (next != NULL && (next->flag & NLATRACK_OVERRIDELIBRARY_LOCAL) == 0) {
- BKE_nlatrack_insert_after(nla_tracks, next, new_track, is_liboverride);
- return;
- }
- }
- BLI_insertlinkbefore(nla_tracks, next, new_track);
- new_track->index = BLI_findindex(nla_tracks, new_track);
-
- /* Must have unique name, but we need to seed this. */
- strcpy(new_track->name, "NlaTrack");
- BLI_uniquename(nla_tracks,
- new_track,
- DATA_("NlaTrack"),
- '.',
- offsetof(NlaTrack, name),
- sizeof(new_track->name));
- }
-
- NlaTrack *BKE_nlatrack_new_after_and_set_active(ListBase *nla_tracks,
- NlaTrack *prev,
- const bool is_liboverride)
- {
- NlaTrack *new_track = BKE_nlatrack_new();
-
- BKE_nlatrack_insert_after(nla_tracks, prev, new_track, is_liboverride);
- BKE_nlatrack_set_active(nla_tracks, new_track);
-
- return new_track;
- }
-
- NlaTrack *BKE_nlatrack_new_before_and_set_active(ListBase *nla_tracks,
- NlaTrack *next,
- const bool is_liboverride)
- {
- NlaTrack *new_track = BKE_nlatrack_new();
-
- BKE_nlatrack_insert_before(nla_tracks, next, new_track, is_liboverride);
- BKE_nlatrack_set_active(nla_tracks, new_track);
-
- return new_track;
- }
-
- NlaTrack *BKE_nlatrack_new_tail_and_set_active(ListBase *nla_tracks, const bool is_liboverride)
- {
- return BKE_nlatrack_new_after_and_set_active(
- nla_tracks, (NlaTrack *)nla_tracks->last, is_liboverride);
- }
-
- NlaTrack *BKE_nlatrack_new_head_and_set_active(ListBase *nla_tracks, const bool is_liboverride)
- {
- return BKE_nlatrack_new_before_and_set_active(
- nla_tracks, (NlaTrack *)nla_tracks->first, is_liboverride);
}
NlaStrip *BKE_nlastrip_new(bAction *act)
--- 328,348 ----
prev = first_local != NULL ? first_local->prev : NULL;
}
/* Add track to stack, and make it the active one. */
+ if (prev != NULL) {
+ BLI_insertlinkafter(&adt->nla_tracks, prev, nlt);
+ }
+ else {
+ BLI_addtail(&adt->nla_tracks, nlt);
+ }
+ BKE_nlatrack_set_active(&adt->nla_tracks, nlt);
/* must have unique name, but we need to seed this */
+ strcpy(nlt->name, "NlaTrack");
+ BLI_uniquename(
+ &adt->nla_tracks, nlt, DATA_("NlaTrack"), '.', offsetof(NlaTrack, name), sizeof(nlt->name));
+ /* return the new track */
+ return nlt;
}
NlaStrip *BKE_nlastrip_new(bAction *act)
***************
*** 504,515 ****
}
/* firstly try adding strip to last track, but if that fails, add to a new track */
- if (!BKE_nlatrack_try_add_strip(adt->nla_tracks.last, strip, is_liboverride)) {
/* trying to add to the last track failed (no track or no space),
* so add a new track to the stack, and add to that...
*/
- nlt = BKE_nlatrack_new_tail_and_set_active(&adt->nla_tracks, is_liboverride);
- BKE_nlatrack_add_strip(nlt, strip);
}
/* automatically name it too */
--- 411,422 ----
}
/* firstly try adding strip to last track, but if that fails, add to a new track */
+ if (BKE_nlatrack_add_strip(adt->nla_tracks.last, strip, is_liboverride) == 0) {
/* trying to add to the last track failed (no track or no space),
* so add a new track to the stack, and add to that...
*/
+ nlt = BKE_nlatrack_add(adt, NULL, is_liboverride);
+ BKE_nlatrack_add_strip(nlt, strip, is_liboverride);
}
/* automatically name it too */
***************
*** 809,821 ****
strips->last = tmp.last;
}
- void BKE_nlastrips_add_strip(ListBase *strips, NlaStrip *strip)
{
NlaStrip *ns;
bool not_added = true;
/* sanity checks */
- BLI_assert(!ELEM(NULL, strips, strip));
/* find the right place to add the strip to the nominated track */
for (ns = strips->first; ns; ns = ns->next) {
--- 716,735 ----
strips->last = tmp.last;
}
+ bool BKE_nlastrips_add_strip(ListBase *strips, NlaStrip *strip)
{
NlaStrip *ns;
bool not_added = true;
/* sanity checks */
+ if (ELEM(NULL, strips, strip)) {
+ return false;
+ }
+
+ /* check if any space to add */
+ if (BKE_nlastrips_has_space(strips, strip->start, strip->end) == 0) {
+ return false;
+ }
/* find the right place to add the strip to the nominated track */
for (ns = strips->first; ns; ns = ns->next) {
***************
*** 830,849 ****
/* just add to the end of the list of the strips then... */
BLI_addtail(strips, strip);
}
- }
-
- /** This version does additional checks (NULL check and space check). */
- bool BKE_nlastrips_try_add_strip(ListBase *strips, NlaStrip *strip)
- {
- if (ELEM(NULL, strips, strip)) {
- return false;
- }
-
- if (!BKE_nlastrips_has_space(strips, strip->start, strip->end)) {
- return false;
- }
- BKE_nlastrips_add_strip(strips, strip);
return true;
}
--- 744,751 ----
/* just add to the end of the list of the strips then... */
BLI_addtail(strips, strip);
}
+ /* added... */
return true;
}
***************
*** 894,900 ****
}
}
- bool BKE_nlameta_try_add_strip(NlaStrip *mstrip, NlaStrip *strip)
{
/* sanity checks */
if (ELEM(NULL, mstrip, strip)) {
--- 796,802 ----
}
}
+ bool BKE_nlameta_add_strip(NlaStrip *mstrip, NlaStrip *strip)
{
/* sanity checks */
if (ELEM(NULL, mstrip, strip)) {
***************
*** 939,945 ****
}
/* just try to add to the meta-strip (no dimension changes needed) */
- return BKE_nlastrips_try_add_strip(&mstrip->strips, strip);
}
void BKE_nlameta_flush_transforms(NlaStrip *mstrip)
--- 841,847 ----
}
/* just try to add to the meta-strip (no dimension changes needed) */
+ return BKE_nlastrips_add_strip(&mstrip->strips, strip);
}
void BKE_nlameta_flush_transforms(NlaStrip *mstrip)
***************
*** 1160,1166 ****
BKE_nlastrips_sort_strips(&nlt->strips);
}
- bool BKE_nlatrack_try_add_strip(NlaTrack *nlt, NlaStrip *strip, const bool is_liboverride)
{
/* sanity checks */
if (ELEM(NULL, nlt, strip)) {
--- 1062,1068 ----
BKE_nlastrips_sort_strips(&nlt->strips);
}
+ bool BKE_nlatrack_add_strip(NlaTrack *nlt, NlaStrip *strip, const bool is_liboverride)
{
/* sanity checks */
if (ELEM(NULL, nlt, strip)) {
***************
*** 1174,1186 ****
}
/* try to add the strip to the track using a more generic function */
- return BKE_nlastrips_try_add_strip(&nlt->strips, strip);
- }
-
- void BKE_nlatrack_add_strip(NlaTrack *nlt, NlaStrip *strip)
- {
- BLI_assert(!ELEM(NULL, nlt, strip));
- BKE_nlastrips_add_strip(&nlt->strips, strip);
}
bool BKE_nlatrack_get_bounds(NlaTrack *nlt, float bounds[2])
--- 1076,1082 ----
}
/* try to add the strip to the track using a more generic function */
+ return BKE_nlastrips_add_strip(&nlt->strips, strip);
}
bool BKE_nlatrack_get_bounds(NlaTrack *nlt, float bounds[2])
***************
*** 1884,1890 ****
}
}
- nlt = BKE_nlatrack_new_after_and_set_active(&adt->nla_tracks, prev_track, is_liboverride);
BLI_assert(nlt != NULL);
/* We need to ensure that if there wasn't any previous instance,
--- 1780,1786 ----
}
}
+ nlt = BKE_nlatrack_add(adt, prev_track, is_liboverride);
BLI_assert(nlt != NULL);
/* We need to ensure that if there wasn't any previous instance,
***************
*** 1904,1910 ****
strip = BKE_nlastrip_new(adt->action);
BLI_assert(strip != NULL);
- BKE_nlatrack_add_strip(nlt, strip);
BKE_nlastrip_validate_name(adt, strip);
/* mark the stash track and strip so that they doesn't disturb the stack animation,
--- 1800,1806 ----
strip = BKE_nlastrip_new(adt->action);
BLI_assert(strip != NULL);
+ BKE_nlatrack_add_strip(nlt, strip, is_liboverride);
BKE_nlastrip_validate_name(adt, strip);
/* mark the stash track and strip so that they doesn't disturb the stack animation,

View File

@ -0,0 +1,988 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2015 Blender Foundation. */
/** \file
* \ingroup spaction
*/
#include <float.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "BLI_utildefines.h"
#include "BLT_translation.h"
#include "DNA_anim_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_key_types.h"
#include "DNA_mask_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"
#include "RNA_prototypes.h"
#include "BKE_action.h"
#include "BKE_context.h"
#include "BKE_fcurve.h"
#include "BKE_key.h"
#include "BKE_lib_id.h"
#include "BKE_nla.h"
#include "BKE_report.h"
#include "BKE_scene.h"
#include "UI_view2d.h"
#include "ED_anim_api.h"
#include "ED_gpencil.h"
#include "ED_keyframes_edit.h"
#include "ED_keyframing.h"
#include "ED_markers.h"
#include "ED_mask.h"
#include "ED_screen.h"
#include "DEG_depsgraph.h"
#include "WM_api.h"
#include "WM_types.h"
#include "UI_interface.h"
#include "action_intern.h"
/* ************************************************************************** */
/* ACTION CREATION */
AnimData *ED_actedit_animdata_from_context(const bContext *C, ID **r_adt_id_owner)
{
SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C);
Object *ob = CTX_data_active_object(C);
AnimData *adt = NULL;
/* Get AnimData block to use */
if (saction->mode == SACTCONT_ACTION) {
/* Currently, "Action Editor" means object-level only... */
if (ob) {
adt = ob->adt;
if (r_adt_id_owner) {
*r_adt_id_owner = &ob->id;
}
}
}
else if (saction->mode == SACTCONT_SHAPEKEY) {
Key *key = BKE_key_from_object(ob);
if (key) {
adt = key->adt;
if (r_adt_id_owner) {
*r_adt_id_owner = &key->id;
}
}
}
return adt;
}
/* -------------------------------------------------------------------- */
/* Create new action */
static bAction *action_create_new(bContext *C, bAction *oldact)
{
ScrArea *area = CTX_wm_area(C);
bAction *action;
/* create action - the way to do this depends on whether we've got an
* existing one there already, in which case we make a copy of it
* (which is useful for "versioning" actions within the same file)
*/
if (oldact && GS(oldact->id.name) == ID_AC) {
/* make a copy of the existing action */
action = (bAction *)BKE_id_copy(CTX_data_main(C), &oldact->id);
}
else {
/* just make a new (empty) action */
action = BKE_action_add(CTX_data_main(C), "Action");
}
/* when creating new ID blocks, there is already 1 user (as for all new datablocks),
* but the RNA pointer code will assign all the proper users instead, so we compensate
* for that here
*/
BLI_assert(action->id.us == 1);
id_us_min(&action->id);
/* set ID-Root type */
if (area->spacetype == SPACE_ACTION) {
SpaceAction *saction = (SpaceAction *)area->spacedata.first;
if (saction->mode == SACTCONT_SHAPEKEY) {
action->idroot = ID_KE;
}
else {
action->idroot = ID_OB;
}
}
return action;
}
/* Change the active action used by the action editor */
static void actedit_change_action(bContext *C, bAction *act)
{
bScreen *screen = CTX_wm_screen(C);
SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C);
PointerRNA ptr, idptr;
PropertyRNA *prop;
/* create RNA pointers and get the property */
RNA_pointer_create(&screen->id, &RNA_SpaceDopeSheetEditor, saction, &ptr);
prop = RNA_struct_find_property(&ptr, "action");
/* NOTE: act may be NULL here, so better to just use a cast here */
RNA_id_pointer_create((ID *)act, &idptr);
/* set the new pointer, and force a refresh */
RNA_property_pointer_set(&ptr, prop, idptr, NULL);
RNA_property_update(C, &ptr, prop);
}
/* ******************** New Action Operator *********************** */
/* Criteria:
* 1) There must be an dopesheet/action editor, and it must be in a mode which uses actions...
* OR
* The NLA Editor is active (i.e. Animation Data panel -> new action)
* 2) The associated AnimData block must not be in tweak-mode.
*/
static bool action_new_poll(bContext *C)
{
Scene *scene = CTX_data_scene(C);
/* Check tweak-mode is off (as you don't want to be tampering with the action in that case) */
/* NOTE: unlike for pushdown,
* this operator needs to be run when creating an action from nothing... */
if (ED_operator_action_active(C)) {
SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C);
Object *ob = CTX_data_active_object(C);
/* For now, actions are only for the active object, and on object and shape-key levels... */
if (saction->mode == SACTCONT_ACTION) {
/* XXX: This assumes that actions are assigned to the active object in this mode */
if (ob) {
if ((ob->adt == NULL) || (ob->adt->flag & ADT_NLA_EDIT_ON) == 0) {
return true;
}
}
}
else if (saction->mode == SACTCONT_SHAPEKEY) {
Key *key = BKE_key_from_object(ob);
if (key) {
if ((key->adt == NULL) || (key->adt->flag & ADT_NLA_EDIT_ON) == 0) {
return true;
}
}
}
}
else if (ED_operator_nla_active(C)) {
if (!(scene->flag & SCE_NLA_EDIT_ON)) {
return true;
}
}
/* something failed... */
return false;
}
static int action_new_exec(bContext *C, wmOperator *UNUSED(op))
{
PointerRNA ptr, idptr;
PropertyRNA *prop;
bAction *oldact = NULL;
AnimData *adt = NULL;
ID *adt_id_owner = NULL;
/* hook into UI */
UI_context_active_but_prop_get_templateID(C, &ptr, &prop);
if (prop) {
/* The operator was called from a button. */
PointerRNA oldptr;
oldptr = RNA_property_pointer_get(&ptr, prop);
oldact = (bAction *)oldptr.owner_id;
/* stash the old action to prevent it from being lost */
if (ptr.type == &RNA_AnimData) {
adt = ptr.data;
adt_id_owner = ptr.owner_id;
}
else if (ptr.type == &RNA_SpaceDopeSheetEditor) {
adt = ED_actedit_animdata_from_context(C, &adt_id_owner);
}
}
else {
adt = ED_actedit_animdata_from_context(C, &adt_id_owner);
oldact = adt->action;
}
{
bAction *action = NULL;
/* Perform stashing operation - But only if there is an action */
if (adt && oldact) {
BLI_assert(adt_id_owner != NULL);
/* stash the action */
if (BKE_nla_action_stash(adt, ID_IS_OVERRIDE_LIBRARY(adt_id_owner))) {
/* The stash operation will remove the user already
* (and unlink the action from the AnimData action slot).
* Hence, we must unset the ref to the action in the
* action editor too (if this is where we're being called from)
* first before setting the new action once it is created,
* or else the user gets decremented twice!
*/
if (ptr.type == &RNA_SpaceDopeSheetEditor) {
SpaceAction *saction = ptr.data;
saction->action = NULL;
}
}
else {
#if 0
printf("WARNING: Failed to stash %s. It may already exist in the NLA stack though\n",
oldact->id.name);
#endif
}
}
/* create action */
action = action_create_new(C, oldact);
if (prop) {
/* set this new action
* NOTE: we can't use actedit_change_action, as this function is also called from the NLA
*/
RNA_id_pointer_create(&action->id, &idptr);
RNA_property_pointer_set(&ptr, prop, idptr, NULL);
RNA_property_update(C, &ptr, prop);
}
}
/* set notifier that keyframes have changed */
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_ADDED, NULL);
return OPERATOR_FINISHED;
}
void ACTION_OT_new(wmOperatorType *ot)
{
/* identifiers */
ot->name = "New Action";
ot->idname = "ACTION_OT_new";
ot->description = "Create new action";
/* api callbacks */
ot->exec = action_new_exec;
ot->poll = action_new_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* ******************* Action Push-Down Operator ******************** */
/* Criteria:
* 1) There must be an dopesheet/action editor, and it must be in a mode which uses actions
* 2) There must be an action active
* 3) The associated AnimData block must not be in tweak-mode
*/
static bool action_pushdown_poll(bContext *C)
{
if (ED_operator_action_active(C)) {
SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C);
AnimData *adt = ED_actedit_animdata_from_context(C, NULL);
/* Check for AnimData, Actions, and that tweak-mode is off. */
if (adt && saction->action) {
/* NOTE: We check this for the AnimData block in question and not the global flag,
* as the global flag may be left dirty by some of the browsing ops here.
*/
if (!(adt->flag & ADT_NLA_EDIT_ON)) {
return true;
}
}
}
/* something failed... */
return false;
}
static int action_pushdown_exec(bContext *C, wmOperator *op)
{
SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C);
ID *adt_id_owner = NULL;
AnimData *adt = ED_actedit_animdata_from_context(C, &adt_id_owner);
/* Do the deed... */
if (adt) {
/* Perform the push-down operation
* - This will deal with all the AnimData-side user-counts. */
if (action_has_motion(adt->action) == 0) {
/* action may not be suitable... */
BKE_report(op->reports, RPT_WARNING, "Action must have at least one keyframe or F-Modifier");
return OPERATOR_CANCELLED;
}
/* action can be safely added */
BKE_nla_action_pushdown(adt, ID_IS_OVERRIDE_LIBRARY(adt_id_owner));
struct Main *bmain = CTX_data_main(C);
DEG_id_tag_update_ex(bmain, adt_id_owner, 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);
/* Stop displaying this action in this editor
* NOTE: The editor itself doesn't set a user...
*/
saction->action = NULL;
}
/* Send notifiers that stuff has changed */
WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL);
return OPERATOR_FINISHED;
}
void ACTION_OT_push_down(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Push Down Action";
ot->idname = "ACTION_OT_push_down";
ot->description = "Push action down on to the NLA stack as a new strip";
/* callbacks */
ot->exec = action_pushdown_exec;
ot->poll = action_pushdown_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* ******************* Action Stash Operator ******************** */
static int action_stash_exec(bContext *C, wmOperator *op)
{
SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C);
ID *adt_id_owner = NULL;
AnimData *adt = ED_actedit_animdata_from_context(C, &adt_id_owner);
/* Perform stashing operation */
if (adt) {
/* don't do anything if this action is empty... */
if (action_has_motion(adt->action) == 0) {
/* action may not be suitable... */
BKE_report(op->reports, RPT_WARNING, "Action must have at least one keyframe or F-Modifier");
return OPERATOR_CANCELLED;
}
/* stash the action */
if (BKE_nla_action_stash(adt, ID_IS_OVERRIDE_LIBRARY(adt_id_owner))) {
/* The stash operation will remove the user already,
* so the flushing step later shouldn't double up
* the user-count fixes. Hence, we must unset this ref
* first before setting the new action.
*/
saction->action = NULL;
}
else {
/* action has already been added - simply warn about this, and clear */
BKE_report(op->reports, RPT_ERROR, "Action has already been stashed");
}
/* clear action refs from editor, and then also the backing data (not necessary) */
actedit_change_action(C, NULL);
}
/* Send notifiers that stuff has changed */
WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL);
return OPERATOR_FINISHED;
}
void ACTION_OT_stash(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Stash Action";
ot->idname = "ACTION_OT_stash";
ot->description = "Store this action in the NLA stack as a non-contributing strip for later use";
/* callbacks */
ot->exec = action_stash_exec;
ot->poll = action_pushdown_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
ot->prop = RNA_def_boolean(ot->srna,
"create_new",
true,
"Create New Action",
"Create a new action once the existing one has been safely stored");
}
/* ----------------- */
/* Criteria:
* 1) There must be an dopesheet/action editor, and it must be in a mode which uses actions
* 2) The associated AnimData block must not be in tweak-mode
*/
static bool action_stash_create_poll(bContext *C)
{
if (ED_operator_action_active(C)) {
AnimData *adt = ED_actedit_animdata_from_context(C, NULL);
/* Check tweak-mode is off (as you don't want to be tampering with the action in that case) */
/* NOTE: unlike for pushdown,
* this operator needs to be run when creating an action from nothing... */
if (adt) {
if (!(adt->flag & ADT_NLA_EDIT_ON)) {
return true;
}
}
else {
/* There may not be any action/animdata yet, so, just fallback to the global setting
* (which may not be totally valid yet if the action editor was used and things are
* now in an inconsistent state)
*/
SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C);
Scene *scene = CTX_data_scene(C);
if (!(scene->flag & SCE_NLA_EDIT_ON)) {
/* For now, actions are only for the active object, and on object and shape-key levels...
*/
return ELEM(saction->mode, SACTCONT_ACTION, SACTCONT_SHAPEKEY);
}
}
}
/* something failed... */
return false;
}
static int action_stash_create_exec(bContext *C, wmOperator *op)
{
SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C);
ID *adt_id_owner = NULL;
AnimData *adt = ED_actedit_animdata_from_context(C, &adt_id_owner);
/* Check for no action... */
if (saction->action == NULL) {
/* just create a new action */
bAction *action = action_create_new(C, NULL);
actedit_change_action(C, action);
}
else if (adt) {
/* Perform stashing operation */
if (action_has_motion(adt->action) == 0) {
/* don't do anything if this action is empty... */
BKE_report(op->reports, RPT_WARNING, "Action must have at least one keyframe or F-Modifier");
return OPERATOR_CANCELLED;
}
/* stash the action */
if (BKE_nla_action_stash(adt, ID_IS_OVERRIDE_LIBRARY(adt_id_owner))) {
bAction *new_action = NULL;
/* Create new action not based on the old one
* (since the "new" operator already does that). */
new_action = action_create_new(C, NULL);
/* The stash operation will remove the user already,
* so the flushing step later shouldn't double up
* the user-count fixes. Hence, we must unset this ref
* first before setting the new action.
*/
saction->action = NULL;
actedit_change_action(C, new_action);
}
else {
/* action has already been added - simply warn about this, and clear */
BKE_report(op->reports, RPT_ERROR, "Action has already been stashed");
actedit_change_action(C, NULL);
}
}
/* Send notifiers that stuff has changed */
WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL);
return OPERATOR_FINISHED;
}
void ACTION_OT_stash_and_create(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Stash Action";
ot->idname = "ACTION_OT_stash_and_create";
ot->description =
"Store this action in the NLA stack as a non-contributing strip for later use, and create a "
"new action";
/* callbacks */
ot->exec = action_stash_create_exec;
ot->poll = action_stash_create_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* ************************************************************************** */
/* ACTION UNLINK */
/* ******************* Action Unlink Operator ******************** */
/* We use a custom unlink operator here, as there are some technicalities which need special care:
* 1) When in Tweak Mode, it shouldn't be possible to unlink the active action,
* or else, everything turns to custard.
* 2) If the Action doesn't have any other users, the user should at least get
* a warning that it is going to get lost.
* 3) We need a convenient way to exit Tweak Mode from the Action Editor
*/
void ED_animedit_unlink_action(
bContext *C, ID *id, AnimData *adt, bAction *act, ReportList *reports, bool force_delete)
{
ScrArea *area = CTX_wm_area(C);
/* If the old action only has a single user (that it's about to lose),
* warn user about it
*
* TODO: Maybe we should just save it for them? But then, there's the problem of
* trying to get rid of stuff that's actually unwanted!
*/
if (act->id.us == 1) {
BKE_reportf(reports,
RPT_WARNING,
"Action '%s' will not be saved, create Fake User or Stash in NLA Stack to retain",
act->id.name + 2);
}
/* Clear Fake User and remove action stashing strip (if present) */
if (force_delete) {
/* Remove stashed strip binding this action to this datablock */
/* XXX: we cannot unlink it from *OTHER* datablocks that may also be stashing it,
* but GE users only seem to use/care about single-object binding for now so this
* should be fine
*/
if (adt) {
NlaTrack *nlt, *nlt_next;
NlaStrip *strip, *nstrip;
for (nlt = adt->nla_tracks.first; nlt; nlt = nlt_next) {
nlt_next = nlt->next;
if (strstr(nlt->name, DATA_("[Action Stash]"))) {
for (strip = nlt->strips.first; strip; strip = nstrip) {
nstrip = strip->next;
if (strip->act == act) {
/* Remove this strip, and the track too if it doesn't have anything else */
BKE_nlastrip_remove_and_free(&nlt->strips, strip, true);
if (nlt->strips.first == NULL) {
BLI_assert(nstrip == NULL);
BKE_nlatrack_free(&adt->nla_tracks, nlt, true);
}
}
}
}
}
}
/* Clear Fake User */
id_fake_user_clear(&act->id);
}
/* If in Tweak Mode, don't unlink. Instead, this becomes a shortcut to exit Tweak Mode. */
if ((adt) && (adt->flag & ADT_NLA_EDIT_ON)) {
BKE_nla_tweakmode_exit(adt);
Scene *scene = CTX_data_scene(C);
if (scene != NULL) {
scene->flag &= ~SCE_NLA_EDIT_ON;
}
}
else {
/* Unlink normally - Setting it to NULL should be enough to get the old one unlinked */
if (area->spacetype == SPACE_ACTION) {
/* clear action editor -> action */
actedit_change_action(C, NULL);
}
else {
/* clear AnimData -> action */
PointerRNA ptr;
PropertyRNA *prop;
/* create AnimData RNA pointers */
RNA_pointer_create(id, &RNA_AnimData, adt, &ptr);
prop = RNA_struct_find_property(&ptr, "action");
/* clear... */
RNA_property_pointer_set(&ptr, prop, PointerRNA_NULL, NULL);
RNA_property_update(C, &ptr, prop);
}
}
}
/* -------------------------- */
static bool action_unlink_poll(bContext *C)
{
if (ED_operator_action_active(C)) {
SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C);
AnimData *adt = ED_actedit_animdata_from_context(C, NULL);
/* Only when there's an active action, in the right modes... */
if (saction->action && adt) {
return true;
}
}
/* something failed... */
return false;
}
static int action_unlink_exec(bContext *C, wmOperator *op)
{
AnimData *adt = ED_actedit_animdata_from_context(C, NULL);
bool force_delete = RNA_boolean_get(op->ptr, "force_delete");
if (adt && adt->action) {
ED_animedit_unlink_action(C, NULL, adt, adt->action, op->reports, force_delete);
}
/* Unlink is also abused to exit NLA tweak mode. */
WM_main_add_notifier(NC_ANIMATION | ND_NLA_ACTCHANGE, NULL);
return OPERATOR_FINISHED;
}
static int 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->modifier & KM_SHIFT);
return action_unlink_exec(C, op);
}
void ACTION_OT_unlink(wmOperatorType *ot)
{
PropertyRNA *prop;
/* identifiers */
ot->name = "Unlink Action";
ot->idname = "ACTION_OT_unlink";
ot->description = "Unlink this action from the active action slot (and/or exit Tweak Mode)";
/* callbacks */
ot->invoke = action_unlink_invoke;
ot->exec = action_unlink_exec;
ot->poll = 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);
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* ************************************************************************** */
/* ACTION BROWSING */
/* Try to find NLA Strip to use for action layer up/down tool */
static NlaStrip *action_layer_get_nlastrip(ListBase *strips, float ctime)
{
NlaStrip *strip;
for (strip = strips->first; strip; strip = strip->next) {
/* Can we use this? */
if (IN_RANGE_INCL(ctime, strip->start, strip->end)) {
/* in range - use this one */
return strip;
}
if ((ctime < strip->start) && (strip->prev == NULL)) {
/* before first - use this one */
return strip;
}
if ((ctime > strip->end) && (strip->next == NULL)) {
/* after last - use this one */
return strip;
}
}
/* nothing suitable found... */
return NULL;
}
/* Switch NLA Strips/Actions. */
static void action_layer_switch_strip(
AnimData *adt, NlaTrack *old_track, NlaStrip *old_strip, NlaTrack *nlt, NlaStrip *strip)
{
/* Exit tweak-mode on old strip
* NOTE: We need to manually clear this stuff ourselves, as tweak-mode exit doesn't do it
*/
BKE_nla_tweakmode_exit(adt);
if (old_strip) {
old_strip->flag &= ~(NLASTRIP_FLAG_ACTIVE | NLASTRIP_FLAG_SELECT);
}
if (old_track) {
old_track->flag &= ~(NLATRACK_ACTIVE | NLATRACK_SELECTED);
}
/* Make this one the active one instead */
strip->flag |= (NLASTRIP_FLAG_ACTIVE | NLASTRIP_FLAG_SELECT);
nlt->flag |= NLATRACK_ACTIVE;
/* Copy over "solo" flag - This is useful for stashed actions... */
if (old_track) {
if (old_track->flag & NLATRACK_SOLO) {
old_track->flag &= ~NLATRACK_SOLO;
nlt->flag |= NLATRACK_SOLO;
}
}
else {
/* NLA muting <==> Solo Tracks */
if (adt->flag & ADT_NLA_EVAL_OFF) {
/* disable NLA muting */
adt->flag &= ~ADT_NLA_EVAL_OFF;
/* mark this track as being solo */
adt->flag |= ADT_NLA_SOLO_TRACK;
nlt->flag |= NLATRACK_SOLO;
/* TODO: Needs rest-pose flushing (when we get reference track) */
}
}
/* Enter tweak-mode again - hopefully we're now "it" */
BKE_nla_tweakmode_enter(adt);
BLI_assert(adt->actstrip == strip);
}
/* ********************** One Layer Up Operator ************************** */
static bool action_layer_next_poll(bContext *C)
{
/* Action Editor's action editing modes only */
if (ED_operator_action_active(C)) {
AnimData *adt = ED_actedit_animdata_from_context(C, NULL);
if (adt) {
/* only allow if we're in tweak-mode, and there's something above us... */
if (adt->flag & ADT_NLA_EDIT_ON) {
/* We need to check if there are any tracks above the active one
* since the track the action comes from is not stored in AnimData
*/
if (adt->nla_tracks.last) {
NlaTrack *nlt = (NlaTrack *)adt->nla_tracks.last;
if (nlt->flag & NLATRACK_DISABLED) {
/* A disabled track will either be the track itself,
* or one of the ones above it.
*
* If this is the top-most one, there is the possibility
* that there is no active action. For now, we let this
* case return true too, so that there is a natural way
* to "move to an empty layer", even though this means
* that we won't actually have an action.
*/
// return (adt->tmpact != NULL);
return true;
}
}
}
}
}
/* something failed... */
return false;
}
static int action_layer_next_exec(bContext *C, wmOperator *op)
{
AnimData *adt = ED_actedit_animdata_from_context(C, NULL);
NlaTrack *act_track;
Scene *scene = CTX_data_scene(C);
float ctime = BKE_scene_ctime_get(scene);
/* Get active track */
act_track = BKE_nlatrack_find_tweaked(adt);
if (act_track == NULL) {
BKE_report(op->reports, RPT_ERROR, "Could not find current NLA Track");
return OPERATOR_CANCELLED;
}
/* Find next action, and hook it up */
if (act_track->next) {
NlaTrack *nlt;
/* Find next action to use */
for (nlt = act_track->next; nlt; nlt = nlt->next) {
NlaStrip *strip = action_layer_get_nlastrip(&nlt->strips, ctime);
if (strip) {
action_layer_switch_strip(adt, act_track, adt->actstrip, nlt, strip);
break;
}
}
}
else {
/* No more actions (strips) - Go back to editing the original active action
* NOTE: This will mean exiting tweak-mode...
*/
BKE_nla_tweakmode_exit(adt);
/* Deal with solo flags...
* Assume: Solo Track == NLA Muting
*/
if (adt->flag & ADT_NLA_SOLO_TRACK) {
/* turn off solo flags on tracks */
act_track->flag &= ~NLATRACK_SOLO;
adt->flag &= ~ADT_NLA_SOLO_TRACK;
/* turn on NLA muting (to keep same effect) */
adt->flag |= ADT_NLA_EVAL_OFF;
/* TODO: Needs rest-pose flushing (when we get reference track) */
}
}
/* Update the action that this editor now uses
* NOTE: The calls above have already handled the user-count/anim-data side of things. */
actedit_change_action(C, adt->action);
return OPERATOR_FINISHED;
}
void ACTION_OT_layer_next(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Next Layer";
ot->idname = "ACTION_OT_layer_next";
ot->description =
"Switch to editing action in animation layer above the current action in the NLA Stack";
/* callbacks */
ot->exec = action_layer_next_exec;
ot->poll = action_layer_next_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* ********************* One Layer Down Operator ************************* */
static bool action_layer_prev_poll(bContext *C)
{
/* Action Editor's action editing modes only */
if (ED_operator_action_active(C)) {
AnimData *adt = ED_actedit_animdata_from_context(C, NULL);
if (adt) {
if (adt->flag & ADT_NLA_EDIT_ON) {
/* Tweak Mode: We need to check if there are any tracks below the active one
* that we can move to */
if (adt->nla_tracks.first) {
NlaTrack *nlt = (NlaTrack *)adt->nla_tracks.first;
/* Since the first disabled track is the track being tweaked/edited,
* we can simplify things by only checking the first track:
* - If it is disabled, this is the track being tweaked,
* so there can't be anything below it
* - Otherwise, there is at least 1 track below the tweaking
* track that we can descend to
*/
if ((nlt->flag & NLATRACK_DISABLED) == 0) {
/* not disabled = there are actions below the one being tweaked */
return true;
}
}
}
else {
/* Normal Mode: If there are any tracks, we can try moving to those */
return (adt->nla_tracks.first != NULL);
}
}
}
/* something failed... */
return false;
}
static int action_layer_prev_exec(bContext *C, wmOperator *op)
{
AnimData *adt = ED_actedit_animdata_from_context(C, NULL);
NlaTrack *act_track;
NlaTrack *nlt;
Scene *scene = CTX_data_scene(C);
float ctime = BKE_scene_ctime_get(scene);
/* Sanity Check */
if (adt == NULL) {
BKE_report(
op->reports, RPT_ERROR, "Internal Error: Could not find Animation Data/NLA Stack to use");
return OPERATOR_CANCELLED;
}
/* Get active track */
act_track = BKE_nlatrack_find_tweaked(adt);
/* If there is no active track, that means we are using the active action... */
if (act_track) {
/* Active Track - Start from the one below it */
nlt = act_track->prev;
}
else {
/* Active Action - Use the top-most track */
nlt = adt->nla_tracks.last;
}
/* Find previous action and hook it up */
for (; nlt; nlt = nlt->prev) {
NlaStrip *strip = action_layer_get_nlastrip(&nlt->strips, ctime);
if (strip) {
action_layer_switch_strip(adt, act_track, adt->actstrip, nlt, strip);
break;
}
}
/* Update the action that this editor now uses
* NOTE: The calls above have already handled the user-count/animdata side of things. */
actedit_change_action(C, adt->action);
return OPERATOR_FINISHED;
}
void ACTION_OT_layer_prev(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Previous Layer";
ot->idname = "ACTION_OT_layer_prev";
ot->description =
"Switch to editing action in animation layer below the current action in the NLA Stack";
/* callbacks */
ot->exec = action_layer_prev_exec;
ot->poll = action_layer_prev_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* ************************************************************************** */

View File

@ -0,0 +1,25 @@
***************
*** 576,586 ****
if (strip->act == act) {
/* Remove this strip, and the track too if it doesn't have anything else */
- BKE_nlastrip_free(&nlt->strips, strip, true);
if (nlt->strips.first == NULL) {
BLI_assert(nstrip == NULL);
- BKE_nlatrack_free(&adt->nla_tracks, nlt, true);
}
}
}
--- 576,586 ----
if (strip->act == act) {
/* Remove this strip, and the track too if it doesn't have anything else */
+ BKE_nlastrip_remove_and_free(&nlt->strips, strip, true);
if (nlt->strips.first == NULL) {
BLI_assert(nstrip == NULL);
+ BKE_nlatrack_remove_and_free(&adt->nla_tracks, nlt, true);
}
}
}

View File

@ -581,14 +581,14 @@ bool nlaedit_add_tracks_existing(bAnimContext *ac, bool above_sel)
*/
if (above_sel) {
/* just add a new one above this one */
BKE_nlatrack_add(adt, nlt, is_liboverride);
BKE_nlatrack_new_after_and_set_active(&adt->nla_tracks, 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);
BKE_nlatrack_new_tail_and_set_active(&adt->nla_tracks, is_liboverride);
lastAdt = adt;
ale->update = ANIM_UPDATE_DEPS;
added = true;
@ -625,7 +625,7 @@ bool nlaedit_add_tracks_empty(bAnimContext *ac)
/* 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));
BKE_nlatrack_new_tail_and_set_active(&adt->nla_tracks, ID_IS_OVERRIDE_LIBRARY(ale->id));
ale->update = ANIM_UPDATE_DEPS;
added = true;
}
@ -736,7 +736,7 @@ static int nlaedit_delete_tracks_exec(bContext *C, wmOperator *UNUSED(op))
}
/* call delete on this track - deletes all strips too */
BKE_nlatrack_free(&adt->nla_tracks, nlt, true);
BKE_nlatrack_remove_and_free(&adt->nla_tracks, nlt, true);
ale->update = ANIM_UPDATE_DEPS;
}
}

View File

@ -0,0 +1,828 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 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_layer.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, int channel_index, short selectmode)
{
ListBase anim_data = {NULL, NULL};
bAnimListElem *ale;
int filter;
int notifierFlags = 0;
/* get the channel that was clicked on */
/* filter channels */
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS |
ANIMFILTER_FCURVESONLY);
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? */
BKE_view_layer_synced_ensure(ac->scene, view_layer);
LISTBASE_FOREACH (Base *, b, BKE_view_layer_object_bases_get(view_layer)) {
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 [#37883] */
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;
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);
/* 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 tweak-mode, clicking here gets us out of tweak-mode, as changing selection
* while in tweak-mode 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 tweak-mode 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, 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 |
ANIMFILTER_FCURVESONLY);
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 Tweak-mode. */
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 | PROP_HIDDEN);
}
/* ******************** Action Unlink ******************************** */
static bool nla_action_unlink_poll(bContext *C)
{
if (ED_operator_nla_active(C)) {
PointerRNA adt_ptr;
return (nla_panel_context(C, &adt_ptr, NULL, NULL) && (adt_ptr.data != 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->modifier & KM_SHIFT);
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 */
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 | ANIMFILTER_FCURVESONLY);
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;
}
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 | ANIMFILTER_FCURVESONLY);
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 | ANIMFILTER_FCURVESONLY);
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_ensure_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;
}
/* *********************************************** */

View File

@ -713,12 +713,12 @@ static int nlaedit_add_actionclip_exec(bContext *C, wmOperator *op)
strip->start = cfra;
/* firstly try adding strip to our current track, but if that fails, add to a new track */
if (BKE_nlatrack_add_strip(nlt, strip, is_liboverride) == 0) {
if (!BKE_nlatrack_try_add_strip(nlt, strip, is_liboverride)) {
/* trying to add to the current failed (no space),
* so add a new track to the stack, and add to that...
*/
nlt = BKE_nlatrack_add(adt, NULL, is_liboverride);
BKE_nlatrack_add_strip(nlt, strip, is_liboverride);
nlt = BKE_nlatrack_new_tail_and_set_active(&adt->nla_tracks, is_liboverride);
BKE_nlatrack_add_strip(nlt, strip);
}
/* auto-name it */
@ -951,12 +951,12 @@ static int nlaedit_add_sound_exec(bContext *C, wmOperator *UNUSED(op))
strip->end += cfra;
/* firstly try adding strip to our current track, but if that fails, add to a new track */
if (BKE_nlatrack_add_strip(nlt, strip, is_liboverride) == 0) {
if (!BKE_nlatrack_try_add_strip(nlt, strip, is_liboverride)) {
/* trying to add to the current failed (no space),
* so add a new track to the stack, and add to that...
*/
nlt = BKE_nlatrack_add(adt, NULL, is_liboverride);
BKE_nlatrack_add_strip(nlt, strip, is_liboverride);
nlt = BKE_nlatrack_new_tail_and_set_active(&adt->nla_tracks, is_liboverride);
BKE_nlatrack_add_strip(nlt, strip);
}
/* auto-name it */
@ -1190,13 +1190,9 @@ static int nlaedit_duplicate_exec(bContext *C, wmOperator *op)
/* in case there's no space in the track above,
* or we haven't got a reference to it yet, try adding */
if (BKE_nlatrack_add_strip(nlt->next, nstrip, is_liboverride) == 0) {
/* need to add a new track above the one above the current one
* - if the current one is the last one, nlt->next will be NULL, which defaults to adding
* at the top of the stack anyway...
*/
track = BKE_nlatrack_add(adt, nlt->next, is_liboverride);
BKE_nlatrack_add_strip(track, nstrip, is_liboverride);
if (!BKE_nlatrack_try_add_strip(nlt->next, nstrip, is_liboverride)) {
track = BKE_nlatrack_new_after_and_set_active(&adt->nla_tracks, nlt, is_liboverride);
BKE_nlatrack_add_strip(track, nstrip);
}
/* deselect the original and the active flag */
@ -1731,8 +1727,8 @@ static int nlaedit_swap_exec(bContext *C, wmOperator *op)
}
/* add strips back to track now */
BKE_nlatrack_add_strip(nlt, area, is_liboverride);
BKE_nlatrack_add_strip(nlt, sb, is_liboverride);
BKE_nlatrack_try_add_strip(nlt, area, is_liboverride);
BKE_nlatrack_try_add_strip(nlt, sb, is_liboverride);
}
/* Clear (temp) meta-strips. */
@ -1802,8 +1798,6 @@ static int nlaedit_move_up_exec(bContext *C, wmOperator *UNUSED(op))
NlaTrack *nltn = nlt->next;
NlaStrip *strip, *stripn;
const bool is_liboverride = ID_IS_OVERRIDE_LIBRARY(ale->id);
/* if this track has no tracks after it, skip for now... */
if (nltn == NULL) {
continue;
@ -1894,8 +1888,6 @@ static int nlaedit_move_down_exec(bContext *C, wmOperator *UNUSED(op))
NlaTrack *nltp = nlt->prev;
NlaStrip *strip, *stripn;
const bool is_liboverride = ID_IS_OVERRIDE_LIBRARY(ale->id);
/* if this track has no tracks before it, skip for now... */
if (nltp == NULL) {
continue;
@ -2457,10 +2449,10 @@ static int nlaedit_snap_exec(bContext *C, wmOperator *op)
BLI_remlink(&tmp_strips, strip);
/* in case there's no space in the current track, try adding */
if (BKE_nlatrack_add_strip(nlt, strip, is_liboverride) == 0) {
if (!BKE_nlatrack_try_add_strip(nlt, strip, is_liboverride)) {
/* need to add a new track above the current one */
track = BKE_nlatrack_add(adt, nlt, is_liboverride);
BKE_nlatrack_add_strip(track, strip, is_liboverride);
track = BKE_nlatrack_new_after_and_set_active(&adt->nla_tracks, nlt, is_liboverride);
BKE_nlatrack_add_strip(track, strip);
/* clear temp meta-strips on this new track,
* as we may not be able to get back to it */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,71 @@
***************
*** 1310,1324 ****
if (strip->flag & NLASTRIP_FLAG_SELECT) {
/* if a strip either side of this was a transition, delete those too */
if ((strip->prev) && (strip->prev->type == NLASTRIP_TYPE_TRANSITION)) {
- BKE_nlastrip_free(&nlt->strips, strip->prev, true);
}
if ((nstrip) && (nstrip->type == NLASTRIP_TYPE_TRANSITION)) {
nstrip = nstrip->next;
- BKE_nlastrip_free(&nlt->strips, strip->next, true);
}
/* finally, delete this strip */
- BKE_nlastrip_free(&nlt->strips, strip, true);
}
}
}
--- 1306,1320 ----
if (strip->flag & NLASTRIP_FLAG_SELECT) {
/* if a strip either side of this was a transition, delete those too */
if ((strip->prev) && (strip->prev->type == NLASTRIP_TYPE_TRANSITION)) {
+ BKE_nlastrip_remove_and_free(&nlt->strips, strip->prev, true);
}
if ((nstrip) && (nstrip->type == NLASTRIP_TYPE_TRANSITION)) {
nstrip = nstrip->next;
+ BKE_nlastrip_remove_and_free(&nlt->strips, strip->next, true);
}
/* finally, delete this strip */
+ BKE_nlastrip_remove_and_free(&nlt->strips, strip, true);
}
}
}
***************
*** 1824,1831 ****
if (BKE_nlatrack_has_space(nltn, strip->start, strip->end)) {
/* remove from its current track, and add to the one above
* (it 'should' work, so no need to worry) */
- BLI_remlink(&nlt->strips, strip);
- BKE_nlatrack_add_strip(nltn, strip, is_liboverride);
}
}
}
--- 1818,1825 ----
if (BKE_nlatrack_has_space(nltn, strip->start, strip->end)) {
/* remove from its current track, and add to the one above
* (it 'should' work, so no need to worry) */
+ BKE_nlatrack_remove_strip(nlt, strip);
+ BKE_nlatrack_add_strip(nltn, strip);
}
}
}
***************
*** 1916,1923 ****
if (BKE_nlatrack_has_space(nltp, strip->start, strip->end)) {
/* remove from its current track, and add to the one above
* (it 'should' work, so no need to worry) */
- BLI_remlink(&nlt->strips, strip);
- BKE_nlatrack_add_strip(nltp, strip, is_liboverride);
}
}
}
--- 1908,1915 ----
if (BKE_nlatrack_has_space(nltp, strip->start, strip->end)) {
/* remove from its current track, and add to the one above
* (it 'should' work, so no need to worry) */
+ BKE_nlatrack_remove_strip(nlt, strip);
+ BKE_nlatrack_add_strip(nltp, strip);
}
}
}

View File

@ -434,7 +434,6 @@ static void recalcData_nla(TransInfo *t)
* as only one may have been altered by transform if only 1 handle moved.
*/
/* In LibOverride case, we cannot move strips across tracks that come from the linked data. */
const bool is_liboverride = ID_IS_OVERRIDE_LIBRARY(tdn->id);
if (BKE_nlatrack_is_nonlocal_in_liboverride(tdn->id, tdn->nlt)) {
continue;
}
@ -489,7 +488,7 @@ static void recalcData_nla(TransInfo *t)
}
}
}
}
}
}
/** \} */

View File

@ -0,0 +1,549 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2001-2002 NaN Holding BV. All rights reserved. */
/** \file
* \ingroup edtransform
*/
#include "DNA_anim_types.h"
#include "DNA_space_types.h"
#include "MEM_guardedalloc.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BKE_context.h"
#include "BKE_nla.h"
#include "ED_anim_api.h"
#include "ED_markers.h"
#include "WM_api.h"
#include "WM_types.h"
#include "RNA_access.h"
#include "RNA_prototypes.h"
#include "transform.h"
#include "transform_convert.h"
#include "transform_mode.h"
#include "transform_snap.h"
/** Used for NLA transform (stored in #TransData.extra pointer). */
typedef struct TransDataNla {
/** ID-block NLA-data is attached to. */
ID *id;
/** Original NLA-Track that the strip belongs to. */
struct NlaTrack *oldTrack;
/** Current NLA-Track that the strip belongs to. */
struct NlaTrack *nlt;
/** NLA-strip this data represents. */
struct NlaStrip *strip;
/* dummy values for transform to write in - must have 3 elements... */
/** start handle. */
float h1[3];
/** end handle. */
float h2[3];
/** index of track that strip is currently in. */
int trackIndex;
/** handle-index: 0 for dummy entry, -1 for start, 1 for end, 2 for both ends. */
int handle;
} TransDataNla;
/* -------------------------------------------------------------------- */
/** \name Transform application to NLA strips
* \{ */
/**
* \brief Applies a translation to the given #NlaStrip.
* \param strip_rna_ptr: The RNA pointer of the NLA strip to modify.
* \param transdata: The transformation info structure.
*/
static void applyTransformNLA_translation(PointerRNA *strip_rna_ptr, const TransDataNla *transdata)
{
/* NOTE: we write these twice to avoid truncation errors which can arise when
* moving the strips a large distance using numeric input #33852.
*/
RNA_float_set(strip_rna_ptr, "frame_start", transdata->h1[0]);
RNA_float_set(strip_rna_ptr, "frame_end", transdata->h2[0]);
RNA_float_set(strip_rna_ptr, "frame_start", transdata->h1[0]);
RNA_float_set(strip_rna_ptr, "frame_end", transdata->h2[0]);
}
static void applyTransformNLA_timeScale(PointerRNA *strip_rna_ptr, const float value)
{
RNA_float_set(strip_rna_ptr, "scale", value);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name NLA Transform Creation
* \{ */
static void createTransNlaData(bContext *C, TransInfo *t)
{
Scene *scene = t->scene;
SpaceNla *snla = NULL;
TransData *td = NULL;
TransDataNla *tdn = NULL;
bAnimContext ac;
ListBase anim_data = {NULL, NULL};
bAnimListElem *ale;
int filter;
int count = 0;
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
/* determine what type of data we are operating on */
if (ANIM_animdata_get_context(C, &ac) == 0) {
return;
}
snla = (SpaceNla *)ac.sl;
/* filter data */
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT |
ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* which side of the current frame should be allowed */
if (t->mode == TFM_TIME_EXTEND) {
t->frame_side = transform_convert_frame_side_dir_get(t, (float)scene->r.cfra);
}
else {
/* normal transform - both sides of current frame are considered */
t->frame_side = 'B';
}
/* loop 1: count how many strips are selected (consider each strip as 2 points) */
for (ale = anim_data.first; ale; ale = ale->next) {
NlaTrack *nlt = (NlaTrack *)ale->data;
NlaStrip *strip;
/* make some meta-strips for chains of selected strips */
BKE_nlastrips_make_metas(&nlt->strips, 1);
/* only consider selected strips */
for (strip = nlt->strips.first; strip; strip = strip->next) {
/* TODO: we can make strips have handles later on. */
/* transition strips can't get directly transformed */
if (strip->type != NLASTRIP_TYPE_TRANSITION) {
if (strip->flag & NLASTRIP_FLAG_SELECT) {
if (FrameOnMouseSide(t->frame_side, strip->start, (float)scene->r.cfra)) {
count++;
}
if (FrameOnMouseSide(t->frame_side, strip->end, (float)scene->r.cfra)) {
count++;
}
}
}
}
}
/* stop if trying to build list if nothing selected */
if (count == 0) {
/* clear temp metas that may have been created but aren't needed now
* because they fell on the wrong side of scene->r.cfra
*/
for (ale = anim_data.first; ale; ale = ale->next) {
NlaTrack *nlt = (NlaTrack *)ale->data;
BKE_nlastrips_clear_metas(&nlt->strips, 0, 1);
}
/* cleanup temp list */
ANIM_animdata_freelist(&anim_data);
return;
}
/* allocate memory for data */
tc->data_len = count;
tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransData(NLA Editor)");
td = tc->data;
tc->custom.type.data = tdn = MEM_callocN(tc->data_len * sizeof(TransDataNla),
"TransDataNla (NLA Editor)");
tc->custom.type.use_free = true;
/* loop 2: build transdata array */
for (ale = anim_data.first; ale; ale = ale->next) {
/* only if a real NLA-track */
if (ale->type == ANIMTYPE_NLATRACK) {
AnimData *adt = ale->adt;
NlaTrack *nlt = (NlaTrack *)ale->data;
NlaStrip *strip;
/* only consider selected strips */
for (strip = nlt->strips.first; strip; strip = strip->next) {
/* TODO: we can make strips have handles later on. */
/* transition strips can't get directly transformed */
if (strip->type != NLASTRIP_TYPE_TRANSITION) {
if (strip->flag & NLASTRIP_FLAG_SELECT) {
/* our transform data is constructed as follows:
* - only the handles on the right side of the current-frame get included
* - td structs are transform-elements operated on by the transform system
* and represent a single handle. The storage/pointer used (val or loc) depends on
* whether we're scaling or transforming. Ultimately though, the handles
* the td writes to will simply be a dummy in tdn
* - for each strip being transformed, a single tdn struct is used, so in some
* cases, there will need to be 1 of these tdn elements in the array skipped...
*/
float center[3], yval;
/* firstly, init tdn settings */
tdn->id = ale->id;
tdn->oldTrack = tdn->nlt = nlt;
tdn->strip = strip;
tdn->trackIndex = BLI_findindex(&adt->nla_tracks, nlt);
yval = (float)(tdn->trackIndex * NLACHANNEL_STEP(snla));
tdn->h1[0] = strip->start;
tdn->h1[1] = yval;
tdn->h2[0] = strip->end;
tdn->h2[1] = yval;
tdn->h1[2] = tdn->h2[2] = strip->scale;
center[0] = (float)scene->r.cfra;
center[1] = yval;
center[2] = 0.0f;
/* set td's based on which handles are applicable */
if (FrameOnMouseSide(t->frame_side, strip->start, (float)scene->r.cfra)) {
/* just set tdn to assume that it only has one handle for now */
tdn->handle = -1;
/* Now, link the transform data up to this data. */
td->loc = tdn->h1;
copy_v3_v3(td->iloc, tdn->h1);
if (ELEM(t->mode, TFM_TRANSLATION, TFM_TIME_EXTEND)) {
/* Store all the other gunk that is required by transform. */
copy_v3_v3(td->center, center);
td->axismtx[2][2] = 1.0f;
td->flag |= TD_SELECTED;
unit_m3(td->mtx);
unit_m3(td->smtx);
}
td->extra = tdn;
td++;
}
if (FrameOnMouseSide(t->frame_side, strip->end, (float)scene->r.cfra)) {
/* if tdn is already holding the start handle,
* then we're doing both, otherwise, only end */
tdn->handle = (tdn->handle) ? 2 : 1;
/* Now, link the transform data up to this data. */
td->loc = tdn->h2;
copy_v3_v3(td->iloc, tdn->h2);
if (ELEM(t->mode, TFM_TRANSLATION, TFM_TIME_EXTEND)) {
/* Store all the other gunk that is required by transform. */
copy_v3_v3(td->center, center);
td->axismtx[2][2] = 1.0f;
td->flag |= TD_SELECTED;
unit_m3(td->mtx);
unit_m3(td->smtx);
}
td->extra = tdn;
td++;
}
/* If both handles were used, skip the next tdn (i.e. leave it blank)
* since the counting code is dumb.
* Otherwise, just advance to the next one.
*/
if (tdn->handle == 2) {
tdn += 2;
}
else {
tdn++;
}
}
}
}
}
}
/* cleanup temp list */
ANIM_animdata_freelist(&anim_data);
}
static void recalcData_nla(TransInfo *t)
{
SpaceNla *snla = (SpaceNla *)t->area->spacedata.first;
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
/* handle auto-snapping
* NOTE: only do this when transform is still running, or we can't restore
*/
if (t->state != TRANS_CANCEL) {
const short autosnap = getAnimEdit_SnapMode(t);
if (autosnap != SACTSNAP_OFF) {
TransData *td = tc->data;
for (int i = 0; i < tc->data_len; i++, td++) {
transform_snap_anim_flush_data(t, td, autosnap, td->loc);
}
}
}
/* For each strip we've got, perform some additional validation of the values
* that got set before using RNA to set the value (which does some special
* operations when setting these values to make sure that everything works ok).
*/
TransDataNla *tdn = tc->custom.type.data;
for (int i = 0; i < tc->data_len; i++, tdn++) {
NlaStrip *strip = tdn->strip;
PointerRNA strip_ptr;
int delta_y1, delta_y2;
/* if this tdn has no handles, that means it is just a dummy that should be skipped */
if (tdn->handle == 0) {
continue;
}
/* set refresh tags for objects using this animation,
* BUT only if realtime updates are enabled
*/
if ((snla->flag & SNLA_NOREALTIMEUPDATES) == 0) {
ANIM_id_update(CTX_data_main(t->context), tdn->id);
}
/* if canceling transform, just write the values without validating, then move on */
if (t->state == TRANS_CANCEL) {
/* clear the values by directly overwriting the originals, but also need to restore
* endpoints of neighboring transition-strips
*/
/* start */
strip->start = tdn->h1[0];
if ((strip->prev) && (strip->prev->type == NLASTRIP_TYPE_TRANSITION)) {
strip->prev->end = tdn->h1[0];
}
/* end */
strip->end = tdn->h2[0];
if ((strip->next) && (strip->next->type == NLASTRIP_TYPE_TRANSITION)) {
strip->next->start = tdn->h2[0];
}
strip->scale = tdn->h1[2];
/* flush transforms to child strips (since this should be a meta) */
BKE_nlameta_flush_transforms(strip);
/* restore to original track (if needed) */
if (tdn->oldTrack != tdn->nlt) {
/* Just append to end of list for now,
* since strips get sorted in special_aftertrans_update(). */
BLI_remlink(&tdn->nlt->strips, strip);
BLI_addtail(&tdn->oldTrack->strips, strip);
}
continue;
}
/* firstly, check if the proposed transform locations would overlap with any neighboring strips
* (barring transitions) which are absolute barriers since they are not being moved
*
* this is done as a iterative procedure (done 5 times max for now)
*/
NlaStrip *prev = BKE_nlastrip_prev_in_track(strip, true);
NlaStrip *next = BKE_nlastrip_next_in_track(strip, true);
for (short iter = 0; iter < 5; iter++) {
const bool pExceeded = (prev != NULL) && (tdn->h1[0] < prev->end);
const bool nExceeded = (next != NULL) && (tdn->h2[0] > next->start);
if ((pExceeded && nExceeded) || (iter == 4)) {
/* both endpoints exceeded (or iteration ping-pong'd meaning that we need a
* compromise)
* - Simply crop strip to fit within the bounds of the strips bounding it
* - If there were no neighbors, clear the transforms
* (make it default to the strip's current values).
*/
if (prev && next) {
tdn->h1[0] = prev->end;
tdn->h2[0] = next->start;
}
else {
tdn->h1[0] = strip->start;
tdn->h2[0] = strip->end;
}
}
else if (nExceeded) {
/* move backwards */
float offset = tdn->h2[0] - next->start;
tdn->h1[0] -= offset;
tdn->h2[0] -= offset;
}
else if (pExceeded) {
/* more forwards */
float offset = prev->end - tdn->h1[0];
tdn->h1[0] += offset;
tdn->h2[0] += offset;
}
else { /* all is fine and well */
break;
}
}
/* Use RNA to write the values to ensure that constraints on these are obeyed
* (e.g. for transition strips, the values are taken from the neighbors)
*/
RNA_pointer_create(NULL, &RNA_NlaStrip, strip, &strip_ptr);
switch (t->mode) {
case TFM_TIME_EXTEND:
case TFM_TIME_SCALE: {
/* The final scale is the product of the original strip scale (from before the transform
* operation started) and the current scale value of this transform operation. */
const float originalStripScale = tdn->h1[2];
const float newStripScale = originalStripScale * t->values_final[0];
applyTransformNLA_timeScale(&strip_ptr, newStripScale);
applyTransformNLA_translation(&strip_ptr, tdn);
break;
}
case TFM_TRANSLATION:
applyTransformNLA_translation(&strip_ptr, tdn);
break;
default:
printf("recalcData_nla: unsupported NLA transformation mode %d\n", t->mode);
continue;
}
/* flush transforms to child strips (since this should be a meta) */
BKE_nlameta_flush_transforms(strip);
/* Now, check if we need to try and move track:
* - we need to calculate both,
* as only one may have been altered by transform if only 1 handle moved.
*/
/* In LibOverride case, we cannot move strips across tracks that come from the linked data. */
const bool is_liboverride = ID_IS_OVERRIDE_LIBRARY(tdn->id);
if (BKE_nlatrack_is_nonlocal_in_liboverride(tdn->id, tdn->nlt)) {
continue;
}
delta_y1 = ((int)tdn->h1[1] / NLACHANNEL_STEP(snla) - tdn->trackIndex);
delta_y2 = ((int)tdn->h2[1] / NLACHANNEL_STEP(snla) - tdn->trackIndex);
if (delta_y1 || delta_y2) {
NlaTrack *track;
int delta = (delta_y2) ? delta_y2 : delta_y1;
int n;
/* Move in the requested direction,
* checking at each layer if there's space for strip to pass through,
* stopping on the last track available or that we're able to fit in.
*/
if (delta > 0) {
for (track = tdn->nlt->next, n = 0; (track) && (n < delta); track = track->next, n++) {
/* check if space in this track for the strip */
if (BKE_nlatrack_has_space(track, strip->start, strip->end) &&
!BKE_nlatrack_is_nonlocal_in_liboverride(tdn->id, track)) {
/* move strip to this track */
BKE_nlatrack_remove_strip(tdn->nlt, strip);
BKE_nlatrack_add_strip(track, strip, is_liboverride);
tdn->nlt = track;
tdn->trackIndex++;
}
else { /* can't move any further */
break;
}
}
}
else {
/* make delta 'positive' before using it, since we now know to go backwards */
delta = -delta;
for (track = tdn->nlt->prev, n = 0; (track) && (n < delta); track = track->prev, n++) {
/* check if space in this track for the strip */
if (BKE_nlatrack_has_space(track, strip->start, strip->end) &&
!BKE_nlatrack_is_nonlocal_in_liboverride(tdn->id, track)) {
/* move strip to this track */
BKE_nlatrack_remove_strip(tdn->nlt, strip);
BKE_nlatrack_add_strip(track, strip, is_liboverride);
tdn->nlt = track;
tdn->trackIndex--;
}
else { /* can't move any further */
break;
}
}
}
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Special After Transform NLA
* \{ */
static void special_aftertrans_update__nla(bContext *C, TransInfo *UNUSED(t))
{
bAnimContext ac;
/* initialize relevant anim-context 'context' data */
if (ANIM_animdata_get_context(C, &ac) == 0) {
return;
}
if (ac.datatype) {
ListBase anim_data = {NULL, NULL};
bAnimListElem *ale;
short filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_FCURVESONLY);
/* get channels to work on */
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
for (ale = anim_data.first; ale; ale = ale->next) {
NlaTrack *nlt = (NlaTrack *)ale->data;
/* make sure strips are in order again */
BKE_nlatrack_sort_strips(nlt);
/* remove the temp metas */
BKE_nlastrips_clear_metas(&nlt->strips, 0, 1);
}
/* General refresh for the outliner because the following might have happened:
* - strips moved between tracks
* - strips swapped order
* - duplicate-move moves to different track. */
WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_ADDED, NULL);
/* free temp memory */
ANIM_animdata_freelist(&anim_data);
/* Perform after-transform validation. */
ED_nla_postop_refresh(&ac);
}
}
/** \} */
TransConvertTypeInfo TransConvertType_NLA = {
/*flags*/ (T_POINTS | T_2D_EDIT),
/*createTransData*/ createTransNlaData,
/*recalcData*/ recalcData_nla,
/*special_aftertrans_update*/ special_aftertrans_update__nla,
};

View File

@ -0,0 +1,38 @@
***************
*** 457,464 ****
if (BKE_nlatrack_has_space(track, strip->start, strip->end) &&
!BKE_nlatrack_is_nonlocal_in_liboverride(tdn->id, track)) {
/* move strip to this track */
- BLI_remlink(&tdn->nlt->strips, strip);
- BKE_nlatrack_add_strip(track, strip, is_liboverride);
tdn->nlt = track;
tdn->trackIndex++;
--- 456,463 ----
if (BKE_nlatrack_has_space(track, strip->start, strip->end) &&
!BKE_nlatrack_is_nonlocal_in_liboverride(tdn->id, track)) {
/* move strip to this track */
+ BKE_nlatrack_remove_strip(tdn->nlt, strip);
+ BKE_nlatrack_add_strip(track, strip);
tdn->nlt = track;
tdn->trackIndex++;
***************
*** 477,484 ****
if (BKE_nlatrack_has_space(track, strip->start, strip->end) &&
!BKE_nlatrack_is_nonlocal_in_liboverride(tdn->id, track)) {
/* move strip to this track */
- BLI_remlink(&tdn->nlt->strips, strip);
- BKE_nlatrack_add_strip(track, strip, is_liboverride);
tdn->nlt = track;
tdn->trackIndex--;
--- 476,483 ----
if (BKE_nlatrack_has_space(track, strip->start, strip->end) &&
!BKE_nlatrack_is_nonlocal_in_liboverride(tdn->id, track)) {
/* move strip to this track */
+ BKE_nlatrack_remove_strip(tdn->nlt, strip);
+ BKE_nlatrack_add_strip(track, strip);
tdn->nlt = track;
tdn->trackIndex--;

View File

@ -573,7 +573,14 @@ static void rna_KeyingSet_paths_clear(KeyingSet *keyingset, ReportList *reports)
/* needs wrapper function to push notifier */
static NlaTrack *rna_NlaTrack_new(ID *id, AnimData *adt, Main *bmain, bContext *C, NlaTrack *track)
{
NlaTrack *new_track = BKE_nlatrack_add(adt, track, ID_IS_OVERRIDE_LIBRARY(id));
NlaTrack *new_track;
if (track != NULL) {
new_track = BKE_nlatrack_new_after_and_set_active(
&adt->nla_tracks, track, ID_IS_OVERRIDE_LIBRARY(id));
}
else {
new_track = BKE_nlatrack_new_tail_and_set_active(&adt->nla_tracks, ID_IS_OVERRIDE_LIBRARY(id));
}
WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_ADDED, NULL);
@ -593,7 +600,7 @@ static void rna_NlaTrack_remove(
return;
}
BKE_nlatrack_free(&adt->nla_tracks, track, true);
BKE_nlatrack_remove_and_free(&adt->nla_tracks, track, true);
RNA_POINTER_INVALIDATE(track_ptr);
WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_REMOVED, NULL);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,44 @@
***************
*** 390,401 ****
strip->end += (start - strip->start);
strip->start = start;
- if (BKE_nlastrips_add_strip(&track->strips, strip) == 0) {
BKE_report(
reports,
RPT_ERROR,
"Unable to add strip (the track does not have any space to accommodate this new strip)");
- BKE_nlastrip_free(NULL, strip, true);
return NULL;
}
--- 390,401 ----
strip->end += (start - strip->start);
strip->start = start;
+ if (!BKE_nlastrips_try_add_strip(&track->strips, strip)) {
BKE_report(
reports,
RPT_ERROR,
"Unable to add strip (the track does not have any space to accommodate this new strip)");
+ BKE_nlastrip_free(strip, true);
return NULL;
}
***************
*** 446,452 ****
return;
}
- BKE_nlastrip_free(&track->strips, strip, true);
RNA_POINTER_INVALIDATE(strip_ptr);
WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_REMOVED, NULL);
--- 446,452 ----
return;
}
+ BKE_nlastrip_remove_and_free(&track->strips, strip, true);
RNA_POINTER_INVALIDATE(strip_ptr);
WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_REMOVED, NULL);

@ -1 +1 @@
Subproject commit e133fc08cd3254bb3d3bd1345028c8486700bca4
Subproject commit d50df97812e481c36ccae965e8fa3101ab8ab320