Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/space_nla/nla_edit.c')
-rw-r--r--source/blender/editors/space_nla/nla_edit.c1499
1 files changed, 1499 insertions, 0 deletions
diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c
new file mode 100644
index 00000000000..01871d3d9d5
--- /dev/null
+++ b/source/blender/editors/space_nla/nla_edit.c
@@ -0,0 +1,1499 @@
+/**
+ * $Id:
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung
+ * All rights reserved.
+ *
+ *
+ * Contributor(s): Joshua Leung (major recode)
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+
+#include "DNA_anim_types.h"
+#include "DNA_action_types.h"
+#include "DNA_nla_types.h"
+#include "DNA_object_types.h"
+#include "DNA_space_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_windowmanager_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_arithb.h"
+#include "BLI_rand.h"
+
+#include "BKE_animsys.h"
+#include "BKE_action.h"
+#include "BKE_fcurve.h"
+#include "BKE_nla.h"
+#include "BKE_context.h"
+#include "BKE_library.h"
+#include "BKE_main.h"
+#include "BKE_report.h"
+#include "BKE_screen.h"
+#include "BKE_utildefines.h"
+
+#include "ED_anim_api.h"
+#include "ED_keyframes_edit.h"
+#include "ED_markers.h"
+#include "ED_space_api.h"
+#include "ED_screen.h"
+#include "ED_transform.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+#include "RNA_enum_types.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+#include "UI_view2d.h"
+
+#include "nla_intern.h" // own include
+#include "nla_private.h" // FIXME... maybe this shouldn't be included?
+
+/* *********************************************** */
+/* 'Special' Editing */
+
+/* ******************** Tweak-Mode Operators ***************************** */
+/* 'Tweak mode' allows the action referenced by the active NLA-strip to be edited
+ * as if it were the normal Active-Action of its AnimData block.
+ */
+
+static int nlaedit_enable_tweakmode_exec (bContext *C, wmOperator *op)
+{
+ bAnimContext ac;
+
+ ListBase anim_data = {NULL, NULL};
+ bAnimListElem *ale;
+ int filter;
+ int ok=0;
+
+ /* 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_VISIBLE | ANIMFILTER_ANIMDATA);
+ ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
+
+ /* if no blocks, popup error? */
+ if (anim_data.first == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "No AnimData blocks to enter tweakmode for");
+ return OPERATOR_CANCELLED;
+ }
+
+ /* for each AnimData block with NLA-data, try setting it in tweak-mode */
+ for (ale= anim_data.first; ale; ale= ale->next) {
+ AnimData *adt= ale->data;
+
+ /* try entering tweakmode if valid */
+ ok += BKE_nla_tweakmode_enter(adt);
+ }
+
+ /* free temp data */
+ BLI_freelistN(&anim_data);
+
+ /* if we managed to enter tweakmode on at least one AnimData block,
+ * set the flag for this in the active scene and send notifiers
+ */
+ if (ac.scene && ok) {
+ /* set editing flag */
+ ac.scene->flag |= SCE_NLA_EDIT_ON;
+
+ /* set notifier that things have changed */
+ WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_ACTCHANGE, NULL);
+ }
+ else {
+ BKE_report(op->reports, RPT_ERROR, "No active strip(s) to enter tweakmode on.");
+ return OPERATOR_CANCELLED;
+ }
+
+ /* done */
+ return OPERATOR_FINISHED;
+}
+
+void NLA_OT_tweakmode_enter (wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Enter Tweak Mode";
+ ot->idname= "NLA_OT_tweakmode_enter";
+ ot->description= "Enter tweaking mode for the action referenced by the active strip.";
+
+ /* api callbacks */
+ ot->exec= nlaedit_enable_tweakmode_exec;
+ ot->poll= nlaop_poll_tweakmode_off;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/* ------------- */
+
+static int nlaedit_disable_tweakmode_exec (bContext *C, wmOperator *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_VISIBLE | ANIMFILTER_ANIMDATA);
+ ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
+
+ /* if no blocks, popup error? */
+ if (anim_data.first == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "No AnimData blocks to enter tweakmode for");
+ return OPERATOR_CANCELLED;
+ }
+
+ /* for each AnimData block with NLA-data, try exitting tweak-mode */
+ for (ale= anim_data.first; ale; ale= ale->next) {
+ AnimData *adt= ale->data;
+
+ /* try entering tweakmode if valid */
+ BKE_nla_tweakmode_exit(adt);
+ }
+
+ /* free temp data */
+ BLI_freelistN(&anim_data);
+
+ /* if we managed to enter tweakmode on at least one AnimData block,
+ * set the flag for this in the active scene and send notifiers
+ */
+ if (ac.scene) {
+ /* clear editing flag */
+ ac.scene->flag &= ~SCE_NLA_EDIT_ON;
+
+ /* set notifier that things have changed */
+ WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_ACTCHANGE, NULL);
+ }
+
+ /* done */
+ return OPERATOR_FINISHED;
+}
+
+void NLA_OT_tweakmode_exit (wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Exit Tweak Mode";
+ ot->idname= "NLA_OT_tweakmode_exit";
+ ot->description= "Exit tweaking mode for the action referenced by the active strip.";
+
+ /* api callbacks */
+ ot->exec= nlaedit_disable_tweakmode_exec;
+ ot->poll= nlaop_poll_tweakmode_on;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/* *********************************************** */
+/* NLA Editing Operations (Constructive/Destructive) */
+
+/* ******************** Add Action-Clip Operator ***************************** */
+/* Add a new Action-Clip strip to the active track (or the active block if no space in the track) */
+
+/* pop up menu allowing user to choose the action to use */
+static int nlaedit_add_actionclip_invoke (bContext *C, wmOperator *op, wmEvent *evt)
+{
+ Main *m= CTX_data_main(C);
+ bAction *act;
+ uiPopupMenu *pup;
+ uiLayout *layout;
+
+ pup= uiPupMenuBegin(C, "Add Action Clip", 0);
+ layout= uiPupMenuLayout(pup);
+
+ /* loop through Actions in Main database, adding as items in the menu */
+ for (act= m->action.first; act; act= act->id.next)
+ uiItemStringO(layout, act->id.name+2, 0, "NLA_OT_add_actionclip", "action", act->id.name);
+ uiItemS(layout);
+
+ uiPupMenuEnd(C, pup);
+
+ return OPERATOR_CANCELLED;
+}
+
+/* add the specified action as new strip */
+static int nlaedit_add_actionclip_exec (bContext *C, wmOperator *op)
+{
+ bAnimContext ac;
+ Scene *scene;
+
+ ListBase anim_data = {NULL, NULL};
+ bAnimListElem *ale;
+ int filter, items;
+
+ bAction *act = NULL;
+ char actname[22];
+ float cfra;
+
+ /* get editor data */
+ if (ANIM_animdata_get_context(C, &ac) == 0)
+ return OPERATOR_CANCELLED;
+
+ scene= ac.scene;
+ cfra= (float)CFRA;
+
+ /* get action to use */
+ RNA_string_get(op->ptr, "action", actname);
+ act= (bAction *)find_id("AC", actname+2);
+
+ if (act == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "No valid Action to add.");
+ //printf("Add strip - actname = '%s' \n", actname);
+ return OPERATOR_CANCELLED;
+ }
+
+ /* get a list of the editable tracks being shown in the NLA
+ * - this is limited to active ones for now, but could be expanded to
+ */
+ filter= (ANIMFILTER_VISIBLE | ANIMFILTER_ACTIVE | ANIMFILTER_NLATRACKS | ANIMFILTER_FOREDIT);
+ items= ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
+
+ if (items == 0) {
+ BKE_report(op->reports, RPT_ERROR, "No active track(s) to add strip to.");
+ return OPERATOR_CANCELLED;
+ }
+
+ /* for every active track, try to add strip to free space in track or to the top of the stack if no space */
+ for (ale= anim_data.first; ale; ale= ale->next) {
+ NlaTrack *nlt= (NlaTrack *)ale->data;
+ AnimData *adt= BKE_animdata_from_id(ale->id);
+ NlaStrip *strip= NULL;
+
+ /* create a new strip, and offset it to start on the current frame */
+ strip= add_nlastrip(act);
+
+ strip->end += (cfra - strip->start);
+ 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) == 0) {
+ /* trying to add to the current failed (no space),
+ * so add a new track to the stack, and add to that...
+ */
+ nlt= add_nlatrack(adt, NULL);
+ BKE_nlatrack_add_strip(nlt, strip);
+ }
+
+ /* auto-name it */
+ BKE_nlastrip_validate_name(adt, strip);
+ }
+
+ /* free temp data */
+ BLI_freelistN(&anim_data);
+
+ /* set notifier that things have changed */
+ WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_EDIT, NULL);
+
+ /* done */
+ return OPERATOR_FINISHED;
+}
+
+void NLA_OT_add_actionclip (wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Add Action Strip";
+ ot->idname= "NLA_OT_add_actionclip";
+ ot->description= "Add an Action-Clip strip (i.e. an NLA Strip referencing an Action) to the active track.";
+
+ /* api callbacks */
+ ot->invoke= nlaedit_add_actionclip_invoke;
+ ot->exec= nlaedit_add_actionclip_exec;
+ ot->poll= nlaop_poll_tweakmode_off;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* props */
+ // TODO: this would be nicer as an ID-pointer...
+ RNA_def_string(ot->srna, "action", "", 21, "Action", "Name of Action to add as a new Action-Clip Strip.");
+}
+
+/* ******************** Add Transition Operator ***************************** */
+/* Add a new transition strip between selected strips */
+
+static int nlaedit_add_transition_exec (bContext *C, wmOperator *op)
+{
+ bAnimContext ac;
+
+ ListBase anim_data = {NULL, NULL};
+ bAnimListElem *ale;
+ int filter;
+
+ int done = 0;
+
+ /* get editor data */
+ if (ANIM_animdata_get_context(C, &ac) == 0)
+ return OPERATOR_CANCELLED;
+
+ /* get a list of the editable tracks being shown in the NLA */
+ filter= (ANIMFILTER_VISIBLE | ANIMFILTER_NLATRACKS | ANIMFILTER_FOREDIT);
+ ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
+
+ /* for each track, find pairs of strips to add transitions to */
+ for (ale= anim_data.first; ale; ale= ale->next) {
+ NlaTrack *nlt= (NlaTrack *)ale->data;
+ AnimData *adt= BKE_animdata_from_id(ale->id);
+ NlaStrip *s1, *s2;
+
+ /* get initial pair of strips */
+ if ELEM(nlt->strips.first, NULL, nlt->strips.last)
+ continue;
+ s1= nlt->strips.first;
+ s2= s1->next;
+
+ /* loop over strips */
+ for (; s1 && s2; s1=s2, s2=s2->next) {
+ NlaStrip *strip;
+
+ /* check if both are selected */
+ if ELEM(0, (s1->flag & NLASTRIP_FLAG_SELECT), (s2->flag & NLASTRIP_FLAG_SELECT))
+ continue;
+ /* check if there's space between the two */
+ if (IS_EQ(s1->end, s2->start))
+ continue;
+ /* make neither one is a transition
+ * - although this is impossible to create with the standard tools,
+ * the user may have altered the settings
+ */
+ if (ELEM(NLASTRIP_TYPE_TRANSITION, s1->type, s2->type))
+ continue;
+
+ /* allocate new strip */
+ strip= MEM_callocN(sizeof(NlaStrip), "NlaStrip");
+ BLI_insertlinkafter(&nlt->strips, s1, strip);
+
+ /* set the type */
+ strip->type= NLASTRIP_TYPE_TRANSITION;
+
+ /* generic settings
+ * - selected flag to highlight this to the user
+ * - auto-blends to ensure that blend in/out values are automatically
+ * determined by overlaps of strips
+ */
+ strip->flag = NLASTRIP_FLAG_SELECT|NLASTRIP_FLAG_AUTO_BLENDS;
+
+ /* range is simply defined as the endpoints of the adjacent strips */
+ strip->start = s1->end;
+ strip->end = s2->start;
+
+ /* scale and repeat aren't of any use, but shouldn't ever be 0 */
+ strip->scale= 1.0f;
+ strip->repeat = 1.0f;
+
+ /* auto-name it */
+ BKE_nlastrip_validate_name(adt, strip);
+
+ /* make note of this */
+ done++;
+ }
+ }
+
+ /* free temp data */
+ BLI_freelistN(&anim_data);
+
+ /* was anything added? */
+ if (done) {
+ /* set notifier that things have changed */
+ WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_EDIT, NULL);
+
+ /* done */
+ return OPERATOR_FINISHED;
+ }
+ else {
+ BKE_report(op->reports, RPT_ERROR, "Needs at least a pair of adjacent selected strips with a gap between them.");
+ return OPERATOR_CANCELLED;
+ }
+}
+
+void NLA_OT_add_transition (wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Add Transition";
+ ot->idname= "NLA_OT_add_transition";
+ ot->description= "Add a transition strip between two adjacent selected strips.";
+
+ /* api callbacks */
+ ot->exec= nlaedit_add_transition_exec;
+ ot->poll= nlaop_poll_tweakmode_off;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/* ******************** Add Meta-Strip Operator ***************************** */
+/* Add new meta-strips incorporating the selected strips */
+
+/* add the specified action as new strip */
+static int nlaedit_add_meta_exec (bContext *C, wmOperator *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 editable tracks being shown in the NLA */
+ filter= (ANIMFILTER_VISIBLE | ANIMFILTER_NLATRACKS | ANIMFILTER_FOREDIT);
+ ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
+
+ /* for each track, find pairs of strips to add transitions to */
+ for (ale= anim_data.first; ale; ale= ale->next) {
+ NlaTrack *nlt= (NlaTrack *)ale->data;
+ AnimData *adt= BKE_animdata_from_id(ale->id);
+ NlaStrip *strip;
+
+ /* create meta-strips from the continuous chains of selected strips */
+ BKE_nlastrips_make_metas(&nlt->strips, 0);
+
+ /* name the metas */
+ for (strip= nlt->strips.first; strip; strip= strip->next) {
+ /* auto-name this strip if selected (that means it is a meta) */
+ if (strip->flag & NLASTRIP_FLAG_SELECT)
+ BKE_nlastrip_validate_name(adt, strip);
+ }
+ }
+
+ /* free temp data */
+ BLI_freelistN(&anim_data);
+
+ /* set notifier that things have changed */
+ WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_EDIT, NULL);
+
+ /* done */
+ return OPERATOR_FINISHED;
+}
+
+void NLA_OT_add_meta (wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Add Meta-Strips";
+ ot->idname= "NLA_OT_add_meta";
+ ot->description= "Add new meta-strips incorporating the selected strips.";
+
+ /* api callbacks */
+ ot->exec= nlaedit_add_meta_exec;
+ ot->poll= nlaop_poll_tweakmode_off;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/* ******************** Remove Meta-Strip Operator ***************************** */
+/* Separate out the strips held by the selected meta-strips */
+
+static int nlaedit_remove_meta_exec (bContext *C, wmOperator *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 editable tracks being shown in the NLA */
+ filter= (ANIMFILTER_VISIBLE | ANIMFILTER_NLATRACKS | ANIMFILTER_FOREDIT);
+ ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
+
+ /* for each track, find pairs of strips to add transitions to */
+ for (ale= anim_data.first; ale; ale= ale->next) {
+ NlaTrack *nlt= (NlaTrack *)ale->data;
+
+ /* clear all selected meta-strips, regardless of whether they are temporary or not */
+ BKE_nlastrips_clear_metas(&nlt->strips, 1, 0);
+ }
+
+ /* free temp data */
+ BLI_freelistN(&anim_data);
+
+ /* set notifier that things have changed */
+ WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_EDIT, NULL);
+
+ /* done */
+ return OPERATOR_FINISHED;
+}
+
+void NLA_OT_remove_meta (wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Remove Meta-Strips";
+ ot->idname= "NLA_OT_remove_meta";
+ ot->description= "Separate out the strips held by the selected meta-strips.";
+
+ /* api callbacks */
+ ot->exec= nlaedit_remove_meta_exec;
+ ot->poll= nlaop_poll_tweakmode_off;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/* ******************** Duplicate Strips Operator ************************** */
+/* Duplicates the selected NLA-Strips, putting them on new tracks above the one
+ * the originals were housed in.
+ */
+
+static int nlaedit_duplicate_exec (bContext *C, wmOperator *op)
+{
+ bAnimContext ac;
+
+ ListBase anim_data = {NULL, NULL};
+ bAnimListElem *ale;
+ int filter;
+
+ short done = 0;
+
+ /* get editor data */
+ if (ANIM_animdata_get_context(C, &ac) == 0)
+ return OPERATOR_CANCELLED;
+
+ /* get a list of editable tracks being shown in the NLA */
+ filter= (ANIMFILTER_VISIBLE | ANIMFILTER_NLATRACKS | ANIMFILTER_FOREDIT);
+ ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
+
+ /* duplicate strips in tracks starting from the last one so that we're
+ * less likely to duplicate strips we just duplicated...
+ */
+ for (ale= anim_data.last; ale; ale= ale->prev) {
+ NlaTrack *nlt= (NlaTrack *)ale->data;
+ AnimData *adt= BKE_animdata_from_id(ale->id);
+ NlaStrip *strip, *nstrip, *next;
+ NlaTrack *track;
+
+ for (strip= nlt->strips.first; strip; strip= next) {
+ next= strip->next;
+
+ /* if selected, split the strip at its midpoint */
+ if (strip->flag & NLASTRIP_FLAG_SELECT) {
+ /* make a copy (assume that this is possible) */
+ nstrip= copy_nlastrip(strip);
+
+ /* 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) == 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= add_nlatrack(adt, nlt->next);
+ BKE_nlatrack_add_strip(track, nstrip);
+ }
+
+ /* deselect the original and the active flag */
+ strip->flag &= ~(NLASTRIP_FLAG_SELECT|NLASTRIP_FLAG_ACTIVE);
+
+ /* auto-name it */
+ BKE_nlastrip_validate_name(adt, strip);
+
+ done++;
+ }
+ }
+ }
+
+ /* free temp data */
+ BLI_freelistN(&anim_data);
+
+ if (done) {
+ /* set notifier that things have changed */
+ WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_EDIT, NULL);
+
+ /* done */
+ return OPERATOR_FINISHED;
+ }
+ else
+ return OPERATOR_CANCELLED;
+}
+
+static int nlaedit_duplicate_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ nlaedit_duplicate_exec(C, op);
+
+ RNA_int_set(op->ptr, "mode", TFM_TIME_TRANSLATE); // XXX
+ WM_operator_name_call(C, "TFM_OT_transform", WM_OP_INVOKE_REGION_WIN, op->ptr);
+
+ return OPERATOR_FINISHED;
+}
+
+void NLA_OT_duplicate (wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Duplicate Strips";
+ ot->idname= "NLA_OT_duplicate";
+ ot->description= "Duplicate selected NLA-Strips, adding the new strips in new tracks above the originals.";
+
+ /* api callbacks */
+ ot->invoke= nlaedit_duplicate_invoke;
+ ot->exec= nlaedit_duplicate_exec;
+ ot->poll= nlaop_poll_tweakmode_off;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* to give to transform */
+ RNA_def_int(ot->srna, "mode", TFM_TRANSLATION, 0, INT_MAX, "Mode", "", 0, INT_MAX);
+}
+
+/* ******************** Delete Strips Operator ***************************** */
+/* Deletes the selected NLA-Strips */
+
+static int nlaedit_delete_exec (bContext *C, wmOperator *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 editable tracks being shown in the NLA */
+ filter= (ANIMFILTER_VISIBLE | ANIMFILTER_NLATRACKS | ANIMFILTER_FOREDIT);
+ ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
+
+ /* for each NLA-Track, delete all selected strips */
+ for (ale= anim_data.first; ale; ale= ale->next) {
+ NlaTrack *nlt= (NlaTrack *)ale->data;
+ NlaStrip *strip, *nstrip;
+
+ for (strip= nlt->strips.first; strip; strip= nstrip) {
+ nstrip= strip->next;
+
+ /* if selected, delete */
+ 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))
+ free_nlastrip(&nlt->strips, strip->prev);
+ if ((nstrip) && (nstrip->type == NLASTRIP_TYPE_TRANSITION)) {
+ nstrip= nstrip->next;
+ free_nlastrip(&nlt->strips, strip->next);
+ }
+
+ /* finally, delete this strip */
+ free_nlastrip(&nlt->strips, strip);
+ }
+ }
+ }
+
+ /* free temp data */
+ BLI_freelistN(&anim_data);
+
+ /* set notifier that things have changed */
+ WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_EDIT, NULL);
+
+ /* done */
+ return OPERATOR_FINISHED;
+}
+
+void NLA_OT_delete (wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Delete Strips";
+ ot->idname= "NLA_OT_delete";
+ ot->description= "Delete selected strips.";
+
+ /* api callbacks */
+ ot->exec= nlaedit_delete_exec;
+ ot->poll= nlaop_poll_tweakmode_off;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/* ******************** Split Strips Operator ***************************** */
+/* Splits the selected NLA-Strips into two strips at the midpoint of the strip */
+// TODO's?
+// - multiple splits
+// - variable-length splits?
+
+/* split a given Action-Clip strip */
+static void nlaedit_split_strip_actclip (AnimData *adt, NlaTrack *nlt, NlaStrip *strip, float cfra)
+{
+ NlaStrip *nstrip;
+ float splitframe, splitaframe;
+
+ /* calculate the frames to do the splitting at
+ * - use current frame if within extents of strip
+ */
+ if ((cfra > strip->start) && (cfra < strip->end)) {
+ /* use the current frame */
+ splitframe= cfra;
+ splitaframe= nlastrip_get_frame(strip, cfra, NLATIME_CONVERT_UNMAP);
+ }
+ else {
+ /* split in the middle */
+ float len;
+
+ /* strip extents */
+ len= strip->end - strip->start;
+ if (IS_EQ(len, 0.0f))
+ return;
+ else
+ splitframe= strip->start + (len / 2.0f);
+
+ /* action range */
+ len= strip->actend - strip->actstart;
+ if (IS_EQ(len, 0.0f))
+ splitaframe= strip->actend;
+ else
+ splitaframe= strip->actstart + (len / 2.0f);
+ }
+
+ /* make a copy (assume that this is possible) and append
+ * it immediately after the current strip
+ */
+ nstrip= copy_nlastrip(strip);
+ BLI_insertlinkafter(&nlt->strips, strip, nstrip);
+
+ /* set the endpoint of the first strip and the start of the new strip
+ * to the splitframe values calculated above
+ */
+ strip->end= splitframe;
+ nstrip->start= splitframe;
+
+ if ((splitaframe > strip->actstart) && (splitaframe < strip->actend)) {
+ /* only do this if we're splitting down the middle... */
+ strip->actend= splitaframe;
+ nstrip->actstart= splitaframe;
+ }
+
+ /* clear the active flag from the copy */
+ nstrip->flag &= ~NLASTRIP_FLAG_ACTIVE;
+
+ /* auto-name the new strip */
+ BKE_nlastrip_validate_name(adt, nstrip);
+}
+
+/* split a given Meta strip */
+static void nlaedit_split_strip_meta (AnimData *adt, NlaTrack *nlt, NlaStrip *strip)
+{
+ /* simply ungroup it for now... */
+ BKE_nlastrips_clear_metastrip(&nlt->strips, strip);
+}
+
+/* ----- */
+
+static int nlaedit_split_exec (bContext *C, wmOperator *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 editable tracks being shown in the NLA */
+ filter= (ANIMFILTER_VISIBLE | ANIMFILTER_NLATRACKS | ANIMFILTER_FOREDIT);
+ ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
+
+ /* for each NLA-Track, split all selected strips into two strips */
+ for (ale= anim_data.first; ale; ale= ale->next) {
+ NlaTrack *nlt= (NlaTrack *)ale->data;
+ AnimData *adt= BKE_animdata_from_id(ale->id);
+ NlaStrip *strip, *next;
+
+ for (strip= nlt->strips.first; strip; strip= next) {
+ next= strip->next;
+
+ /* if selected, split the strip at its midpoint */
+ if (strip->flag & NLASTRIP_FLAG_SELECT) {
+ /* splitting method depends on the type of strip */
+ switch (strip->type) {
+ case NLASTRIP_TYPE_CLIP: /* action-clip */
+ nlaedit_split_strip_actclip(adt, nlt, strip, (float)ac.scene->r.cfra);
+ break;
+
+ case NLASTRIP_TYPE_META: /* meta-strips need special handling */
+ nlaedit_split_strip_meta(adt, nlt, strip);
+ break;
+
+ default: /* for things like Transitions, do not split! */
+ break;
+ }
+ }
+ }
+ }
+
+ /* free temp data */
+ BLI_freelistN(&anim_data);
+
+ /* set notifier that things have changed */
+ WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_EDIT, NULL);
+
+ /* done */
+ return OPERATOR_FINISHED;
+}
+
+void NLA_OT_split (wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Split Strips";
+ ot->idname= "NLA_OT_split";
+ ot->description= "Split selected strips at their midpoints.";
+
+ /* api callbacks */
+ ot->exec= nlaedit_split_exec;
+ ot->poll= nlaop_poll_tweakmode_off;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/* *********************************************** */
+/* NLA Editing Operations (Modifying) */
+
+/* ******************** Toggle Muting Operator ************************** */
+/* Toggles whether strips are muted or not */
+
+static int nlaedit_toggle_mute_exec (bContext *C, wmOperator *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 editable tracks being shown in the NLA */
+ filter= (ANIMFILTER_VISIBLE | ANIMFILTER_NLATRACKS | ANIMFILTER_FOREDIT);
+ ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
+
+ /* go over all selected strips */
+ for (ale= anim_data.first; ale; ale= ale->next) {
+ NlaTrack *nlt= (NlaTrack *)ale->data;
+ NlaStrip *strip;
+
+ /* for every selected strip, toggle muting */
+ for (strip= nlt->strips.first; strip; strip= strip->next) {
+ if (strip->flag & NLASTRIP_FLAG_SELECT) {
+ /* just flip the mute flag for now */
+ // TODO: have a pre-pass to check if mute all or unmute all?
+ strip->flag ^= NLASTRIP_FLAG_MUTED;
+ }
+ }
+ }
+
+ /* free temp data */
+ BLI_freelistN(&anim_data);
+
+ /* set notifier that things have changed */
+ WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_EDIT, NULL);
+
+ /* done */
+ return OPERATOR_FINISHED;
+}
+
+void NLA_OT_mute_toggle (wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Toggle Muting";
+ ot->idname= "NLA_OT_mute_toggle";
+ ot->description= "Mute or un-muted selected strips.";
+
+ /* api callbacks */
+ ot->exec= nlaedit_toggle_mute_exec;
+ ot->poll= nlaop_poll_tweakmode_off;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/* ******************** Move Strips Up Operator ************************** */
+/* Tries to move the selected strips into the track above if possible. */
+
+static int nlaedit_move_up_exec (bContext *C, wmOperator *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 editable tracks being shown in the NLA */
+ filter= (ANIMFILTER_VISIBLE | ANIMFILTER_NLATRACKS | ANIMFILTER_FOREDIT);
+ ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
+
+ /* since we're potentially moving strips from lower tracks to higher tracks, we should
+ * loop over the tracks in reverse order to avoid moving earlier strips up multiple tracks
+ */
+ for (ale= anim_data.last; ale; ale= ale->prev) {
+ NlaTrack *nlt= (NlaTrack *)ale->data;
+ NlaTrack *nltn= nlt->next;
+ NlaStrip *strip, *stripn;
+
+ /* if this track has no tracks after it, skip for now... */
+ if (nltn == NULL)
+ continue;
+
+ /* for every selected strip, try to move */
+ for (strip= nlt->strips.first; strip; strip= stripn) {
+ stripn= strip->next;
+
+ if (strip->flag & NLASTRIP_FLAG_SELECT) {
+ /* check if the track above has room for this strip */
+ 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);
+ }
+ }
+ }
+ }
+
+ /* free temp data */
+ BLI_freelistN(&anim_data);
+
+ /* set notifier that things have changed */
+ WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_EDIT, NULL);
+
+ /* done */
+ return OPERATOR_FINISHED;
+}
+
+void NLA_OT_move_up (wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Move Strips Up";
+ ot->idname= "NLA_OT_move_up";
+ ot->description= "Move selected strips up a track if there's room.";
+
+ /* api callbacks */
+ ot->exec= nlaedit_move_up_exec;
+ ot->poll= nlaop_poll_tweakmode_off;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/* ******************** Move Strips Down Operator ************************** */
+/* Tries to move the selected strips into the track above if possible. */
+
+static int nlaedit_move_down_exec (bContext *C, wmOperator *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 editable tracks being shown in the NLA */
+ filter= (ANIMFILTER_VISIBLE | ANIMFILTER_NLATRACKS | ANIMFILTER_FOREDIT);
+ ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
+
+ /* loop through the tracks in normal order, since we're pushing strips down,
+ * strips won't get operated on twice
+ */
+ for (ale= anim_data.first; ale; ale= ale->next) {
+ NlaTrack *nlt= (NlaTrack *)ale->data;
+ NlaTrack *nltp= nlt->prev;
+ NlaStrip *strip, *stripn;
+
+ /* if this track has no tracks before it, skip for now... */
+ if (nltp == NULL)
+ continue;
+
+ /* for every selected strip, try to move */
+ for (strip= nlt->strips.first; strip; strip= stripn) {
+ stripn= strip->next;
+
+ if (strip->flag & NLASTRIP_FLAG_SELECT) {
+ /* check if the track below has room for this strip */
+ 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);
+ }
+ }
+ }
+ }
+
+ /* free temp data */
+ BLI_freelistN(&anim_data);
+
+ /* set notifier that things have changed */
+ WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_EDIT, NULL);
+
+ /* done */
+ return OPERATOR_FINISHED;
+}
+
+void NLA_OT_move_down (wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Move Strips Down";
+ ot->idname= "NLA_OT_move_down";
+ ot->description= "Move selected strips down a track if there's room.";
+
+ /* api callbacks */
+ ot->exec= nlaedit_move_down_exec;
+ ot->poll= nlaop_poll_tweakmode_off;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/* ******************** Apply Scale Operator ***************************** */
+/* Reset the scaling of the selected strips to 1.0f */
+
+/* apply scaling to keyframe */
+static short bezt_apply_nlamapping (BeztEditData *bed, BezTriple *bezt)
+{
+ /* NLA-strip which has this scaling is stored in bed->data */
+ NlaStrip *strip= (NlaStrip *)bed->data;
+
+ /* adjust all the times */
+ bezt->vec[0][0]= nlastrip_get_frame(strip, bezt->vec[0][0], NLATIME_CONVERT_MAP);
+ bezt->vec[1][0]= nlastrip_get_frame(strip, bezt->vec[1][0], NLATIME_CONVERT_MAP);
+ bezt->vec[2][0]= nlastrip_get_frame(strip, bezt->vec[2][0], NLATIME_CONVERT_MAP);
+
+ /* nothing to return or else we exit */
+ return 0;
+}
+
+static int nlaedit_apply_scale_exec (bContext *C, wmOperator *op)
+{
+ bAnimContext ac;
+
+ ListBase anim_data = {NULL, NULL};
+ bAnimListElem *ale;
+ int filter;
+
+ BeztEditData bed;
+
+ /* get editor data */
+ if (ANIM_animdata_get_context(C, &ac) == 0)
+ return OPERATOR_CANCELLED;
+
+ /* get a list of the editable tracks being shown in the NLA */
+ filter= (ANIMFILTER_VISIBLE | ANIMFILTER_NLATRACKS | ANIMFILTER_FOREDIT);
+ ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
+
+ /* init the editing data */
+ memset(&bed, 0, sizeof(BeztEditData));
+
+ /* for each NLA-Track, apply scale of all selected strips */
+ for (ale= anim_data.first; ale; ale= ale->next) {
+ NlaTrack *nlt= (NlaTrack *)ale->data;
+ NlaStrip *strip;
+
+ for (strip= nlt->strips.first; strip; strip= strip->next) {
+ /* strip must be selected, and must be action-clip only (transitions don't have scale) */
+ if ((strip->flag & NLASTRIP_FLAG_SELECT) && (strip->type == NLASTRIP_TYPE_CLIP)) {
+ /* if the referenced action is used by other strips, make this strip use its own copy */
+ if (strip->act == NULL)
+ continue;
+ if (strip->act->id.us > 1) {
+ /* make a copy of the Action to work on */
+ bAction *act= copy_action(strip->act);
+
+ /* set this as the new referenced action, decrementing the users of the old one */
+ strip->act->id.us--;
+ strip->act= act;
+ }
+
+ /* setup iterator, and iterate over all the keyframes in the action, applying this scaling */
+ bed.data= strip;
+ ANIM_animchanneldata_keys_bezier_loop(&bed, strip->act, ALE_ACT, NULL, bezt_apply_nlamapping, calchandles_fcurve, 0);
+
+ /* clear scale of strip now that it has been applied,
+ * and recalculate the extents of the action now that it has been scaled
+ * but leave everything else alone
+ */
+ strip->scale= 1.0f;
+ calc_action_range(strip->act, &strip->actstart, &strip->actend, 1);
+ }
+ }
+ }
+
+ /* free temp data */
+ BLI_freelistN(&anim_data);
+
+ /* set notifier that things have changed */
+ WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_EDIT, NULL);
+
+ /* done */
+ return OPERATOR_FINISHED;
+}
+
+void NLA_OT_apply_scale (wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Apply Scale";
+ ot->idname= "NLA_OT_apply_scale";
+ ot->description= "Apply scaling of selected strips to their referenced Actions.";
+
+ /* api callbacks */
+ ot->exec= nlaedit_apply_scale_exec;
+ ot->poll= nlaop_poll_tweakmode_off;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/* ******************** Clear Scale Operator ***************************** */
+/* Reset the scaling of the selected strips to 1.0f */
+
+static int nlaedit_clear_scale_exec (bContext *C, wmOperator *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 editable tracks being shown in the NLA */
+ filter= (ANIMFILTER_VISIBLE | ANIMFILTER_NLATRACKS | ANIMFILTER_FOREDIT);
+ ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
+
+ /* for each NLA-Track, reset scale of all selected strips */
+ for (ale= anim_data.first; ale; ale= ale->next) {
+ NlaTrack *nlt= (NlaTrack *)ale->data;
+ NlaStrip *strip;
+
+ for (strip= nlt->strips.first; strip; strip= strip->next) {
+ /* strip must be selected, and must be action-clip only (transitions don't have scale) */
+ if ((strip->flag & NLASTRIP_FLAG_SELECT) && (strip->type == NLASTRIP_TYPE_CLIP)) {
+ PointerRNA strip_ptr;
+
+ RNA_pointer_create(NULL, &RNA_NlaStrip, strip, &strip_ptr);
+ RNA_float_set(&strip_ptr, "scale", 1.0f);
+ }
+ }
+ }
+
+ /* free temp data */
+ BLI_freelistN(&anim_data);
+
+ /* set notifier that things have changed */
+ WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_EDIT, NULL);
+
+ /* done */
+ return OPERATOR_FINISHED;
+}
+
+void NLA_OT_clear_scale (wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Clear Scale";
+ ot->idname= "NLA_OT_clear_scale";
+ ot->description= "Reset scaling of selected strips.";
+
+ /* api callbacks */
+ ot->exec= nlaedit_clear_scale_exec;
+ ot->poll= nlaop_poll_tweakmode_off;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/* ******************** Snap Strips Operator ************************** */
+/* Moves the start-point of the selected strips to the specified places */
+
+/* defines for snap keyframes tool */
+EnumPropertyItem prop_nlaedit_snap_types[] = {
+ {NLAEDIT_SNAP_CFRA, "CFRA", 0, "Current frame", ""},
+ {NLAEDIT_SNAP_NEAREST_FRAME, "NEAREST_FRAME", 0, "Nearest Frame", ""}, // XXX as single entry?
+ {NLAEDIT_SNAP_NEAREST_SECOND, "NEAREST_SECOND", 0, "Nearest Second", ""}, // XXX as single entry?
+ {NLAEDIT_SNAP_NEAREST_MARKER, "NEAREST_MARKER", 0, "Nearest Marker", ""},
+ {0, NULL, 0, NULL, NULL}
+};
+
+static int nlaedit_snap_exec (bContext *C, wmOperator *op)
+{
+ bAnimContext ac;
+
+ ListBase anim_data = {NULL, NULL};
+ bAnimListElem *ale;
+ int filter;
+
+ Scene *scene;
+ int mode = RNA_enum_get(op->ptr, "type");
+ float secf;
+
+ /* get editor data */
+ if (ANIM_animdata_get_context(C, &ac) == 0)
+ return OPERATOR_CANCELLED;
+
+ /* get a list of the editable tracks being shown in the NLA */
+ filter= (ANIMFILTER_VISIBLE | ANIMFILTER_NLATRACKS | ANIMFILTER_FOREDIT);
+ ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
+
+ /* get some necessary vars */
+ scene= ac.scene;
+ secf= (float)FPS;
+
+ /* since we may add tracks, perform this in reverse order */
+ for (ale= anim_data.last; ale; ale= ale->prev) {
+ ListBase tmp_strips = {NULL, NULL};
+ AnimData *adt= BKE_animdata_from_id(ale->id);
+ NlaTrack *nlt= (NlaTrack *)ale->data;
+ NlaStrip *strip, *stripn;
+ NlaTrack *track;
+
+ /* create meta-strips from the continuous chains of selected strips */
+ BKE_nlastrips_make_metas(&nlt->strips, 1);
+
+ /* apply the snapping to all the temp meta-strips, then put them in a separate list to be added
+ * back to the original only if they still fit
+ */
+ for (strip= nlt->strips.first; strip; strip= stripn) {
+ stripn= strip->next;
+
+ if (strip->flag & NLASTRIP_FLAG_TEMP_META) {
+ float start, end;
+
+ /* get the existing end-points */
+ start= strip->start;
+ end= strip->end;
+
+ /* calculate new start position based on snapping mode */
+ switch (mode) {
+ case NLAEDIT_SNAP_CFRA: /* to current frame */
+ strip->start= (float)CFRA;
+ break;
+ case NLAEDIT_SNAP_NEAREST_FRAME: /* to nearest frame */
+ strip->start= (float)(floor(start+0.5));
+ break;
+ case NLAEDIT_SNAP_NEAREST_SECOND: /* to nearest second */
+ strip->start= ((float)floor(start/secf + 0.5f) * secf);
+ break;
+ case NLAEDIT_SNAP_NEAREST_MARKER: /* to nearest marker */
+ strip->start= (float)ED_markers_find_nearest_marker_time(ac.markers, start);
+ break;
+ default: /* just in case... no snapping */
+ strip->start= start;
+ break;
+ }
+
+ /* get new endpoint based on start-point (and old length) */
+ strip->end= strip->start + (end - start);
+
+ /* apply transforms to meta-strip to its children */
+ BKE_nlameta_flush_transforms(strip);
+
+ /* remove strip from track, and add to the temp buffer */
+ BLI_remlink(&nlt->strips, strip);
+ BLI_addtail(&tmp_strips, strip);
+ }
+ }
+
+ /* try adding each meta-strip back to the track one at a time, to make sure they'll fit */
+ for (strip= tmp_strips.first; strip; strip= stripn) {
+ stripn= strip->next;
+
+ /* remove from temp-strips list */
+ BLI_remlink(&tmp_strips, strip);
+
+ /* in case there's no space in the current track, try adding */
+ if (BKE_nlatrack_add_strip(nlt, strip) == 0) {
+ /* need to add a new track above the current one */
+ track= add_nlatrack(adt, nlt);
+ 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 */
+ BKE_nlastrips_clear_metas(&track->strips, 0, 1);
+ }
+ }
+
+ /* remove the meta-strips now that we're done */
+ BKE_nlastrips_clear_metas(&nlt->strips, 0, 1);
+ }
+
+ /* free temp data */
+ BLI_freelistN(&anim_data);
+
+ /* set notifier that things have changed */
+ WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_EDIT, NULL);
+
+ /* done */
+ return OPERATOR_FINISHED;
+}
+
+void NLA_OT_snap (wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Snap Strips";
+ ot->idname= "NLA_OT_snap";
+ ot->description= "Move start of strips to specified time.";
+
+ /* api callbacks */
+ ot->invoke= WM_menu_invoke;
+ ot->exec= nlaedit_snap_exec;
+ ot->poll= nlaop_poll_tweakmode_off;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_enum(ot->srna, "type", prop_nlaedit_snap_types, 0, "Type", "");
+}
+
+/* *********************************************** */
+/* NLA Modifiers */
+
+/* ******************** Add F-Modifier Operator *********************** */
+
+/* present a special customised popup menu for this, with some filtering */
+static int nla_fmodifier_add_invoke (bContext *C, wmOperator *op, wmEvent *event)
+{
+ uiPopupMenu *pup;
+ uiLayout *layout;
+ int i;
+
+ pup= uiPupMenuBegin(C, "Add F-Modifier", 0);
+ layout= uiPupMenuLayout(pup);
+
+ /* start from 1 to skip the 'Invalid' modifier type */
+ for (i = 1; i < FMODIFIER_NUM_TYPES; i++) {
+ FModifierTypeInfo *fmi= get_fmodifier_typeinfo(i);
+
+ /* check if modifier is valid for this context */
+ if (fmi == NULL)
+ continue;
+ if (i == FMODIFIER_TYPE_CYCLES) /* we already have repeat... */
+ continue;
+
+ /* add entry to add this type of modifier */
+ uiItemEnumO(layout, fmi->name, 0, "NLA_OT_fmodifier_add", "type", i);
+ }
+ uiItemS(layout);
+
+ uiPupMenuEnd(C, pup);
+
+ return OPERATOR_CANCELLED;
+}
+
+static int nla_fmodifier_add_exec(bContext *C, wmOperator *op)
+{
+ bAnimContext ac;
+
+ ListBase anim_data = {NULL, NULL};
+ bAnimListElem *ale;
+ int filter;
+
+ FModifier *fcm;
+ int type= RNA_enum_get(op->ptr, "type");
+ short onlyActive = RNA_boolean_get(op->ptr, "only_active");
+
+ /* get editor data */
+ if (ANIM_animdata_get_context(C, &ac) == 0)
+ return OPERATOR_CANCELLED;
+
+ /* get a list of the editable tracks being shown in the NLA */
+ filter= (ANIMFILTER_VISIBLE | ANIMFILTER_NLATRACKS | ANIMFILTER_FOREDIT);
+ ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
+
+ /* for each NLA-Track, add the specified modifier to all selected strips */
+ for (ale= anim_data.first; ale; ale= ale->next) {
+ NlaTrack *nlt= (NlaTrack *)ale->data;
+ NlaStrip *strip;
+ int i = 1;
+
+ for (strip= nlt->strips.first; strip; strip=strip->next, i++) {
+ /* only add F-Modifier if on active strip? */
+ if ((onlyActive) && (strip->flag & NLASTRIP_FLAG_ACTIVE)==0)
+ continue;
+
+ /* add F-Modifier of specified type to selected, and make it the active one */
+ fcm= add_fmodifier(&strip->modifiers, type);
+
+ if (fcm)
+ set_active_fmodifier(&strip->modifiers, fcm);
+ else {
+ char errormsg[128];
+ sprintf(errormsg, "Modifier couldn't be added to (%s : %d). See console for details.", nlt->name, i);
+
+ BKE_report(op->reports, RPT_ERROR, errormsg);
+ }
+ }
+ }
+
+ /* free temp data */
+ BLI_freelistN(&anim_data);
+
+ /* set notifier that things have changed */
+ // FIXME: this doesn't really do it justice...
+ WM_event_add_notifier(C, NC_ANIMATION, NULL);
+
+ /* done */
+ return OPERATOR_FINISHED;
+}
+
+void NLA_OT_fmodifier_add (wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Add F-Modifier";
+ ot->idname= "NLA_OT_fmodifier_add";
+
+ /* api callbacks */
+ ot->invoke= nla_fmodifier_add_invoke;
+ ot->exec= nla_fmodifier_add_exec;
+ ot->poll= nlaop_poll_tweakmode_off;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* id-props */
+ RNA_def_enum(ot->srna, "type", fmodifier_type_items, 0, "Type", "");
+ RNA_def_boolean(ot->srna, "only_active", 0, "Only Active", "Only add F-Modifier of the specified type to the active strip.");
+}
+
+/* *********************************************** */