NLA SoC: Merge from 2.5 - 21146 to 21178
[blender.git] / source / blender / blenkernel / intern / nla.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung
21  * All rights reserved.
22  *
23  * The Original Code is: all of this file.
24  *
25  * Contributor(s): Joshua Leung (full recode)
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  */
29
30 #include <stdlib.h>
31 #include <stddef.h>
32 #include <stdio.h>
33 #include <math.h>
34 #include <float.h>
35
36 #include "MEM_guardedalloc.h"
37
38 #include "BLI_blenlib.h"
39
40 #include "DNA_anim_types.h"
41 #include "DNA_action_types.h"
42
43 #include "BKE_animsys.h"
44 #include "BKE_action.h"
45 #include "BKE_fcurve.h"
46 #include "BKE_nla.h"
47 #include "BKE_blender.h"
48 #include "BKE_library.h"
49 #include "BKE_object.h"
50 #include "BKE_utildefines.h"
51
52 #include "RNA_access.h"
53 #include "nla_private.h"
54
55
56 #ifdef HAVE_CONFIG_H
57 #include <config.h>
58 #endif
59
60
61 /* *************************************************** */
62 /* Data Management */
63
64 /* Freeing ------------------------------------------- */
65
66 /* Remove the given NLA strip from the NLA track it occupies, free the strip's data,
67  * and the strip itself. 
68  */
69 // TODO: with things like transitions, should these get freed too? Maybe better as a UI tool
70 void free_nlastrip (ListBase *strips, NlaStrip *strip)
71 {
72         FModifier *fcm, *fmn;
73         
74         /* sanity checks */
75         if (strip == NULL)
76                 return;
77                 
78         /* remove reference to action */
79         if (strip->act)
80                 strip->act->id.us--;
81                 
82         /* free remapping info */
83         //if (strip->remap)
84         //      BKE_animremap_free();
85         
86         /* free own F-Curves */
87         free_fcurves(&strip->fcurves);
88         
89         /* free F-Modifiers */
90         for (fcm= strip->modifiers.first; fcm; fcm= fmn) {
91                 fmn= fcm->next;
92                 
93                 BLI_remlink(&strip->modifiers, fcm);
94                 fcurve_remove_modifier(NULL, fcm);
95         }
96         
97         /* free the strip itself */
98         if (strips)
99                 BLI_freelinkN(strips, strip);
100         else
101                 MEM_freeN(strip);
102 }
103
104 /* Remove the given NLA track from the set of NLA tracks, free the track's data,
105  * and the track itself.
106  */
107 void free_nlatrack (ListBase *tracks, NlaTrack *nlt)
108 {
109         NlaStrip *strip, *stripn;
110         
111         /* sanity checks */
112         if (nlt == NULL)
113                 return;
114                 
115         /* free strips */
116         for (strip= nlt->strips.first; strip; strip= stripn) {
117                 stripn= strip->next;
118                 free_nlastrip(&nlt->strips, strip);
119         }
120         
121         /* free NLA track itself now */
122         if (tracks)
123                 BLI_freelinkN(tracks, nlt);
124         else
125                 MEM_freeN(nlt);
126 }
127
128 /* Free the elements of type NLA Tracks provided in the given list, but do not free
129  * the list itself since that is not free-standing
130  */
131 void free_nladata (ListBase *tracks)
132 {
133         NlaTrack *nlt, *nltn;
134         
135         /* sanity checks */
136         if ELEM(NULL, tracks, tracks->first)
137                 return;
138                 
139         /* free tracks one by one */
140         for (nlt= tracks->first; nlt; nlt= nltn) {
141                 nltn= nlt->next;
142                 free_nlatrack(tracks, nlt);
143         }
144         
145         /* clear the list's pointers to be safe */
146         tracks->first= tracks->last= NULL;
147 }
148
149 /* Copying ------------------------------------------- */
150
151 /* Copy NLA strip */
152 NlaStrip *copy_nlastrip (NlaStrip *strip)
153 {
154         NlaStrip *strip_d;
155         
156         /* sanity check */
157         if (strip == NULL)
158                 return NULL;
159                 
160         /* make a copy */
161         strip_d= MEM_dupallocN(strip);
162         strip_d->next= strip_d->prev= NULL;
163         
164         /* increase user-count of action */
165         if (strip_d->act)
166                 strip_d->act->id.us++;
167                 
168         /* copy F-Curves and modifiers */
169         copy_fcurves(&strip_d->fcurves, &strip->fcurves);
170         fcurve_copy_modifiers(&strip_d->modifiers, &strip->modifiers);
171         
172         /* return the strip */
173         return strip_d;
174 }
175
176 /* Copy NLA Track */
177 NlaTrack *copy_nlatrack (NlaTrack *nlt)
178 {
179         NlaStrip *strip, *strip_d;
180         NlaTrack *nlt_d;
181         
182         /* sanity check */
183         if (nlt == NULL)
184                 return NULL;
185                 
186         /* make a copy */
187         nlt_d= MEM_dupallocN(nlt);
188         nlt_d->next= nlt_d->prev= NULL;
189         
190         /* make a copy of all the strips, one at a time */
191         nlt_d->strips.first= nlt_d->strips.last= NULL;
192         
193         for (strip= nlt->strips.first; strip; strip= strip->next) {
194                 strip_d= copy_nlastrip(strip);
195                 BLI_addtail(&nlt_d->strips, strip_d);
196         }
197         
198         /* return the copy */
199         return nlt_d;
200 }
201
202 /* Copy all NLA data */
203 void copy_nladata (ListBase *dst, ListBase *src)
204 {
205         NlaTrack *nlt, *nlt_d;
206         
207         /* sanity checks */
208         if ELEM(NULL, dst, src)
209                 return;
210                 
211         /* copy each NLA-track, one at a time */
212         for (nlt= src->first; nlt; nlt= nlt->next) {
213                 /* make a copy, and add the copy to the destination list */
214                 nlt_d= copy_nlatrack(nlt);
215                 BLI_addtail(dst, nlt_d);
216         }
217 }
218
219 /* Adding ------------------------------------------- */
220
221 /* Add a NLA Track to the given AnimData 
222  *      - prev: NLA-Track to add the new one after
223  */
224 NlaTrack *add_nlatrack (AnimData *adt, NlaTrack *prev)
225 {
226         NlaTrack *nlt;
227         
228         /* sanity checks */
229         if (adt == NULL)
230                 return NULL;
231                 
232         /* allocate new track */
233         nlt= MEM_callocN(sizeof(NlaTrack), "NlaTrack");
234         
235         /* set settings requiring the track to not be part of the stack yet */
236         nlt->flag = NLATRACK_SELECTED;
237         nlt->index= BLI_countlist(&adt->nla_tracks);
238         
239         /* add track to stack, and make it the active one */
240         if (prev)
241                 BLI_insertlinkafter(&adt->nla_tracks, prev, nlt);
242         else
243                 BLI_addtail(&adt->nla_tracks, nlt);
244         BKE_nlatrack_set_active(&adt->nla_tracks, nlt);
245         
246         /* must have unique name, but we need to seed this */
247         sprintf(nlt->name, "NlaTrack");
248         BLI_uniquename(&adt->nla_tracks, nlt, "NlaTrack", '.', offsetof(NlaTrack, name), 64);
249         
250         /* return the new track */
251         return nlt;
252 }
253
254 /* Add a NLA Strip referencing the given Action */
255 NlaStrip *add_nlastrip (bAction *act)
256 {
257         NlaStrip *strip;
258         
259         /* sanity checks */
260         if (act == NULL)
261                 return NULL;
262                 
263         /* allocate new strip */
264         strip= MEM_callocN(sizeof(NlaStrip), "NlaStrip");
265         
266         /* generic settings 
267          *      - selected flag to highlight this to the user
268          *      - auto-blends to ensure that blend in/out values are automatically 
269          *        determined by overlaps of strips
270          *      - (XXX) synchronisation of strip-length in accordance with changes to action-length
271          *        is not done though, since this should only really happens in editmode for strips now
272          *        though this decision is still subject to further review...
273          */
274         strip->flag = NLASTRIP_FLAG_SELECT|NLASTRIP_FLAG_AUTO_BLENDS;
275         
276         /* assign the action reference */
277         strip->act= act;
278         id_us_plus(&act->id);
279         
280         /* determine initial range 
281          *      - strip length cannot be 0... ever...
282          */
283         calc_action_range(strip->act, &strip->actstart, &strip->actend, 1);
284         
285         strip->start = strip->actstart;
286         strip->end = (IS_EQ(strip->actstart, strip->actend)) ?  (strip->actstart + 1.0f): (strip->actend);
287         
288         /* strip should be referenced as-is */
289         strip->scale= 1.0f;
290         strip->repeat = 1.0f;
291         
292         /* return the new strip */
293         return strip;
294 }
295
296 /* Add new NLA-strip to the top of the NLA stack - i.e. into the last track if space, or a new one otherwise */
297 NlaStrip *add_nlastrip_to_stack (AnimData *adt, bAction *act)
298 {
299         NlaStrip *strip;
300         NlaTrack *nlt;
301         
302         /* sanity checks */
303         if ELEM(NULL, adt, act)
304                 return NULL;
305         
306         /* create a new NLA strip */
307         strip= add_nlastrip(act);
308         if (strip == NULL)
309                 return NULL;
310         
311         /* firstly try adding strip to last track, but if that fails, add to a new track */
312         if (BKE_nlatrack_add_strip(adt->nla_tracks.last, strip) == 0) {
313                 /* trying to add to the last track failed (no track or no space), 
314                  * so add a new track to the stack, and add to that...
315                  */
316                 nlt= add_nlatrack(adt, NULL);
317                 BKE_nlatrack_add_strip(nlt, strip);
318         }
319         
320         /* returns the strip added */
321         return strip;
322 }
323
324 /* *************************************************** */
325 /* NLA Evaluation <-> Editing Stuff */
326
327 /* Strip Mapping ------------------------------------- */
328
329 /* non clipped mapping for strip-time <-> global time (for Action-Clips)
330  *      invert = convert action-strip time to global time 
331  */
332 static float nlastrip_get_frame_actionclip (NlaStrip *strip, float cframe, short invert)
333 {
334         float length, actlength, repeat, scale;
335         
336         /* get number of repeats */
337         if (IS_EQ(strip->repeat, 0.0f)) strip->repeat = 1.0f;
338         repeat = strip->repeat;
339         
340         /* scaling */
341         if (IS_EQ(strip->scale, 0.0f)) strip->scale= 1.0f;
342         scale = (float)fabs(strip->scale); /* scale must be positive - we've got a special flag for reversing */
343         
344         /* length of referenced action */
345         actlength = strip->actend - strip->actstart;
346         if (IS_EQ(actlength, 0.0f)) actlength = 1.0f;
347         
348         /* length of strip */
349         length= actlength * scale * repeat;
350         if (IS_EQ(length, 0.0f)) length= strip->end - strip->start;
351         
352         /* reversed = play strip backwards */
353         if (strip->flag & NLASTRIP_FLAG_REVERSE) {
354                 /* invert = convert action-strip time to global time */
355                 if (invert)
356                         return length*(strip->actend - cframe)/(repeat*actlength) + strip->start;
357                 else
358                         return strip->actend - repeat*actlength*(cframe - strip->start)/length;
359         }
360         else {
361                 /* invert = convert action-strip time to global time */
362                 if (invert)
363                         return length*(cframe - strip->actstart)/(repeat*actlength) + strip->start;
364                 else
365                         return repeat*actlength*(cframe - strip->start)/length + strip->actstart;
366         }
367 }
368
369 /* non clipped mapping for strip-time <-> global time (for Transitions)
370  *      invert = convert action-strip time to global time 
371  */
372 static float nlastrip_get_frame_transition (NlaStrip *strip, float cframe, short invert)
373 {
374         float length;
375         
376         /* length of strip */
377         length= strip->end - strip->start;
378         
379         /* reversed = play strip backwards */
380         if (strip->flag & NLASTRIP_FLAG_REVERSE) {
381                 /* invert = convert within-strip-time to global time */
382                 if (invert)
383                         return strip->end - (length * cframe);
384                 else
385                         return (strip->end - cframe) / length;
386         }
387         else {
388                 /* invert = convert within-strip-time to global time */
389                 if (invert)
390                         return (length * cframe) + strip->start;
391                 else
392                         return (cframe - strip->start) / length;
393         }
394 }
395
396 /* non clipped mapping for strip-time <-> global time
397  *      invert = convert action-strip time to global time 
398  *
399  * only secure for 'internal' (i.e. within AnimSys evaluation) operations,
400  * but should not be directly relied on for stuff which interacts with editors
401  */
402 float nlastrip_get_frame (NlaStrip *strip, float cframe, short invert)
403 {
404         switch (strip->type) {
405                 case NLASTRIP_TYPE_TRANSITION: /* transition */
406                         return nlastrip_get_frame_transition(strip, cframe, invert);
407                 
408                 case NLASTRIP_TYPE_CLIP: /* action-clip (default) */
409                 default:
410                         return nlastrip_get_frame_actionclip(strip, cframe, invert);
411         }       
412 }
413
414
415 /* Non clipped mapping for strip-time <-> global time
416  *      invert = convert strip-time to global time 
417  *
418  * Public API method - perform this mapping using the given AnimData block
419  * and perform any necessary sanity checks on the value
420  */
421 float BKE_nla_tweakedit_remap (AnimData *adt, float cframe, short invert)
422 {
423         NlaStrip *strip;
424         
425         /* sanity checks 
426          *      - obviously we've got to have some starting data
427          *      - when not in tweakmode, the active Action does not have any scaling applied :)
428          */
429         if ((adt == NULL) || (adt->flag & ADT_NLA_EDIT_ON)==0)
430                 return cframe;
431                 
432         /* if the active-strip info has been stored already, access this, otherwise look this up
433          * and store for (very probable) future usage
434          */
435         if (adt->actstrip == NULL) {
436                 NlaTrack *nlt= BKE_nlatrack_find_active(&adt->nla_tracks);
437                 adt->actstrip= BKE_nlastrip_find_active(nlt);
438         }
439         strip= adt->actstrip;
440         
441         /* sanity checks 
442          *      - in rare cases, we may not be able to find this strip for some reason (internal error)
443          *      - for now, if the user has defined a curve to control the time, this correction cannot be performed
444          *        reliably...
445          */
446         if ((strip == NULL) || (strip->flag & NLASTRIP_FLAG_USR_TIME))
447                 return cframe;
448                 
449         /* perform the correction now... */
450         return nlastrip_get_frame(strip, cframe, invert);
451 }
452
453 /* *************************************************** */
454 /* Basic Utilities */
455
456 /* NLA-Tracks ---------------------------------------- */
457
458 /* Find the active NLA-track for the given stack */
459 NlaTrack *BKE_nlatrack_find_active (ListBase *tracks)
460 {
461         NlaTrack *nlt;
462         
463         /* sanity check */
464         if ELEM(NULL, tracks, tracks->first)
465                 return NULL;
466                 
467         /* try to find the first active track */
468         for (nlt= tracks->first; nlt; nlt= nlt->next) {
469                 if (nlt->flag & NLATRACK_ACTIVE)
470                         return nlt;
471         }
472         
473         /* none found */
474         return NULL;
475 }
476
477 /* Toggle the 'solo' setting for the given NLA-track, making sure that it is the only one
478  * that has this status in its AnimData block.
479  */
480 void BKE_nlatrack_solo_toggle (AnimData *adt, NlaTrack *nlt)
481 {
482         NlaTrack *nt;
483         
484         /* sanity check */
485         if ELEM(NULL, adt, adt->nla_tracks.first)
486                 return;
487                 
488         /* firstly, make sure 'solo' flag for all tracks is disabled */
489         for (nt= adt->nla_tracks.first; nt; nt= nt->next) {
490                 if (nt != nlt)
491                         nt->flag &= ~NLATRACK_SOLO;
492         }
493                 
494         /* now, enable 'solo' for the given track if appropriate */
495         if (nlt) {
496                 /* toggle solo status */
497                 nlt->flag ^= NLATRACK_SOLO;
498                 
499                 /* set or clear solo-status on AnimData */
500                 if (nlt->flag & NLATRACK_SOLO)
501                         adt->flag |= ADT_NLA_SOLO_TRACK;
502                 else
503                         adt->flag &= ~ADT_NLA_SOLO_TRACK;
504         }
505         else
506                 adt->flag &= ~ADT_NLA_SOLO_TRACK;
507 }
508
509 /* Make the given NLA-track the active one for the given stack. If no track is provided, 
510  * this function can be used to simply deactivate all the NLA tracks in the given stack too.
511  */
512 void BKE_nlatrack_set_active (ListBase *tracks, NlaTrack *nlt_a)
513 {
514         NlaTrack *nlt;
515         
516         /* sanity check */
517         if ELEM(NULL, tracks, tracks->first)
518                 return;
519         
520         /* deactive all the rest */
521         for (nlt= tracks->first; nlt; nlt= nlt->next) 
522                 nlt->flag &= ~NLATRACK_ACTIVE;
523                 
524         /* set the given one as the active one */
525         if (nlt_a)
526                 nlt_a->flag |= NLATRACK_ACTIVE;
527 }
528
529 /* Check if there is any space in the last track to add the given strip */
530 short BKE_nlatrack_has_space (NlaTrack *nlt, float start, float end)
531 {
532         NlaStrip *strip;
533         
534         /* sanity checks */
535         if ((nlt == NULL) || IS_EQ(start, end))
536                 return 0;
537         if (start > end) {
538                 puts("BKE_nlatrack_has_space error... start and end arguments swapped");
539                 SWAP(float, start, end);
540         }
541         
542         /* loop over NLA strips checking for any overlaps with this area... */
543         for (strip= nlt->strips.first; strip; strip= strip->next) {
544                 /* if start frame of strip is past the target end-frame, that means that
545                  * we've gone past the window we need to check for, so things are fine
546                  */
547                 if (strip->start > end)
548                         return 1;
549                 
550                 /* if the end of the strip is greater than either of the boundaries, the range
551                  * must fall within the extents of the strip
552                  */
553                 if ((strip->end > start) || (strip->end > end))
554                         return 0;
555         }
556         
557         /* if we are still here, we haven't encountered any overlapping strips */
558         return 1;
559 }
560
561 /* Rearrange the strips in the track so that they are always in order 
562  * (usually only needed after a strip has been moved) 
563  */
564 void BKE_nlatrack_sort_strips (NlaTrack *nlt)
565 {
566         ListBase tmp = {NULL, NULL};
567         NlaStrip *strip, *sstrip;
568         
569         /* sanity checks */
570         if ELEM(NULL, nlt, nlt->strips.first)
571                 return;
572                 
573         /* we simply perform insertion sort on this list, since it is assumed that per track,
574          * there are only likely to be at most 5-10 strips
575          */
576         for (strip= nlt->strips.first; strip; strip= strip->next) {
577                 short not_added = 1;
578                 
579                 /* remove this strip from the list, and add it to the new list, searching from the end of 
580                  * the list, assuming that the lists are in order 
581                  */
582                 BLI_remlink(&nlt->strips, strip);
583                 
584                 for (sstrip= tmp.last; not_added && sstrip; sstrip= sstrip->prev) {
585                         /* check if add after */
586                         if (sstrip->end < strip->start) {
587                                 BLI_insertlinkafter(&tmp, sstrip, strip);
588                                 not_added= 0;
589                                 break;
590                         }
591                 }
592                 
593                 /* add before first? */
594                 if (not_added)
595                         BLI_addhead(&tmp, strip);
596         }
597         
598         /* reassign the start and end points of the strips */
599         nlt->strips.first= tmp.first;
600         nlt->strips.last= tmp.last;
601 }
602
603 /* Add the given NLA-Strip to the given NLA-Track, assuming that it 
604  * isn't currently attached to another one 
605  */
606 short BKE_nlatrack_add_strip (NlaTrack *nlt, NlaStrip *strip)
607 {
608         NlaStrip *ns;
609         short not_added = 1;
610         
611         /* sanity checks */
612         if ELEM(NULL, nlt, strip)
613                 return 0;
614                 
615         /* check if any space to add */
616         if (BKE_nlatrack_has_space(nlt, strip->start, strip->end)==0)
617                 return 0;
618         
619         /* find the right place to add the strip to the nominated track */
620         for (ns= nlt->strips.first; ns; ns= ns->next) {
621                 /* if current strip occurs after the new strip, add it before */
622                 if (ns->start > strip->end) {
623                         BLI_insertlinkbefore(&nlt->strips, ns, strip);
624                         not_added= 0;
625                         break;
626                 }
627         }
628         if (not_added) {
629                 /* just add to the end of the list of the strips then... */
630                 BLI_addtail(&nlt->strips, strip);
631         }
632         
633         /* added... */
634         return 1;
635 }
636
637 /* NLA Strips -------------------------------------- */
638
639 /* Find the active NLA-strip within the given track */
640 NlaStrip *BKE_nlastrip_find_active (NlaTrack *nlt)
641 {
642         NlaStrip *strip;
643         
644         /* sanity check */
645         if ELEM(NULL, nlt, nlt->strips.first)
646                 return NULL;
647                 
648         /* try to find the first active strip */
649         for (strip= nlt->strips.first; strip; strip= strip->next) {
650                 if (strip->flag & NLASTRIP_FLAG_ACTIVE)
651                         return strip;
652         }
653         
654         /* none found */
655         return NULL;
656 }
657
658 /* Does the given NLA-strip fall within the given bounds (times)? */
659 short BKE_nlastrip_within_bounds (NlaStrip *strip, float min, float max)
660 {
661         const float stripLen= (strip) ? strip->end - strip->start : 0.0f;
662         const float boundsLen= (float)fabs(max - min);
663         
664         /* sanity checks */
665         if ((strip == NULL) || IS_EQ(stripLen, 0.0f) || IS_EQ(boundsLen, 0.0f))
666                 return 0;
667         
668         /* only ok if at least part of the strip is within the bounding window
669          *      - first 2 cases cover when the strip length is less than the bounding area
670          *      - second 2 cases cover when the strip length is greater than the bounding area
671          */
672         if ( (stripLen < boundsLen) && 
673                  !(IN_RANGE(strip->start, min, max) ||
674                    IN_RANGE(strip->end, min, max)) )
675         {
676                 return 0;
677         }
678         if ( (stripLen > boundsLen) && 
679                  !(IN_RANGE(min, strip->start, strip->end) ||
680                    IN_RANGE(max, strip->start, strip->end)) )
681         {
682                 return 0;
683         }
684         
685         /* should be ok! */
686         return 1;
687 }
688
689 /* Is the given NLA-strip the first one to occur for the given AnimData block */
690 // TODO: make this an api method if necesary, but need to add prefix first
691 short nlastrip_is_first (AnimData *adt, NlaStrip *strip)
692 {
693         NlaTrack *nlt;
694         NlaStrip *ns;
695         
696         /* sanity checks */
697         if ELEM(NULL, adt, strip)
698                 return 0;
699                 
700         /* check if strip has any strips before it */
701         if (strip->prev)
702                 return 0;
703                 
704         /* check other tracks to see if they have a strip that's earlier */
705         // TODO: or should we check that the strip's track is also the first?
706         for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next) {
707                 /* only check the first strip, assuming that they're all in order */
708                 ns= nlt->strips.first;
709                 if (ns) {
710                         if (ns->start < strip->start)
711                                 return 0;
712                 }
713         }       
714         
715         /* should be first now */
716         return 1;
717 }
718  
719 /* Tools ------------------------------------------- */
720
721 /* For the given AnimData block, add the active action to the NLA
722  * stack (i.e. 'push-down' action). The UI should only allow this 
723  * for normal editing only (i.e. not in editmode for some strip's action),
724  * so no checks for this are performed.
725  */
726 // TODO: maybe we should have checks for this too...
727 void BKE_nla_action_pushdown (AnimData *adt)
728 {
729         NlaStrip *strip;
730         
731         /* sanity checks */
732         // TODO: need to report the error for this
733         if ELEM(NULL, adt, adt->action) 
734                 return;
735                 
736         /* if the action is empty, we also shouldn't try to add to stack, 
737          * as that will cause us grief down the track
738          */
739         // TODO: what about modifiers?
740         if (action_has_motion(adt->action) == 0) {
741                 printf("BKE_nla_action_pushdown(): action has no data \n");
742                 return;
743         }
744         
745         /* add a new NLA strip to the track, which references the active action */
746         strip= add_nlastrip_to_stack(adt, adt->action);
747         
748         /* do other necessary work on strip */  
749         if (strip) {
750                 /* clear reference to action now that we've pushed it onto the stack */
751                 adt->action->id.us--;
752                 adt->action= NULL;
753                 
754                 /* if the strip is the first one in the track it lives in, check if there
755                  * are strips in any other tracks that may be before this, and set the extend
756                  * mode accordingly
757                  */
758                 if (nlastrip_is_first(adt, strip) == 0) {
759                         /* not first, so extend mode can only be NLASTRIP_EXTEND_HOLD_FORWARD not NLASTRIP_EXTEND_HOLD,
760                          * so that it doesn't override strips in previous tracks
761                          */
762                         strip->extendmode= NLASTRIP_EXTEND_HOLD_FORWARD;
763                 }
764         }
765 }
766
767
768 /* Find the active strip + track combo, and set them up as the tweaking track,
769  * and return if successful or not.
770  */
771 short BKE_nla_tweakmode_enter (AnimData *adt)
772 {
773         NlaTrack *nlt, *activeTrack=NULL;
774         NlaStrip *strip, *activeStrip=NULL;
775         
776         /* verify that data is valid */
777         if ELEM(NULL, adt, adt->nla_tracks.first)
778                 return 0;
779                 
780         /* if block is already in tweakmode, just leave, but we should report 
781          * that this block is in tweakmode (as our returncode)
782          */
783         if (adt->flag & ADT_NLA_EDIT_ON)
784                 return 1;
785                 
786         /* go over the tracks, finding the active one, and its active strip
787          *      - if we cannot find both, then there's nothing to do
788          */
789         for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next) {
790                 /* check if active */
791                 if (nlt->flag & NLATRACK_ACTIVE) {
792                         /* store reference to this active track */
793                         activeTrack= nlt;
794                         
795                         /* now try to find active strip */
796                         activeStrip= BKE_nlastrip_find_active(nlt);
797                         break;
798                 }       
799         }
800         if ELEM3(NULL, activeTrack, activeStrip, activeStrip->act) {
801                 printf("NLA tweakmode enter - neither active requirement found \n");
802                 return 0;
803         }
804                 
805         /* go over all the tracks up to the active one, tagging each strip that uses the same 
806          * action as the active strip, but leaving everything else alone
807          */
808         for (nlt= activeTrack->prev; nlt; nlt= nlt->prev) {
809                 for (strip= nlt->strips.first; strip; strip= strip->next) {
810                         if (strip->act == activeStrip->act)
811                                 strip->flag |= NLASTRIP_FLAG_TWEAKUSER;
812                         else
813                                 strip->flag &= ~NLASTRIP_FLAG_TWEAKUSER; // XXX probably don't need to clear this...
814                 }
815         }
816         
817         
818         /* go over all the tracks after AND INCLUDING the active one, tagging them as being disabled 
819          *      - the active track needs to also be tagged, otherwise, it'll overlap with the tweaks going on
820          */
821         for (nlt= activeTrack; nlt; nlt= nlt->next)
822                 nlt->flag |= NLATRACK_DISABLED;
823         
824         /* handle AnimData level changes:
825          *      - 'real' active action to temp storage (no need to change user-counts)
826          *      - action of active strip set to be the 'active action', and have its usercount incremented
827          *      - editing-flag for this AnimData block should also get turned on (for more efficient restoring)
828          *      - take note of the active strip for mapping-correction of keyframes in the action being edited
829          */
830         adt->tmpact= adt->action;
831         adt->action= activeStrip->act;
832         adt->actstrip= activeStrip;
833         id_us_plus(&activeStrip->act->id);
834         adt->flag |= ADT_NLA_EDIT_ON;
835         
836         /* done! */
837         return 1;
838 }
839
840 /* Exit tweakmode for this AnimData block */
841 void BKE_nla_tweakmode_exit (AnimData *adt)
842 {
843         NlaStrip *strip;
844         NlaTrack *nlt;
845         
846         /* verify that data is valid */
847         if ELEM(NULL, adt, adt->nla_tracks.first)
848                 return;
849                 
850         /* hopefully the flag is correct - skip if not on */
851         if ((adt->flag & ADT_NLA_EDIT_ON) == 0)
852                 return;
853                 
854         // TODO: need to sync the user-strip with the new state of the action!
855                 
856         /* for all NLA-tracks, clear the 'disabled' flag
857          * for all NLA-strips, clear the 'tweak-user' flag
858          */
859         for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next) {
860                 nlt->flag &= ~NLATRACK_DISABLED;
861                 
862                 for (strip= nlt->strips.first; strip; strip= strip->next) 
863                         strip->flag &= ~NLASTRIP_FLAG_TWEAKUSER;
864         }
865         
866         /* handle AnimData level changes:
867          *      - 'temporary' active action needs its usercount decreased, since we're removing this reference
868          *      - 'real' active action is restored from storage
869          *      - storage pointer gets cleared (to avoid having bad notes hanging around)
870          *      - editing-flag for this AnimData block should also get turned off
871          *      - clear pointer to active strip
872          */
873         if (adt->action) adt->action->id.us--;
874         adt->action= adt->tmpact;
875         adt->tmpact= NULL;
876         adt->actstrip= NULL;
877         adt->flag &= ~ADT_NLA_EDIT_ON;
878 }
879
880 /* *************************************************** */