NLA SoC: Merge from 2.5
authorJoshua Leung <aligorith@gmail.com>
Sat, 6 Jun 2009 05:00:40 +0000 (05:00 +0000)
committerJoshua Leung <aligorith@gmail.com>
Sat, 6 Jun 2009 05:00:40 +0000 (05:00 +0000)
20571 to 20667

38 files changed:
release/ui/buttons_data_modifier.py
source/blender/blenkernel/BKE_action.h
source/blender/blenkernel/BKE_fcurve.h
source/blender/blenkernel/BKE_nla.h
source/blender/blenkernel/BKE_utildefines.h
source/blender/blenkernel/intern/action.c
source/blender/blenkernel/intern/anim_sys.c
source/blender/blenkernel/intern/fcurve.c
source/blender/blenkernel/intern/nla.c
source/blender/blenkernel/intern/object.c
source/blender/blenloader/intern/readfile.c
source/blender/blenloader/intern/writefile.c
source/blender/editors/animation/anim_channels.c
source/blender/editors/animation/anim_filter.c
source/blender/editors/animation/keyframes_draw.c
source/blender/editors/include/ED_anim_api.h
source/blender/editors/include/ED_screen.h
source/blender/editors/include/UI_view2d.h
source/blender/editors/interface/resources.c
source/blender/editors/interface/view2d.c
source/blender/editors/screen/screen_ops.c
source/blender/editors/space_action/action_select.c
source/blender/editors/space_file/filesel.c
source/blender/editors/space_graph/graph_select.c
source/blender/editors/space_graph/space_graph.c
source/blender/editors/space_nla/nla_channels.c [new file with mode: 0644]
source/blender/editors/space_nla/nla_draw.c [new file with mode: 0644]
source/blender/editors/space_nla/nla_edit.c [new file with mode: 0644]
source/blender/editors/space_nla/nla_header.c
source/blender/editors/space_nla/nla_intern.h
source/blender/editors/space_nla/nla_ops.c [new file with mode: 0644]
source/blender/editors/space_nla/nla_select.c [new file with mode: 0644]
source/blender/editors/space_nla/space_nla.c
source/blender/editors/space_view3d/space_view3d.c
source/blender/makesdna/DNA_action_types.h
source/blender/makesdna/DNA_anim_types.h
source/blender/makesdna/DNA_scene_types.h
source/blender/makesdna/DNA_space_types.h

index 0f5d446f889b2f788214cb02159d4fb4d70f94dd..72b2f29100eb4ecbb1a194e4ed37e67194dbe934 100644 (file)
@@ -23,9 +23,9 @@ class DATA_PT_modifiers(DataButtonsPanel):
                row.itemL();
 
                for md in ob.modifiers:
-                       box = layout.template_modifier(md)
+                       box = layout.template_modifier(context, md)
 
-                       if box:
+                       if md.expanded:
                                if md.type == 'ARMATURE':
                                        self.armature(box, md)
                                if md.type == 'ARRAY':
@@ -103,7 +103,7 @@ class DATA_PT_modifiers(DataButtonsPanel):
                if md.fit_type == 'FIT_LENGTH':
                        layout.itemR(md, "length")
                if md.fit_type == 'FIT_CURVE':
-                       layout.itemR(md, "curve")
+                               layout.itemR(md, "curve")
 
                layout.itemS()
                
@@ -198,7 +198,7 @@ class DATA_PT_modifiers(DataButtonsPanel):
                layout.itemL(text="See Collision panel.")
                
        def curve(self, layout, md):
-               layout.itemR(md, "object")
+               layout.itemR(md, "curve")
                layout.itemR(md, "vertex_group")
                layout.itemR(md, "deform_axis")
                
@@ -248,7 +248,7 @@ class DATA_PT_modifiers(DataButtonsPanel):
                # Missing: "Reset" and "Recenter"
                
        def lattice(self, layout, md):
-               layout.itemR(md, "object")
+               layout.itemR(md, "lattice")
                layout.itemR(md, "vertex_group")
                
        def mask(self, layout, md):
@@ -260,7 +260,7 @@ class DATA_PT_modifiers(DataButtonsPanel):
                layout.itemR(md, "inverse")
                
        def mesh_deform(self, layout, md):
-               layout.itemR(md, "object")
+               layout.itemR(md, "mesh")
                layout.itemR(md, "vertex_group")
                layout.itemR(md, "invert")
 
index 67eb2ed58bf2cc972850cf16865c8498fc5d1fc9..cc10a4071a650a08081e60526e65c76b3399b6bc 100644 (file)
@@ -68,6 +68,9 @@ void make_local_action(struct bAction *act);
 /* Some kind of bounding box operation on the action */
 void calc_action_range(const struct bAction *act, float *start, float *end, int incl_hidden);
 
+/* Does action have any motion data at all? */
+short action_has_motion(const struct bAction *act);
+
 /* Action Groups API ----------------- */
 
 /* Make the given Action Group the active one */
index 9b8a2990fe57dfbf5cb807bf2447dea175fae772..7058b9d236af83c2a0b33567898003a5defd1f45 100644 (file)
@@ -108,11 +108,15 @@ struct FModifier *fcurve_add_modifier(struct FCurve *fcu, int type);
 void fcurve_copy_modifiers(ListBase *dst, ListBase *src);
 void fcurve_remove_modifier(struct FCurve *fcu, struct FModifier *fcm);
 void fcurve_free_modifiers(struct FCurve *fcu);
-void fcurve_bake_modifiers(struct FCurve *fcu, int start, int end);
 
 struct FModifier *fcurve_find_active_modifier(struct FCurve *fcu);
 void fcurve_set_active_modifier(struct FCurve *fcu, struct FModifier *fcm);
 
+float evaluate_time_fmodifiers(ListBase *modifiers, struct FCurve *fcu, float cvalue, float evaltime);
+void evaluate_value_fmodifiers(ListBase *modifiers, struct FCurve *fcu, float *cvalue, float evaltime);
+
+void fcurve_bake_modifiers(struct FCurve *fcu, int start, int end);
+
 /* ************** F-Curves API ******************** */
 
 /* -------- Data Managemnt  --------  */
index 230096d7ea745c2d01e4c1fe53b6108fc7d81782..da824fd20933c83e9c810bbb1114a5dcf0cb9136 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  *
- * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung
  * All rights reserved.
  *
  * The Original Code is: all of this file.
  *
- * Contributor(s): none yet.
+ * Contributor(s): Joshua Leung (full recode)
  *
  * ***** END GPL LICENSE BLOCK *****
  */
 #ifndef BKE_NLA_H
 #define BKE_NLA_H
 
-struct bActionStrip;
-struct ListBase;
-struct Object;
+struct AnimData;
+struct NlaStrip;
+struct NlaTrack;
+struct bAction;
+
+/* ----------------------------- */
+/* Data Management */
+
+void free_nlastrip(ListBase *strips, struct NlaStrip *strip);
+void free_nlatrack(ListBase *tracks, struct NlaTrack *nlt);
+void free_nladata(ListBase *tracks);
+
+struct NlaStrip *copy_nlastrip(struct NlaStrip *strip);
+struct NlaTrack *copy_nlatrack(struct NlaTrack *nlt);
+void copy_nladata(ListBase *dst, ListBase *src);
+
+struct NlaTrack *add_nlatrack(struct AnimData *adt);
+struct NlaStrip *add_nlastrip(struct bAction *act);
+struct NlaStrip *add_nlastrip_to_stack(struct AnimData *adt, struct bAction *act);
+
+/* ----------------------------- */
+/* API */
+
+struct NlaTrack *BKE_nlatrack_find_active(ListBase *tracks);
+void BKE_nlatrack_set_active(ListBase *tracks, struct NlaTrack *nlt);
+
+void BKE_nlatrack_solo_toggle(struct AnimData *adt, struct NlaTrack *nlt);
+
+short BKE_nlatrack_has_space(struct NlaTrack *nlt, float start, float end);
+void BKE_nlatrack_sort_strips(struct NlaTrack *nlt);
+
+
+struct NlaStrip *BKE_nlastrip_find_active(struct NlaTrack *nlt);
+short BKE_nlastrip_within_bounds(struct NlaStrip *strip, float min, float max);
+
+
+void BKE_nla_action_pushdown(struct AnimData *adt);
+
+short BKE_nla_tweakmode_enter(struct AnimData *adt);
+void BKE_nla_tweakmode_exit(struct AnimData *adt);
 
-void free_actionstrip (struct bActionStrip* strip);
-void free_nlastrips (struct ListBase *nlalist);
-void copy_nlastrips (struct ListBase *dst, struct ListBase *src);
-void copy_actionstrip (struct bActionStrip **dst, struct bActionStrip **src);
-void find_stridechannel(struct Object *ob, struct bActionStrip *strip);
-struct bActionStrip *convert_action_to_strip (struct Object *ob);
 #endif
 
index 6584af085cd5c962e9cc36ef66f32900b17a99d2..0bed2c095e2121154fdf2439c235b6b0ef289688 100644 (file)
 
 #define IS_EQT(a, b, c) ((a > b)? (((a-b) <= c)? 1:0) : ((((b-a) <= c)? 1:0)))
 #define IN_RANGE(a, b, c) ((b < c)? ((b<a && a<c)? 1:0) : ((c<a && a<b)? 1:0))
+#define IN_RANGE_INCL(a, b, c) ((b < c)? ((b<=a && a<=c)? 1:0) : ((c<=a && a<=b)? 1:0))
 
 /* this weirdo pops up in two places ... */
 #if !defined(WIN32) && !defined(__BeOS)
index d54bc749b71e8163cafc9182509178c47e53d185..bb458cc7e251cfd34100abee51daaaad75614037 100644 (file)
@@ -717,50 +717,45 @@ static bActionStrip *get_active_strip(Object *ob)
        return NULL;
 }
 
-/* non clipped mapping of strip */
-static float get_actionstrip_frame(bActionStrip *strip, float cframe, int invert)
-{
-       float length, actlength, repeat, scale;
-       
-       if (strip->repeat == 0.0f) strip->repeat = 1.0f;
-       repeat = (strip->flag & ACTSTRIP_USESTRIDE) ? (1.0f) : (strip->repeat);
-       
-       if (strip->scale == 0.0f) strip->scale= 1.0f;
-       scale = (float)fabs(strip->scale); /* scale must be positive (for now) */
-       
-       actlength = strip->actend-strip->actstart;
-       if (actlength == 0.0f) actlength = 1.0f;
-       length = repeat * scale * actlength;
-       
-       /* invert = convert action-strip time to global time */
-       if (invert)
-               return length*(cframe - strip->actstart)/(repeat*actlength) + strip->start;
-       else
-               return repeat*actlength*(cframe - strip->start)/length + strip->actstart;
-}
-
 /* if the conditions match, it converts current time to strip time */
+// TODO: change this adt
 float get_action_frame(Object *ob, float cframe)
 {
        bActionStrip *strip= get_active_strip(ob);
        
-       if(strip)
-               return get_actionstrip_frame(strip, cframe, 0);
+       //if(strip)
+       //      return get_actionstrip_frame(strip, cframe, 0);
        return cframe;
 }
 
 /* inverted, strip time to current time */
+// TODO: change this to adt
 float get_action_frame_inv(Object *ob, float cframe)
 {
        bActionStrip *strip= get_active_strip(ob);
        
-       if(strip)
-               return get_actionstrip_frame(strip, cframe, 1);
+       //if(strip)
+       //      return get_actionstrip_frame(strip, cframe, 1);
        return cframe;
 }
 
 
-
+/* Check if the given action has any keyframes */
+short action_has_motion(const bAction *act)
+{
+       FCurve *fcu;
+       
+       /* return on the first F-Curve that has some keyframes/samples defined */
+       if (act) {
+               for (fcu= act->curves.first; fcu; fcu= fcu->next) {
+                       if (fcu->totvert)
+                               return 1;
+               }
+       }
+       
+       /* nothing found */
+       return 0;
+}
 
 /* Calculate the extents of given action */
 void calc_action_range(const bAction *act, float *start, float *end, int incl_hidden)
index 30dcb383ef629410eceac9a0ec81c9827fa2f448..fb4c2663c3ce859fc4a5408667c4a3ab752efd8e 100644 (file)
@@ -5,6 +5,8 @@
 #include <stdio.h>
 #include <string.h>
 #include <stddef.h>
+#include <float.h>
+#include <math.h>
 
 #include "MEM_guardedalloc.h"
 
@@ -15,6 +17,7 @@
 #include "BKE_animsys.h"
 #include "BKE_action.h"
 #include "BKE_fcurve.h"
+#include "BKE_nla.h"
 #include "BKE_global.h"
 #include "BKE_main.h"
 #include "BKE_utildefines.h"
@@ -115,7 +118,13 @@ void BKE_free_animdata (ID *id)
                        /* unlink action (don't free, as it's in its own list) */
                        if (adt->action)
                                adt->action->id.us--;
+                       /* same goes for the temporarily displaced action */
+                       if (adt->tmpact)
+                               adt->tmpact->id.us--;
                                
+                       /* free nla data */
+                       free_nladata(&adt->nla_tracks);
+                       
                        /* free drivers - stored as a list of F-Curves */
                        free_fcurves(&adt->drivers);
                        
@@ -145,9 +154,10 @@ AnimData *BKE_copy_animdata (AnimData *adt)
        // XXX review this... it might not be optimal behaviour yet...
        //id_us_plus((ID *)dadt->action);
        dadt->action= copy_action(adt->action);
+       dadt->tmpact= copy_action(adt->action);
        
        /* duplicate NLA data */
-       // XXX todo...
+       copy_nladata(&dadt->nla_tracks, &adt->nla_tracks);
        
        /* duplicate drivers (F-Curves) */
        copy_fcurves(&dadt->drivers, &adt->drivers);
@@ -544,19 +554,17 @@ typedef struct NlaEvalStrip {
        struct NlaEvalStrip *next, *prev;
        
        NlaTrack *track;                        /* track that this strip belongs to */
-       NlaStrip *strip;                /* strip that's being used */
-       NlaStrip *sblend;               /* strip that's being blended towards (if applicable) */
+       NlaStrip *strip;                        /* strip that's being used */
        
        short track_index;                      /* the index of the track within the list */
        short strip_mode;                       /* which end of the strip are we looking at */
 } NlaEvalStrip;
 
-/* bNlaEvalStrip->strip_mode */
+/* NlaEvalStrip->strip_mode */
 enum {
        NES_TIME_BEFORE = -1,
        NES_TIME_WITHIN,
        NES_TIME_AFTER,
-       NES_TIME_AFTER_BLEND
 } eNlaEvalStrip_StripMode;
 
 
@@ -565,8 +573,9 @@ enum {
 typedef struct NlaEvalChannel {
        struct NlaEvalChannel *next, *prev;
        
-       char *path;                             /* ready-to-use path (i.e. remapped already) */
-       int array_index;                /* if applicable... */
+       PointerRNA ptr;                 /* pointer to struct containing property to use */
+       PropertyRNA *prop;              /* RNA-property type to use (should be in the struct given) */
+       int index;                              /* array index (where applicable) */
        
        float value;                    /* value of this channel */
 } NlaEvalChannel;
@@ -574,117 +583,414 @@ typedef struct NlaEvalChannel {
 
 /* ---------------------- */
 
-/* evaluate the F-Curves controlling settings for the NLA-strips (currently, not relinkable) */
-static void nlastrip_evaluate_fcurves (NlaStrip *strip, float ctime)
+/* non clipped mapping for strip-time <-> global time 
+ *     invert = convert action-strip time to global time 
+ */
+static float nlastrip_get_frame (NlaStrip *strip, float cframe, short invert)
 {
-       //PointerRNA actstrip_ptr;
-       //FCurve *fcu;
+       float length, actlength, repeat, scale;
+       
+       /* get number of repeats */
+       if (strip->repeat == 0.0f) strip->repeat = 1.0f;
+       repeat = strip->repeat;
+       
+       /* scaling */
+       if (strip->scale == 0.0f) strip->scale= 1.0f;
+       scale = (float)fabs(strip->scale); /* scale must be positive - we've got a special flag for reversing */
+       
+       /* length of referenced action */
+       actlength = strip->actend - strip->actstart;
+       if (actlength == 0.0f) actlength = 1.0f;
        
-       /* create RNA-pointer needed to set values */
-       //RNA_pointer_create(NULL, &RNA_NlaStrip, strip, &actstrip_ptr);
+       /* length of strip */
+       length = repeat * scale * actlength;
        
-       /* execute these settings as per normal */
-       //animsys_evaluate_fcurves(&actstrip_ptr, &strip->fcurves, NULL, ctime);
+       /* reversed = play strip backwards */
+       if (strip->flag & NLASTRIP_FLAG_REVERSE) {
+               // FIXME: verify these 
+               /* invert = convert action-strip time to global time */
+               if (invert)
+                       return length*(strip->actend - cframe)/(repeat*actlength) + strip->start;
+               else
+                       return strip->actend - repeat*actlength*(cframe - strip->start)/length;
+       }
+       else {
+               /* invert = convert action-strip time to global time */
+               if (invert)
+                       return length*(cframe - strip->actstart)/(repeat*actlength) + strip->start;
+               else
+                       return repeat*actlength*(cframe - strip->start)/length + strip->actstart;
+       }
 }
 
+/* calculate influence of strip based for given frame based on blendin/out values */
+static float nlastrip_get_influence (NlaStrip *strip, float cframe)
+{
+       /* sanity checks - normalise the blendin/out values? */
+       strip->blendin= (float)fabs(strip->blendin);
+       strip->blendout= (float)fabs(strip->blendout);
+       
+       /* result depends on where frame is in respect to blendin/out values */
+       // the +0.0001 factors are to combat rounding errors
+       if (IS_EQ(strip->blendin, 0)==0 && (cframe <= (strip->start + strip->blendin))) {
+               /* there is some blend-in */
+               return (float)fabs(cframe - strip->start) / (strip->blendin);
+       }
+       else if (IS_EQ(strip->blendout, 0)==0 && (cframe >= (strip->end - strip->blendout))) {
+               /* there is some blend-out */
+               return (float)fabs(strip->end - cframe) / (strip->blendout);
+       }
+       else {
+               /* in the middle of the strip, we should be full strength */
+               return 1.0f;
+       }
+}
 
-/* gets the strip active at the current time for a track */
+/* evaluate the evaluation time and influence for the strip, storing the results in the strip */
+void nlastrip_evaluate_controls (NlaStrip *strip, float ctime)
+{
+       /* firstly, analytically generate values for influence and time (if applicable) */
+       if ((strip->flag & NLASTRIP_FLAG_USR_TIME) == 0)
+               strip->strip_time= nlastrip_get_frame(strip, ctime, 1); /* last arg '1' means current time to 'strip'/action time */
+       if ((strip->flag & NLASTRIP_FLAG_USR_INFLUENCE) == 0)
+               strip->influence= nlastrip_get_influence(strip, ctime);
+       
+       /* now strip's evaluate F-Curves for these settings (if applicable) */
+       if (strip->fcurves.first) {
+#if 0
+               PointerRNA strip_ptr;
+               FCurve *fcu;
+               
+               /* create RNA-pointer needed to set values */
+               RNA_pointer_create(NULL, &RNA_NlaStrip, strip, &strip_ptr);
+               
+               /* execute these settings as per normal */
+               animsys_evaluate_fcurves(&actstrip_ptr, &strip->fcurves, NULL, ctime);
+#endif
+       }
+}
+
+
+/* gets the strip active at the current time for a track for evaluation purposes */
 static void nlatrack_ctime_get_strip (ListBase *list, NlaTrack *nlt, short index, float ctime)
 {
-       NlaStrip *strip, *astrip=NULL, *bstrip=NULL;
+       NlaStrip *strip, *estrip=NULL;
        NlaEvalStrip *nes;
        short side= 0;
        
-       /* skip if track is muted */
-       if (nlt->flag & NLATRACK_MUTED) 
-               return;
-       
        /* loop over strips, checking if they fall within the range */
        for (strip= nlt->strips.first; strip; strip= strip->next) {
-               /* only consider if:
-                *      - current time occurs within strip's extents
-                *      - current time occurs before strip (if it is the first)
-                *      - current time occurs after strip (if hold is on)
-                *      - current time occurs between strips (1st of those isn't holding) - blend!
-                */
-               if (IN_RANGE(ctime, strip->start, strip->end)) {
-                       astrip= strip;
+               /* check if current time occurs within this strip  */
+               if (IN_RANGE_INCL(ctime, strip->start, strip->end)) {
+                       /* this strip is active, so try to use it */
+                       estrip= strip;
                        side= NES_TIME_WITHIN;
                        break;
                }
-               else if (ctime < strip->start) {
+               
+               /* if time occurred before current strip... */
+               if (ctime < strip->start) {
                        if (strip == nlt->strips.first) {
-                               astrip= strip;
+                               /* before first strip - only try to use it if it extends backwards in time too */
+                               if (strip->extendmode == NLASTRIP_EXTEND_HOLD)
+                                       estrip= strip;
+                                       
+                               /* side is 'before' regardless of whether there's a useful strip */
                                side= NES_TIME_BEFORE;
-                               break;
                        }
                        else {
-                               astrip= strip->prev;
+                               /* before next strip - previous strip has ended, but next hasn't begun, 
+                                * so blending mode depends on whether strip is being held or not...
+                                *      - only occurs when no transition strip added, otherwise the transition would have
+                                *        been picked up above...
+                                */
+                               strip= strip->prev;
                                
-                               if (astrip->flag & NLASTRIP_HOLDLASTFRAME) {
-                                       side= NES_TIME_AFTER;
-                                       break;
-                               }
-                               else {
-                                       bstrip= strip;
-                                       side= NES_TIME_AFTER_BLEND;
-                                       break;
-                               }
+                               if (strip->extendmode != NLASTRIP_EXTEND_NOTHING)
+                                       estrip= strip;
+                               side= NES_TIME_AFTER;
                        }
+                       break;
+               }
+               
+               /* if time occurred after current strip... */
+               if (ctime > strip->end) {
+                       /* only if this is the last strip should we do anything, and only if that is being held */
+                       if (strip == nlt->strips.last) {
+                               if (strip->extendmode != NLASTRIP_EXTEND_NOTHING)
+                                       estrip= strip;
+                                       
+                               side= NES_TIME_AFTER;
+                               break;
+                       }
+                       
+                       /* otherwise, skip... as the 'before' case will catch it more elegantly! */
                }
        }
        
-       /* check if strip has been found (and whether it has data worth considering) */
-       if (ELEM(NULL, astrip, astrip->act)) 
-               return;
-       if (astrip->flag & NLASTRIP_MUTE
+       /* check if a valid strip was found
+        *      - must not be muted (i.e. will have contribution
+        */
+       if ((estrip == NULL) || (estrip->flag & NLASTRIP_FLAG_MUTED)
                return;
        
-       /* check if blending between strips */
-       if (side == NES_TIME_AFTER_BLEND) {
-               /* blending between strips... so calculate influence+act_time of both */
-               nlastrip_evaluate_fcurves(astrip, ctime);
-               nlastrip_evaluate_fcurves(bstrip, ctime);
-               
-               if ((astrip->influence <= 0.0f) && (bstrip->influence <= 0.0f))
-                       return;
-       }
-       else {
-               /* calculate/set the influence+act_time of this strip - don't consider if 0 influence */
-               nlastrip_evaluate_fcurves(astrip, ctime);
+       /* evaluate strip's evaluation controls  
+        *      - skip if no influence (i.e. same effect as muting the strip)
+        *      - negative influence is not supported yet... how would that be defined?
+        */
+       // TODO: this sounds a bit hacky having a few isolated F-Curves stuck on some data it operates on...
+       // TODO: should we clamp the time to only be on the endpoints of the strip?
+       nlastrip_evaluate_controls(estrip, ctime);
+       if (estrip->influence <= 0.0f) // XXX is it useful to invert the strip?
+               return;
                
-               if (astrip->influence <= 0.0f) 
-                       return;
+       /* check if strip has valid data to evaluate */
+       switch (estrip->type) {
+               case NLASTRIP_TYPE_CLIP: 
+                       /* clip must have some action to evaluate */
+                       if (estrip->act == NULL)
+                               return;
+                       break;
+               case NLASTRIP_TYPE_TRANSITION:
+                       /* there must be strips to transition from and to (i.e. prev and next required) */
+                       // TODO: what happens about cross-track transitions? 
+                       if (ELEM(NULL, estrip->prev, estrip->next))
+                               return;
+                       break;
        }
        
-       
-       /* allocate new eval-strip for this strip + add to stack */
+       /* add to list of strips we need to evaluate */
        nes= MEM_callocN(sizeof(NlaEvalStrip), "NlaEvalStrip");
        
        nes->track= nlt;
-       nes->strip= astrip;
-       nes->sblend= bstrip;
-       nes->track_index= index;
+       nes->strip= estrip;
        nes->strip_mode= side;
+       nes->track_index= index;
        
        BLI_addtail(list, nes);
 }
 
 /* ---------------------- */
 
+/* find an NlaEvalChannel that matches the given criteria 
+ *     - ptr and prop are the RNA data to find a match for
+ */
+static NlaEvalChannel *nlaevalchan_find_match (ListBase *channels, PointerRNA *ptr, PropertyRNA *prop, int array_index)
+{
+       NlaEvalChannel *nec;
+       
+       /* sanity check */
+       if (channels == NULL)
+               return NULL;
+       
+       /* loop through existing channels, checking for a channel which affects the same property */
+       for (nec= channels->first; nec; nec= nec->next) {
+               /* - comparing the PointerRNA's is done by comparing the pointers
+                *   to the actual struct the property resides in, since that all the
+                *   other data stored in PointerRNA cannot allow us to definitively 
+                *      identify the data 
+                */
+               if ((nec->ptr.data == ptr->data) && (nec->prop == prop) && (nec->index == array_index))
+                       return nec;
+       }
+       
+       /* not found */
+       return NULL;
+}
+
+/* verify that an appropriate NlaEvalChannel for this F-Curve exists */
+static NlaEvalChannel *nlaevalchan_verify (PointerRNA *ptr, ListBase *channels, NlaEvalStrip *nes, FCurve *fcu, short *newChan)
+{
+       NlaEvalChannel *nec;
+       NlaStrip *strip= nes->strip;
+       PropertyRNA *prop;
+       PointerRNA new_ptr;
+       char *path = NULL;
+       short free_path=0;
+       
+       /* sanity checks */
+       if (channels == NULL)
+               return NULL;
+       
+       /* get RNA pointer+property info from F-Curve for more convenient handling */
+               /* get path, remapped as appropriate to work in its new environment */
+       free_path= animsys_remap_path(strip->remap, fcu->rna_path, &path);
+       
+               /* a valid property must be available, and it must be animateable */
+       if (RNA_path_resolve(ptr, path, &new_ptr, &prop) == 0) {
+               if (G.f & G_DEBUG) printf("NLA Strip Eval: Cannot resolve path \n");
+               return NULL;
+       }
+               /* only ok if animateable */
+       else if (RNA_property_animateable(&new_ptr, prop) == 0) {
+               if (G.f & G_DEBUG) printf("NLA Strip Eval: Property not animateable \n");
+               return NULL;
+       }
+       
+       /* try to find a match */
+       nec= nlaevalchan_find_match(channels, &new_ptr, prop, fcu->array_index);
+       
+       /* allocate a new struct for this if none found */
+       if (nec == NULL) {
+               nec= MEM_callocN(sizeof(NlaEvalChannel), "NlaEvalChannel");
+               *newChan= 1;
+               BLI_addtail(channels, nec);
+               
+               nec->ptr= new_ptr; 
+               nec->prop= prop;
+               nec->index= fcu->array_index;
+       }
+       
+       /* we can now return */
+       return nec;
+}
+
+/* accumulate (i.e. blend) the given value on to the channel it affects */
+static void nlaevalchan_accumulate (NlaEvalChannel *nec, NlaEvalStrip *nes, short newChan, float value)
+{
+       NlaStrip *strip= nes->strip;
+       float inf= strip->influence;
+       
+       /* if channel is new, just store value regardless of blending factors, etc. */
+       if (newChan) {
+               nec->value= value;
+               return;
+       }
+       
+       /* premultiply the value by the weighting factor */
+       if (IS_EQ(inf, 0)) return;
+       value *= inf;
+       
+       /* perform blending */
+       switch (strip->blendmode) {
+               case NLASTRIP_MODE_ADD:
+                       /* simply add the scaled value on to the stack */
+                       nec->value += value;
+                       break;
+                       
+               case NLASTRIP_MODE_SUBTRACT:
+                       /* simply subtract the scaled value from the stack */
+                       nec->value -= value;
+                       break;
+                       
+               case NLASTRIP_MODE_MULTIPLY:
+                       /* multiply the scaled value with the stack */
+                       nec->value *= value;
+                       break;
+               
+               case NLASTRIP_MODE_BLEND:
+               default: // TODO: do we really want to blend by default? it seems more uses might prefer add...
+                       /* do linear interpolation 
+                        *      - the influence of the accumulated data (elsewhere, that is called dstweight) 
+                        *        is 1 - influence, since the strip's influence is srcweight
+                        */
+                       nec->value= nec->value * (1.0f - inf)   +   value;
+                       break;
+       }
+}
+
+/* ---------------------- */
+
+/* evaluate action-clip strip */
+static void nlastrip_evaluate_actionclip (PointerRNA *ptr, ListBase *channels, NlaEvalStrip *nes)
+{
+       NlaStrip *strip= nes->strip;
+       FCurve *fcu;
+       float evaltime;
+       
+       /* evaluate strip's modifiers which modify time to evaluate the base curves at */
+       evaltime= evaluate_time_fmodifiers(&strip->modifiers, NULL, 0.0f, strip->strip_time);
+       
+       /* evaluate all the F-Curves in the action, saving the relevant pointers to data that will need to be used */
+       for (fcu= strip->act->curves.first; fcu; fcu= fcu->next) {
+               NlaEvalChannel *nec;
+               float value = 0.0f;
+               short newChan = -1;
+               
+               /* check if this curve should be skipped */
+               if (fcu->flag & (FCURVE_MUTED|FCURVE_DISABLED)) 
+                       continue;
+                       
+               /* evaluate the F-Curve's value for the time given in the strip 
+                * NOTE: we use the modified time here, since strip's F-Curve Modifiers are applied on top of this 
+                */
+               value= evaluate_fcurve(fcu, evaltime);
+               
+               /* apply strip's F-Curve Modifiers on this value 
+                * NOTE: we apply the strip's original evaluation time not the modified one (as per standard F-Curve eval)
+                */
+               evaluate_value_fmodifiers(&strip->modifiers, fcu, &value, strip->strip_time);
+               
+               
+               /* get an NLA evaluation channel to work with, and accumulate the evaluated value with the value(s)
+                * stored in this channel if it has been used already
+                */
+               nec= nlaevalchan_verify(ptr, channels, nes, fcu, &newChan);
+               if (nec)
+                       nlaevalchan_accumulate(nec, nes, newChan, value);
+       }
+}
+
 /* evaluates the given evaluation strip */
-// FIXME: will we need the evaluation cache table set up to blend stuff in?
 // TODO: only evaluate here, but flush in one go using the accumulated channels at end...
-static void nlastrip_ctime_evaluate (ListBase *channels, NlaEvalStrip *nes, float ctime)
+static void nlastrip_evaluate (PointerRNA *ptr, ListBase *channels, NlaEvalStrip *nes)
 {
-       // 1. (in old code) was to extract 'IPO-channels' from actions
-       // 2. blend between the 'accumulated' data, and the new data
+       /* actions to take depend on the type of strip */
+       switch (nes->strip->type) {
+               case NLASTRIP_TYPE_CLIP: /* action-clip */
+                       nlastrip_evaluate_actionclip(ptr, channels, nes);
+                       break;
+               case NLASTRIP_TYPE_TRANSITION: /* transition */
+                       // XXX code this...
+                       break;
+       }
 }
 
 /* write the accumulated settings to */
-static void nladata_flush_channels (PointerRNA *ptr, ListBase *channels)
+static void nladata_flush_channels (ListBase *channels)
 {
+       NlaEvalChannel *nec;
+       
+       /* sanity checks */
+       if (channels == NULL)
+               return;
        
+       /* for each channel with accumulated values, write its value on the property it affects */
+       for (nec= channels->first; nec; nec= nec->next) {
+               PointerRNA *ptr= &nec->ptr;
+               PropertyRNA *prop= nec->prop;
+               int array_index= nec->index;
+               float value= nec->value;
+               
+               /* write values - see animsys_write_rna_setting() to sync the code */
+               switch (RNA_property_type(prop)) 
+               {
+                       case PROP_BOOLEAN:
+                               if (RNA_property_array_length(prop))
+                                       RNA_property_boolean_set_index(ptr, prop, array_index, (int)value);
+                               else
+                                       RNA_property_boolean_set(ptr, prop, (int)value);
+                               break;
+                       case PROP_INT:
+                               if (RNA_property_array_length(prop))
+                                       RNA_property_int_set_index(ptr, prop, array_index, (int)value);
+                               else
+                                       RNA_property_int_set(ptr, prop, (int)value);
+                               break;
+                       case PROP_FLOAT:
+                               if (RNA_property_array_length(prop))
+                                       RNA_property_float_set_index(ptr, prop, array_index, value);
+                               else
+                                       RNA_property_float_set(ptr, prop, value);
+                               break;
+                       case PROP_ENUM:
+                               RNA_property_enum_set(ptr, prop, (int)value);
+                               break;
+                       default:
+                               // can't do anything with other types of property....
+                               break;
+               }
+       }
 }
 
 /* ---------------------- */
@@ -703,8 +1009,21 @@ static void animsys_evaluate_nla (PointerRNA *ptr, AnimData *adt, float ctime)
        NlaEvalStrip *nes;
        
        /* 1. get the stack of strips to evaluate at current time (influence calculated here) */
-       for (nlt=adt->nla_tracks.first; nlt; nlt=nlt->next, track_index++) 
+       for (nlt=adt->nla_tracks.first; nlt; nlt=nlt->next, track_index++) { 
+               /* if tweaking is on and this strip is the tweaking track, stop on this one */
+               if ((adt->flag & ADT_NLA_EDIT_ON) && (nlt->flag & NLATRACK_DISABLED))
+                       break;
+                       
+               /* skip if we're only considering a track tagged 'solo' */
+               if ((adt->flag & ADT_NLA_SOLO_TRACK) && (nlt->flag & NLATRACK_SOLO)==0)
+                       continue;
+               /* skip if track is muted */
+               if (nlt->flag & NLATRACK_MUTED) 
+                       continue;
+                       
+               /* otherwise, get strip to evaluate for this channel */
                nlatrack_ctime_get_strip(&estrips, nlt, track_index, ctime);
+       }
        
        /* only continue if there are strips to evaluate */
        if (estrips.first == NULL)
@@ -713,10 +1032,10 @@ static void animsys_evaluate_nla (PointerRNA *ptr, AnimData *adt, float ctime)
        
        /* 2. for each strip, evaluate then accumulate on top of existing channels, but don't set values yet */
        for (nes= estrips.first; nes; nes= nes->next) 
-               nlastrip_ctime_evaluate(&echannels, nes, ctime);
+               nlastrip_evaluate(ptr, &echannels, nes);
        
        /* 3. flush effects of accumulating channels in NLA to the actual data they affect */
-       nladata_flush_channels(ptr, &echannels);
+       nladata_flush_channels(&echannels);
        
        /* 4. free temporary evaluation data */
        BLI_freelistN(&estrips);
@@ -798,17 +1117,23 @@ void BKE_animsys_evaluate_animdata (ID *id, AnimData *adt, float ctime, short re
         *      - NLA before Active Action, as Active Action behaves as 'tweaking track'
         *        that overrides 'rough' work in NLA
         */
+       // TODO: need to double check that this all works correctly
        if ((recalc & ADT_RECALC_ANIM) || (adt->recalc & ADT_RECALC_ANIM))
        {
                /* evaluate NLA data */
                if ((adt->nla_tracks.first) && !(adt->flag & ADT_NLA_EVAL_OFF))
                {
+                       /* evaluate NLA-stack */
                        animsys_evaluate_nla(&id_ptr, adt, ctime);
+                       
+                       /* evaluate 'active' Action (may be tweaking track) on top of results of NLA-evaluation 
+                        *      - only do this if we're not exclusively evaluating the 'solo' NLA-track
+                        */
+                       if ((adt->action) && !(adt->flag & ADT_NLA_SOLO_TRACK))
+                               animsys_evaluate_action(&id_ptr, adt->action, adt->remap, ctime);
                }
-               
-               /* evaluate Action data */
-               // FIXME: what if the solo track was not tweaking one, then nla-solo should be checked too?
-               if (adt->action) 
+               /* evaluate Active Action only */
+               else if (adt->action)
                        animsys_evaluate_action(&id_ptr, adt->action, adt->remap, ctime);
                
                /* reset tag */
index ad8115ba9aaf487c62b6ce884df2558944bdfaeb..6a3870f8f31a8dfc695b331e6383762da6f9e536 100644 (file)
@@ -2190,34 +2190,6 @@ void fcurve_free_modifiers (FCurve *fcu)
        }
 }
 
-/* Bake modifiers for given F-Curve to curve sample data, in the frame range defined
- * by start and end (inclusive).
- */
-void fcurve_bake_modifiers (FCurve *fcu, int start, int end)
-{
-       ChannelDriver *driver;
-       
-       /* sanity checks */
-       // TODO: make these tests report errors using reports not printf's
-       if ELEM(NULL, fcu, fcu->modifiers.first) {
-               printf("Error: No F-Curve with F-Curve Modifiers to Bake\n");
-               return;
-       }
-       
-       /* temporarily, disable driver while we sample, so that they don't influence the outcome */
-       driver= fcu->driver;
-       fcu->driver= NULL;
-       
-       /* bake the modifiers, by sampling the curve at each frame */
-       fcurve_store_samples(fcu, NULL, start, end, fcurve_samplingcb_evalcurve);
-       
-       /* free the modifiers now */
-       fcurve_free_modifiers(fcu);
-       
-       /* restore driver */
-       fcu->driver= driver;
-}
-
 /* Find the active F-Curve Modifier */
 FModifier *fcurve_find_active_modifier (FCurve *fcu)
 {
@@ -2255,6 +2227,98 @@ void fcurve_set_active_modifier (FCurve *fcu, FModifier *fcm)
                fcm->flag |= FMODIFIER_FLAG_ACTIVE;
 }
 
+/* Evaluation API --------------------------- */
+
+/* evaluate time modifications imposed by some F-Curve Modifiers
+ *     - this step acts as an optimisation to prevent the F-Curve stack being evaluated 
+ *       several times by modifiers requesting the time be modified, as the final result
+ *       would have required using the modified time
+ *     - modifiers only ever recieve the unmodified time, as subsequent modifiers should be
+ *       working on the 'global' result of the modified curve, not some localised segment,
+ *       so nevaltime gets set to whatever the last time-modifying modifier likes...
+ *     - we start from the end of the stack, as only the last one matters for now
+ */
+float evaluate_time_fmodifiers (ListBase *modifiers, FCurve *fcu, float cvalue, float evaltime)
+{
+       FModifier *fcm;
+       float m_evaltime= evaltime;
+       
+       /* sanity checks */
+       if ELEM(NULL, modifiers, modifiers->first)
+               return evaltime;
+               
+       /* find the first modifier from end of stack that modifies time, and calculate the time the modifier
+        * would calculate time at
+        */
+       for (fcm= fcu->modifiers.last; fcm; fcm= fcm->prev) {
+               FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm);
+               
+               /* only evaluate if there's a callback for this */
+               // TODO: implement the 'influence' control feature...
+               if (fmi && fmi->evaluate_modifier_time) {
+                       if ((fcm->flag & (FMODIFIER_FLAG_DISABLED|FMODIFIER_FLAG_MUTED)) == 0)
+                               m_evaltime= fmi->evaluate_modifier_time(fcu, fcm, cvalue, evaltime);
+                       break;
+               }
+       }
+       
+       /* return the modified evaltime */
+       return m_evaltime;
+}
+
+/* Evalautes the given set of F-Curve Modifiers using the given data
+ * Should only be called after evaluate_time_fmodifiers() has been called...
+ */
+void evaluate_value_fmodifiers (ListBase *modifiers, FCurve *fcu, float *cvalue, float evaltime)
+{
+       FModifier *fcm;
+       
+       /* sanity checks */
+       if ELEM(NULL, modifiers, modifiers->first)
+               return;
+       
+       /* evaluate modifiers */
+       for (fcm= modifiers->first; fcm; fcm= fcm->next) {
+               FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm);
+               
+               /* only evaluate if there's a callback for this */
+               // TODO: implement the 'influence' control feature...
+               if (fmi && fmi->evaluate_modifier) {
+                       if ((fcm->flag & (FMODIFIER_FLAG_DISABLED|FMODIFIER_FLAG_MUTED)) == 0)
+                               fmi->evaluate_modifier(fcu, fcm, cvalue, evaltime);
+               }
+       }
+} 
+
+
+/* Bake modifiers for given F-Curve to curve sample data, in the frame range defined
+ * by start and end (inclusive).
+ */
+void fcurve_bake_modifiers (FCurve *fcu, int start, int end)
+{
+       ChannelDriver *driver;
+       
+       /* sanity checks */
+       // TODO: make these tests report errors using reports not printf's
+       if ELEM(NULL, fcu, fcu->modifiers.first) {
+               printf("Error: No F-Curve with F-Curve Modifiers to Bake\n");
+               return;
+       }
+       
+       /* temporarily, disable driver while we sample, so that they don't influence the outcome */
+       driver= fcu->driver;
+       fcu->driver= NULL;
+       
+       /* bake the modifiers, by sampling the curve at each frame */
+       fcurve_store_samples(fcu, NULL, start, end, fcurve_samplingcb_evalcurve);
+       
+       /* free the modifiers now */
+       fcurve_free_modifiers(fcu);
+       
+       /* restore driver */
+       fcu->driver= driver;
+}
+
 /* ***************************** F-Curve - Evaluation ********************************* */
 
 /* Evaluate and return the value of the given F-Curve at the specified frame ("evaltime") 
@@ -2262,7 +2326,6 @@ void fcurve_set_active_modifier (FCurve *fcu, FModifier *fcm)
  */
 float evaluate_fcurve (FCurve *fcu, float evaltime) 
 {
-       FModifier *fcm;
        float cvalue= 0.0f;
        float devaltime;
        
@@ -2275,28 +2338,8 @@ float evaluate_fcurve (FCurve *fcu, float evaltime)
                evaltime= cvalue= evaluate_driver(fcu->driver, evaltime);
        }
        
-       /* evaluate time modifications imposed by some F-Curve Modifiers
-        *      - this step acts as an optimisation to prevent the F-Curve stack being evaluated 
-        *        several times by modifiers requesting the time be modified, as the final result
-        *        would have required using the modified time
-        *      - modifiers only ever recieve the unmodified time, as subsequent modifiers should be
-        *        working on the 'global' result of the modified curve, not some localised segment,
-        *        so nevaltime gets set to whatever the last time-modifying modifier likes...
-        *      - we start from the end of the stack, as only the last one matters for now
-        */
-       devaltime= evaltime;
-       
-       for (fcm= fcu->modifiers.last; fcm; fcm= fcm->prev) {
-               FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm);
-               
-               /* only evaluate if there's a callback for this */
-               // TODO: implement the 'influence' control feature...
-               if (fmi && fmi->evaluate_modifier_time) {
-                       if ((fcm->flag & (FMODIFIER_FLAG_DISABLED|FMODIFIER_FLAG_MUTED)) == 0)
-                               devaltime= fmi->evaluate_modifier_time(fcu, fcm, cvalue, evaltime);
-                       break;
-               }
-       }
+       /* evaluate modifiers which modify time to evaluate the base curve at */
+       devaltime= evaluate_time_fmodifiers(&fcu->modifiers, fcu, cvalue, evaltime);
        
        /* evaluate curve-data 
         *      - 'devaltime' instead of 'evaltime', as this is the time that the last time-modifying 
@@ -2308,16 +2351,7 @@ float evaluate_fcurve (FCurve *fcu, float evaltime)
                cvalue= fcurve_eval_samples(fcu, fcu->fpt, devaltime);
        
        /* evaluate modifiers */
-       for (fcm= fcu->modifiers.first; fcm; fcm= fcm->next) {
-               FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm);
-               
-               /* only evaluate if there's a callback for this */
-               // TODO: implement the 'influence' control feature...
-               if (fmi && fmi->evaluate_modifier) {
-                       if ((fcm->flag & (FMODIFIER_FLAG_DISABLED|FMODIFIER_FLAG_MUTED)) == 0)
-                               fmi->evaluate_modifier(fcu, fcm, &cvalue, evaltime);
-               }
-       }
+       evaluate_value_fmodifiers(&fcu->modifiers, fcu, &cvalue, evaltime);
        
        /* if curve can only have integral values, perform truncation (i.e. drop the decimal part)
         * here so that the curve can be sampled correctly
index dc2bf26759f3b3c32cc0db9f18abdebc4b8f5767..d3a01b6d610e06f6fbb0c1184c294ba76cb5bf6d 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  *
- * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung
  * All rights reserved.
  *
  * The Original Code is: all of this file.
  *
- * Contributor(s): none yet.
+ * Contributor(s): Joshua Leung (full recode)
  *
  * ***** END GPL LICENSE BLOCK *****
  */
 
 #include <stdlib.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <math.h>
+#include <float.h>
 
 #include "MEM_guardedalloc.h"
 
 #include "BLI_blenlib.h"
 
-#include "DNA_space_types.h"
-#include "DNA_nla_types.h"
+#include "DNA_anim_types.h"
 #include "DNA_action_types.h"
-#include "DNA_ID.h"
-#include "DNA_ipo_types.h"
-#include "DNA_object_types.h"
 
-#include "BKE_nla.h"
+#include "BKE_animsys.h"
 #include "BKE_action.h"
+#include "BKE_fcurve.h"
+#include "BKE_nla.h"
 #include "BKE_blender.h"
 #include "BKE_library.h"
-#include "BKE_object.h" /* for convert_action_to_strip(ob) */
+#include "BKE_object.h"
+#include "BKE_utildefines.h"
 
 
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
 
-/* NOTE: in group.c the strips get copied for group-nla override, this assumes
-   that strips are one single block, without additional data to be copied */
 
-void copy_actionstrip (bActionStrip **dst, bActionStrip **src){
-       bActionStrip *dstrip;
-       bActionStrip *sstrip = *src;
+/* *************************************************** */
+/* Data Management */
+
+/* Freeing ------------------------------------------- */
+
+/* Remove the given NLA strip from the NLA track it occupies, free the strip's data,
+ * and the strip itself. 
+ */
+// TODO: with things like transitions, should these get freed too? Maybe better as a UI tool
+void free_nlastrip (ListBase *strips, NlaStrip *strip)
+{
+       FModifier *fcm, *fmn;
+       
+       /* sanity checks */
+       if (strip == NULL)
+               return;
+               
+       /* remove reference to action */
+       if (strip->act)
+               strip->act->id.us--;
+               
+       /* free remapping info */
+       //if (strip->remap)
+       //      BKE_animremap_free();
+       
+       /* free own F-Curves */
+       free_fcurves(&strip->fcurves);
+       
+       /* free F-Modifiers */
+       for (fcm= strip->modifiers.first; fcm; fcm= fmn) {
+               fmn= fcm->next;
+               
+               BLI_remlink(&strip->modifiers, fcm);
+               fcurve_remove_modifier(NULL, fcm);
+       }
+       
+       /* free the strip itself */
+       if (strips)
+               BLI_freelinkN(strips, strip);
+       else
+               MEM_freeN(strip);
+}
+
+/* Remove the given NLA track from the set of NLA tracks, free the track's data,
+ * and the track itself.
+ */
+void free_nlatrack (ListBase *tracks, NlaTrack *nlt)
+{
+       NlaStrip *strip, *stripn;
+       
+       /* sanity checks */
+       if (nlt == NULL)
+               return;
+               
+       /* free strips */
+       for (strip= nlt->strips.first; strip; strip= stripn) {
+               stripn= strip->next;
+               free_nlastrip(&nlt->strips, strip);
+       }
+       
+       /* free NLA track itself now */
+       if (tracks)
+               BLI_freelinkN(tracks, nlt);
+       else
+               MEM_freeN(nlt);
+}
 
-       if (!*src){
-               *dst=NULL;
+/* 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 free_nladata (ListBase *tracks)
+{
+       NlaTrack *nlt, *nltn;
+       
+       /* sanity checks */
+       if ELEM(NULL, tracks, tracks->first)
                return;
+               
+       /* free tracks one by one */
+       for (nlt= tracks->first; nlt; nlt= nltn) {
+               nltn= nlt->next;
+               free_nlatrack(tracks, nlt);
        }
+       
+       /* clear the list's pointers to be safe */
+       tracks->first= tracks->last= NULL;
+}
 
-       *dst = MEM_dupallocN(sstrip);
+/* Copying ------------------------------------------- */
 
-       dstrip = *dst;
-       if (dstrip->act)
-               dstrip->act->id.us++;
+/* Copy NLA strip */
+NlaStrip *copy_nlastrip (NlaStrip *strip)
+{
+       NlaStrip *strip_d;
+       
+       /* sanity check */
+       if (strip == NULL)
+               return NULL;
+               
+       /* make a copy */
+       strip_d= MEM_dupallocN(strip);
+       strip_d->next= strip_d->prev= NULL;
+       
+       /* increase user-count of action */
+       if (strip_d->act)
+               strip_d->act->id.us++;
+               
+       /* copy F-Curves and modifiers */
+       copy_fcurves(&strip_d->fcurves, &strip->fcurves);
+       fcurve_copy_modifiers(&strip_d->modifiers, &strip->modifiers);
+       
+       /* return the strip */
+       return strip_d;
+}
 
-       if (dstrip->ipo)
-               dstrip->ipo->id.us++;
+/* Copy NLA Track */
+NlaTrack *copy_nlatrack (NlaTrack *nlt)
+{
+       NlaStrip *strip, *strip_d;
+       NlaTrack *nlt_d;
+       
+       /* sanity check */
+       if (nlt == NULL)
+               return NULL;
+               
+       /* make a copy */
+       nlt_d= MEM_dupallocN(nlt);
+       nlt_d->next= nlt_d->prev= NULL;
        
-       if (dstrip->modifiers.first) {
-               BLI_duplicatelist (&dstrip->modifiers, &sstrip->modifiers);
+       /* make a copy of all the strips, one at a time */
+       nlt_d->strips.first= nlt_d->strips.last= NULL;
+       
+       for (strip= nlt->strips.first; strip; strip= strip->next) {
+               strip_d= copy_nlastrip(strip);
+               BLI_addtail(&nlt_d->strips, strip_d);
        }
        
+       /* return the copy */
+       return nlt_d;
 }
 
-void copy_nlastrips (ListBase *dst, ListBase *src)
+/* Copy all NLA data */
+void copy_nladata (ListBase *dst, ListBase *src)
 {
-       bActionStrip *strip;
+       NlaTrack *nlt, *nlt_d;
+       
+       /* sanity checks */
+       if ELEM(NULL, dst, src)
+               return;
+               
+       /* copy each NLA-track, one at a time */
+       for (nlt= src->first; nlt; nlt= nlt->next) {
+               /* make a copy, and add the copy to the destination list */
+               nlt_d= copy_nlatrack(nlt);
+               BLI_addtail(dst, nlt_d);
+       }
+}
 
-       dst->first=dst->last=NULL;
+/* Adding ------------------------------------------- */
 
-       BLI_duplicatelist (dst, src);
+/* Add a NLA Track to the given AnimData */
+NlaTrack *add_nlatrack (AnimData *adt)
+{
+       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;
+       nlt->index= BLI_countlist(&adt->nla_tracks);
+       
+       /* add track to stack, and make it the active one */
+       BLI_addtail(&adt->nla_tracks, nlt);
+       BKE_nlatrack_set_active(&adt->nla_tracks, nlt);
+       
+       /* must have unique name, but we need to seed this */
+       sprintf(nlt->name, "NlaTrack");
+       BLI_uniquename(&adt->nla_tracks, nlt, "NlaTrack", '.', offsetof(NlaTrack, name), 64);
+       
+       /* return the new track */
+       return nlt;
+}
 
-       /* Update specific data */
-       if (!dst->first)
-               return;
+/* Add a NLA Strip referencing the given Action */
+NlaStrip *add_nlastrip (bAction *act)
+{
+       NlaStrip *strip;
+       
+       /* sanity checks */
+       if (act == NULL)
+               return NULL;
+               
+       /* allocate new strip */
+       strip= MEM_callocN(sizeof(NlaStrip), "NlaStrip");
+       
+       /* 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
+        *      - (XXX) synchronisation of strip-length in accordance with changes to action-length
+        *        is not done though, since this should only really happens in editmode for strips now
+        *        though this decision is still subject to further review...
+        */
+       strip->flag = NLASTRIP_FLAG_SELECT|NLASTRIP_FLAG_AUTO_BLENDS;
+       
+       /* assign the action reference */
+       strip->act= act;
+       id_us_plus(&act->id);
+       
+       /* determine initial range 
+        *      - strip length cannot be 0... ever...
+        */
+       calc_action_range(strip->act, &strip->actstart, &strip->actend, 1);
+       
+       strip->start = strip->actstart;
+       strip->end = (IS_EQ(strip->actstart, strip->actend)) ?  (strip->actstart + 1.0f): (strip->actend);
+       
+       /* strip should be referenced as-is */
+       strip->scale= 1.0f;
+       strip->repeat = 1.0f;
+       
+       /* return the new strip */
+       return strip;
+}
 
-       for (strip = dst->first; strip; strip=strip->next){
-               if (strip->act)
-                       strip->act->id.us++;
-               if (strip->ipo)
-                       strip->ipo->id.us++;
-               if (strip->modifiers.first) {
-                       ListBase listb;
-                       BLI_duplicatelist (&listb, &strip->modifiers);
-                       strip->modifiers= listb;
+/* Add new NLA-strip to the top of the NLA stack - i.e. into the last track if space, or a new one otherwise */
+NlaStrip *add_nlastrip_to_stack (AnimData *adt, bAction *act)
+{
+       NlaStrip *strip, *ns;
+       NlaTrack *nlt;
+       short not_added = 1;
+       
+       /* sanity checks */
+       if ELEM(NULL, adt, act)
+               return NULL;
+       
+       /* create a new NLA strip */
+       strip= add_nlastrip(act);
+       if (strip == NULL)
+               return NULL;
+       
+       /* check if the last NLA-track (if it exists) has any space for this strip:
+        *      - if so, add this strip to that track
+        */
+       if ( (adt->nla_tracks.last == NULL) || 
+                (BKE_nlatrack_has_space(adt->nla_tracks.last, strip->start, strip->end)==0) ) 
+       {
+               /* no space, so add to a new track... */
+               nlt= add_nlatrack(adt);
+       }
+       else 
+       {
+               /* there's some space, so add to this track... */
+               nlt= adt->nla_tracks.last;
+       }
+       
+       /* find the right place to add the strip to the nominated track */
+       for (ns= nlt->strips.first; ns; ns= ns->next) {
+               /* if current strip occurs after the new strip, add it before */
+               if (ns->start > strip->end) {
+                       BLI_insertlinkbefore(&nlt->strips, ns, strip);
+                       not_added= 0;
+                       break;
                }
        }
+       if (not_added) {
+               /* just add to the end of the list of the strips then... */
+               BLI_addtail(&nlt->strips, strip);
+       }
+       
+       
+       /* returns the strip added */
+       return strip;
 }
 
-/* from editnla, for convert_action_to_strip -- no UI code so should be ok here.. */
-void find_stridechannel(Object *ob, bActionStrip *strip)
+/* *************************************************** */
+/* Basic Utilities */
+
+/* NLA-Tracks ---------------------------------------- */
+
+/* Find the active NLA-track for the given stack */
+NlaTrack *BKE_nlatrack_find_active (ListBase *tracks)
 {
-       if(ob && ob->pose) {
-               bPoseChannel *pchan;
-               for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next)
-                       if(pchan->flag & POSE_STRIDE)
-                               break;
-               if(pchan)
-                       BLI_strncpy(strip->stridechannel, pchan->name, 32);
-               else
-                       strip->stridechannel[0]= 0;
+       NlaTrack *nlt;
+       
+       /* sanity check */
+       if ELEM(NULL, tracks, tracks->first)
+               return NULL;
+               
+       /* try to find the first active track */
+       for (nlt= tracks->first; nlt; nlt= nlt->next) {
+               if (nlt->flag & NLATRACK_ACTIVE)
+                       return nlt;
        }
+       
+       /* none found */
+       return NULL;
 }
 
-//called by convert_nla / bpy api with an object with the action to be converted to a new strip
-bActionStrip *convert_action_to_strip (Object *ob)
+/* 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 (AnimData *adt, NlaTrack *nlt)
 {
-       bActionStrip *nstrip;
-
-       /* Make new actionstrip */
-       nstrip = MEM_callocN(sizeof(bActionStrip), "bActionStrip");
-                       
-       /* Link the action to the nstrip */
-       nstrip->act = ob->action;
-       id_us_plus(&nstrip->act->id);
-       calc_action_range(nstrip->act, &nstrip->actstart, &nstrip->actend, 1);
-       nstrip->start = nstrip->actstart;
-       nstrip->end = nstrip->actend;
-       nstrip->flag = ACTSTRIP_SELECT|ACTSTRIP_LOCK_ACTION;
-                       
-       find_stridechannel(ob, nstrip);
-       //set_active_strip(ob, nstrip); /* is in editnla as does UI calls */
-                       
-       nstrip->repeat = 1.0;
+       NlaTrack *nt;
+       
+       /* sanity check */
+       if ELEM(NULL, adt, adt->nla_tracks.first)
+               return;
+               
+       /* firstly, make sure 'solo' flag for all tracks is disabled */
+       for (nt= adt->nla_tracks.first; nt; nt= nt->next) {
+               if (nt != nlt)
+                       nt->flag &= ~NLATRACK_SOLO;
+       }
+               
+       /* now, enable 'solo' for the given track if appropriate */
+       if (nlt) {
+               /* toggle solo status */
+               nlt->flag ^= NLATRACK_SOLO;
+               
+               /* set or clear solo-status on AnimData */
+               if (nlt->flag & NLATRACK_SOLO)
+                       adt->flag |= ADT_NLA_SOLO_TRACK;
+               else
+                       adt->flag &= ~ADT_NLA_SOLO_TRACK;
+       }
+       else
+               adt->flag &= ~ADT_NLA_SOLO_TRACK;
+}
 
-       if(ob->nlastrips.first == NULL)
-               ob->nlaflag |= OB_NLA_OVERRIDE;
+/* 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, NlaTrack *nlt_a)
+{
+       NlaTrack *nlt;
        
-       BLI_addtail(&ob->nlastrips, nstrip);
-       return nstrip; /* is created, malloced etc. here so is safe to just return the pointer?
-                         this is needed for setting this active in UI, and probably useful for API too */
+       /* sanity check */
+       if ELEM(NULL, tracks, tracks->first)
+               return;
        
+       /* deactive all the rest */
+       for (nlt= tracks->first; nlt; nlt= nlt->next) 
+               nlt->flag &= ~NLATRACK_ACTIVE;
+               
+       /* set the given one as the active one */
+       if (nlt_a)
+               nlt_a->flag |= NLATRACK_ACTIVE;
 }
 
+/* Check if there is any space in the last track to add the given strip */
+short BKE_nlatrack_has_space (NlaTrack *nlt, float start, float end)
+{
+       NlaStrip *strip;
+       
+       /* sanity checks */
+       if ((nlt == NULL) || IS_EQ(start, end))
+               return 0;
+       if (start > end) {
+               puts("BKE_nlatrack_has_space error... start and end arguments swapped");
+               SWAP(float, start, end);
+       }
+       
+       /* loop over NLA strips checking for any overlaps with this area... */
+       for (strip= nlt->strips.first; strip; strip= strip->next) {
+               /* if start frame of strip is past the target end-frame, that means that
+                * we've gone past the window we need to check for, so things are fine
+                */
+               if (strip->start > end)
+                       return 1;
+               
+               /* if the end of the strip is greater than either of the boundaries, the range
+                * must fall within the extents of the strip
+                */
+               if ((strip->end > start) || (strip->end > end))
+                       return 0;
+       }
+       
+       /* if we are still here, we haven't encountered any overlapping strips */
+       return 1;
+}
 
-/* not strip itself! */
-void free_actionstrip(bActionStrip* strip)
+/* 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 (NlaTrack *nlt)
 {
-       if (!strip)
+       ListBase tmp = {NULL, NULL};
+       NlaStrip *strip, *sstrip;
+       
+       /* sanity checks */
+       if ELEM(NULL, nlt, nlt->strips.first)
                return;
+               
+       /* we simply perform insertion sort on this list, since it is assumed that per track,
+        * there are only likely to be at most 5-10 strips
+        */
+       for (strip= nlt->strips.first; strip; strip= strip->next) {
+               short not_added = 1;
+               
+               /* remove this strip from the list, and add it to the new list, searching from the end of 
+                * the list, assuming that the lists are in order 
+                */
+               BLI_remlink(&nlt->strips, strip);
+               
+               for (sstrip= tmp.last; not_added && sstrip; sstrip= sstrip->prev) {
+                       /* check if add after */
+                       if (sstrip->end < strip->start) {
+                               BLI_insertlinkafter(&tmp, sstrip, strip);
+                               not_added= 0;
+                               break;
+                       }
+               }
+               
+               /* add before first? */
+               if (not_added)
+                       BLI_addhead(&tmp, strip);
+       }
+       
+       /* reassign the start and end points of the strips */
+       nlt->strips.first= tmp.first;
+       nlt->strips.last= tmp.last;
+}
 
-       if (strip->act){
-               strip->act->id.us--;
-               strip->act = NULL;
+/* NLA Strips -------------------------------------- */
+
+/* Find the active NLA-strip within the given track */
+NlaStrip *BKE_nlastrip_find_active (NlaTrack *nlt)
+{
+       NlaStrip *strip;
+       
+       /* sanity check */
+       if ELEM(NULL, nlt, nlt->strips.first)
+               return NULL;
+               
+       /* try to find the first active strip */
+       for (strip= nlt->strips.first; strip; strip= strip->next) {
+               if (strip->flag & NLASTRIP_FLAG_ACTIVE)
+                       return strip;
        }
-       if (strip->ipo){
-               strip->ipo->id.us--;
-               strip->ipo = NULL;
+       
+       /* none found */
+       return NULL;
+}
+
+/* Does the given NLA-strip fall within the given bounds (times)? */
+short BKE_nlastrip_within_bounds (NlaStrip *strip, float min, float max)
+{
+       const float stripLen= (strip) ? strip->end - strip->start : 0.0f;
+       const float boundsLen= (float)fabs(max - min);
+       
+       /* sanity checks */
+       if ((strip == NULL) || IS_EQ(stripLen, 0.0f) || IS_EQ(boundsLen, 0.0f))
+               return 0;
+       
+       /* only ok if at least part of the strip is within the bounding window
+        *      - first 2 cases cover when the strip length is less than the bounding area
+        *      - second 2 cases cover when the strip length is greater than the bounding area
+        */
+       if ( (stripLen < boundsLen) && 
+                !(IN_RANGE(strip->start, min, max) ||
+                  IN_RANGE(strip->end, min, max)) )
+       {
+               return 0;
        }
-       if (strip->modifiers.first) {
-               BLI_freelistN(&strip->modifiers);
+       if ( (stripLen > boundsLen) && 
+                !(IN_RANGE(min, strip->start, strip->end) ||
+                  IN_RANGE(max, strip->start, strip->end)) )
+       {
+               return 0;
        }
        
+       /* should be ok! */
+       return 1;
 }
 
-void free_nlastrips (ListBase *nlalist)
+/* Is the given NLA-strip the first one to occur for the given AnimData block */
+// TODO: make this an api method if necesary, but need to add prefix first
+short nlastrip_is_first (AnimData *adt, NlaStrip *strip)
 {
-       bActionStrip *strip;
+       NlaTrack *nlt;
+       NlaStrip *ns;
+       
+       /* sanity checks */
+       if ELEM(NULL, adt, strip)
+               return 0;
+               
+       /* check if strip has any strips before it */
+       if (strip->prev)
+               return 0;
+               
+       /* check other tracks to see if they have a strip that's earlier */
+       // TODO: or should we check that the strip's track is also the first?
+       for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next) {
+               /* only check the first strip, assuming that they're all in order */
+               ns= nlt->strips.first;
+               if (ns) {
+                       if (ns->start < strip->start)
+                               return 0;
+               }
+       }       
+       
+       /* should be first now */
+       return 1;
+}
 
-       if (!nlalist->first)
+/* Tools ------------------------------------------- */
+
+/* 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 editmode 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 (AnimData *adt)
+{
+       NlaStrip *strip;
+       
+       /* sanity checks */
+       // TODO: need to report the error for this
+       if ELEM(NULL, adt, adt->action) 
+               return;
+               
+       /* if the action is empty, we also shouldn't try to add to stack, 
+        * as that will cause us grief down the track
+        */
+       // TODO: what about modifiers?
+       if (action_has_motion(adt->action) == 0) {
+               printf("BKE_nla_action_pushdown(): action has no data \n");
                return;
+       }
+       
+       /* add a new NLA strip to the track, which references the active action */
+       strip= add_nlastrip_to_stack(adt, adt->action);
+       
+       /* do other necessary work on strip */  
+       if (strip) {
+               /* clear reference to action now that we've pushed it onto the stack */
+               adt->action->id.us--;
+               adt->action= NULL;
+               
+               /* if the strip is the first one in the track it lives in, check if there
+                * are strips in any other tracks that may be before this, and set the extend
+                * mode accordingly
+                */
+               if (nlastrip_is_first(adt, strip) == 0) {
+                       /* not first, so extend mode can only be NLASTRIP_EXTEND_HOLD_FORWARD not NLASTRIP_EXTEND_HOLD,
+                        * so that it doesn't override strips in previous tracks
+                        */
+                       strip->extendmode= NLASTRIP_EXTEND_HOLD_FORWARD;
+               }
+       }
+}
 
-       /* Do any specific freeing */
-       for (strip=nlalist->first; strip; strip=strip->next)
-       {
-               free_actionstrip (strip);
-       };
 
-       /* Free the whole list */
-       BLI_freelistN(nlalist);
+/* Find the active strip + track combo, and set them up as the tweaking track,
+ * and return if successful or not.
+ */
+short BKE_nla_tweakmode_enter (AnimData *adt)
+{
+       NlaTrack *nlt, *activeTrack=NULL;
+       NlaStrip *strip, *activeStrip=NULL;
+       
+       /* verify that data is valid */
+       if ELEM(NULL, adt, adt->nla_tracks.first)
+               return 0;
+               
+       /* if block is already in tweakmode, just leave, but we should report 
+        * that this block is in tweakmode (as our returncode)
+        */
+       // FIXME: hopefully the flag is correct!
+       if (adt->flag & ADT_NLA_EDIT_ON)
+               return 1;
+               
+       /* go over the tracks, finding the active one, and its active strip
+        *      - if we cannot find both, then there's nothing to do
+        */
+       for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next) {
+               /* check if active */
+               if (nlt->flag & NLATRACK_ACTIVE) {
+                       /* store reference to this active track */
+                       activeTrack= nlt;
+                       
+                       /* now try to find active strip */
+                       activeStrip= BKE_nlastrip_find_active(nlt);
+                       break;
+               }       
+       }
+       if ELEM3(NULL, activeTrack, activeStrip, activeStrip->act) {
+               printf("NLA tweakmode enter - neither active requirement found \n");
+               return 0;
+       }
+               
+       /* go over all the tracks up to the active one, tagging each strip that uses the same 
+        * action as the active strip, but leaving everything else alone
+        */
+       for (nlt= activeTrack->prev; nlt; nlt= nlt->prev) {
+               for (strip= nlt->strips.first; strip; strip= strip->next) {
+                       if (strip->act == activeStrip->act)
+                               strip->flag |= NLASTRIP_FLAG_TWEAKUSER;
+                       else
+                               strip->flag &= ~NLASTRIP_FLAG_TWEAKUSER; // XXX probably don't need to clear this...
+               }
+       }
+       
+       
+       /* go over all the tracks after AND INCLUDING the active one, tagging them as being disabled 
+        *      - the active track needs to also be tagged, otherwise, it'll overlap with the tweaks going on
+        */
+       for (nlt= activeTrack; nlt; nlt= nlt->next)
+               nlt->flag |= NLATRACK_DISABLED;
+       
+       /* handle AnimData level changes:
+        *      - 'real' active action to temp storage (no need to change user-counts)
+        *      - action of active strip set to be the 'active action'
+        *      - editing-flag for this AnimData block should also get turned on (for more efficient restoring)
+        */
+       adt->tmpact= adt->action;
+       adt->action= activeStrip->act;
+       adt->flag |= ADT_NLA_EDIT_ON;
+       
+       /* done! */
+       return 1;
 }
+
+/* Exit tweakmode for this AnimData block */
+void BKE_nla_tweakmode_exit (AnimData *adt)
+{
+       NlaTrack *nlt;
+       
+       /* verify that data is valid */
+       if ELEM(NULL, adt, adt->nla_tracks.first)
+               return;
+               
+       /* hopefully the flag is correct - skip if not on */
+       if ((adt->flag & ADT_NLA_EDIT_ON) == 0)
+               return;
+               
+       // TODO: need to sync the user-strip with the new state of the action!
+               
+       /* for all NLA-tracks, clear the 'disabled' flag */
+       for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next)
+               nlt->flag &= ~NLATRACK_DISABLED;
+       
+       /* handle AnimData level changes:
+        *      - 'real' active action is restored from storage
+        *      - storage pointer gets cleared (to avoid having bad notes hanging around)
+        *      - editing-flag for this AnimData block should also get turned off
+        */
+       adt->action= adt->tmpact;
+       adt->tmpact= NULL;
+       adt->flag &= ~ADT_NLA_EDIT_ON;
+}
+
+/* *************************************************** */
index d7619010808a4f4f8ecfcd2f3e69860ac57443bb..7a83162f7a747f0db238dc2d1d7d7c0fef8a5b49 100644 (file)
@@ -1193,18 +1193,12 @@ Object *copy_object(Object *ob)
                        armature_rebuild_pose(obn, obn->data);
        }
        copy_defgroups(&obn->defbase, &ob->defbase);
-#if 0 // XXX old animation system
-       copy_nlastrips(&obn->nlastrips, &ob->nlastrips);
-#endif // XXX old animation system
        copy_constraints(&obn->constraints, &ob->constraints);
 
        /* increase user numbers */
        id_us_plus((ID *)obn->data);
-#if 0 // XXX old animation system
-       id_us_plus((ID *)obn->ipo);
-       id_us_plus((ID *)obn->action);
-#endif // XXX old animation system
        id_us_plus((ID *)obn->dup_group);
+       // FIXME: add this for animdata too...
 
        for(a=0; a<obn->totcol; a++) id_us_plus((ID *)obn->mat[a]);
        
index c459d7669374db081b70239c5dc6a84d2e0a2e6c..e3869d4bc8a06dc5daec823f9485bd2694f1f952 100644 (file)
@@ -1657,10 +1657,26 @@ static void lib_link_constraint_channels(FileData *fd, ID *id, ListBase *chanbas
 
 /* Data Linking ----------------------------- */
 
+static void lib_link_fmodifiers(FileData *fd, ID *id, ListBase *list)
+{
+       FModifier *fcm;
+       
+       for (fcm= list->first; fcm; fcm= fcm->next) {
+               /* data for specific modifiers */
+               switch (fcm->type) {
+                       case FMODIFIER_TYPE_PYTHON:
+                       {
+                               FMod_Python *data= (FMod_Python *)fcm->data;
+                               data->script = newlibadr(fd, id->lib, data->script);
+                       }
+                               break;
+               }
+       }
+}
+
 static void lib_link_fcurves(FileData *fd, ID *id, ListBase *list) 
 {
        FCurve *fcu;
-       FModifier *fcm;
        
        /* relink ID-block references... */
        for (fcu= list->first; fcu; fcu= fcu->next) {
@@ -1674,16 +1690,45 @@ static void lib_link_fcurves(FileData *fd, ID *id, ListBase *list)
                }
                
                /* modifiers */
-               for (fcm= fcu->modifiers.first; fcm; fcm= fcm->next) {
-                       /* data for specific modifiers */
-                       switch (fcm->type) {
-                               case FMODIFIER_TYPE_PYTHON:
-                               {
-                                       FMod_Python *data= (FMod_Python *)fcm->data;
-                                       data->script = newlibadr(fd, id->lib, data->script);
-                               }
-                                       break;
+               lib_link_fmodifiers(fd, id, &fcu->modifiers);
+       }
+}
+
+
+/* NOTE: this assumes that link_list has already been called on the list */
+static void direct_link_fmodifiers(FileData *fd, ListBase *list)
+{
+       FModifier *fcm;
+       
+       for (fcm= list->first; fcm; fcm= fcm->next) {
+               /* relink general data */
+               fcm->data = newdataadr(fd, fcm->data);
+               fcm->edata= NULL;
+               
+               /* do relinking of data for specific types */
+               switch (fcm->type) {
+                       case FMODIFIER_TYPE_GENERATOR:
+                       {
+                               FMod_Generator *data= (FMod_Generator *)fcm->data;
+                               
+                               data->coefficients= newdataadr(fd, data->coefficients);
+                       }
+                               break;
+                       case FMODIFIER_TYPE_ENVELOPE:
+                       {
+                               FMod_Envelope *data= (FMod_Envelope *)fcm->data;
+                               
+                               data->data= newdataadr(fd, data->data);
                        }
+                               break;
+                       case FMODIFIER_TYPE_PYTHON:
+                       {
+                               FMod_Python *data= (FMod_Python *)fcm->data;
+                               
+                               data->prop = newdataadr(fd, data->prop);
+                               IDP_DirectLinkProperty(data->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+                       }
+                               break;
                }
        }
 }
@@ -1692,7 +1737,6 @@ static void lib_link_fcurves(FileData *fd, ID *id, ListBase *list)
 static void direct_link_fcurves(FileData *fd, ListBase *list)
 {
        FCurve *fcu;
-       FModifier *fcm;
        
        /* link F-Curve data to F-Curve again (non ID-libs) */
        for (fcu= list->first; fcu; fcu= fcu->next) {
@@ -1720,37 +1764,7 @@ static void direct_link_fcurves(FileData *fd, ListBase *list)
                
                /* modifiers */
                link_list(fd, &fcu->modifiers);
-               for (fcm= fcu->modifiers.first; fcm; fcm= fcm->next) {
-                       /* relink general data */
-                       fcm->data = newdataadr(fd, fcm->data);
-                       fcm->edata= NULL;
-                       
-                       /* do relinking of data for specific types */
-                       switch (fcm->type) {
-                               case FMODIFIER_TYPE_GENERATOR:
-                               {
-                                       FMod_Generator *data= (FMod_Generator *)fcm->data;
-                                       
-                                       data->coefficients= newdataadr(fd, data->coefficients);
-                               }
-                                       break;
-                               case FMODIFIER_TYPE_ENVELOPE:
-                               {
-                                       FMod_Envelope *data= (FMod_Envelope *)fcm->data;
-                                       
-                                       data->data= newdataadr(fd, data->data);
-                               }
-                                       break;
-                               case FMODIFIER_TYPE_PYTHON:
-                               {
-                                       FMod_Python *data= (FMod_Python *)fcm->data;
-                                       
-                                       data->prop = newdataadr(fd, data->prop);
-                                       IDP_DirectLinkProperty(data->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
-                               }
-                                       break;
-                       }
-               }
+               direct_link_fmodifiers(fd, &fcu->modifiers);
        }
 }
 
@@ -1802,6 +1816,44 @@ static void direct_link_action(FileData *fd, bAction *act)
        }
 }
 
+
+static void lib_link_nladata (FileData *fd, ID *id, ListBase *list)
+{
+       NlaTrack *nlt;
+       NlaStrip *strip;
+       
+       /* we only acare about the NLA strips inside the tracks */
+       for (nlt= list->first; nlt; nlt= nlt->next) {
+               for (strip= nlt->strips.first; strip; strip= strip->next) {
+                       /* reassign the counted-reference to action */
+                       strip->act = newlibadr_us(fd, id->lib, strip->act);
+               }
+       }
+}
+
+/* NOTE: this assumes that link_list has already been called on the list */
+static void direct_link_nladata(FileData *fd, ListBase *list)
+{
+       NlaTrack *nlt;
+       NlaStrip *strip;
+       
+       for (nlt= list->first; nlt; nlt= nlt->next) {
+               /* relink list of strips */
+               link_list(fd, &nlt->strips);
+               
+               /* relink strip data */
+               for (strip= nlt->strips.first; strip; strip= strip->next) {
+                       /* strip's F-Curves */
+                       link_list(fd, &strip->fcurves);
+                       direct_link_fcurves(fd, &strip->fcurves);
+                       
+                       /* strip's F-Modifiers */
+                       link_list(fd, &strip->modifiers);
+                       direct_link_fcurves(fd, &strip->modifiers);
+               }
+       }
+}
+
 /* ------- */
 
 static void lib_link_keyingsets(FileData *fd, ID *id, ListBase *list)
@@ -1851,7 +1903,7 @@ static void lib_link_animdata(FileData *fd, ID *id, AnimData *adt)
        /* overrides don't have lib-link for now, so no need to do anything */
        
        /* link NLA-data */
-       // TODO... 
+       lib_link_nladata(fd, id, &adt->nla_tracks);
 }
 
 static void direct_link_animdata(FileData *fd, AnimData *adt)
@@ -1868,7 +1920,8 @@ static void direct_link_animdata(FileData *fd, AnimData *adt)
        // TODO...
        
        /* link NLA-data */
-       // TODO...
+       link_list(fd, &adt->nla_tracks);
+       direct_link_nladata(fd, &adt->nla_tracks);
 }      
 
 /* ************ READ NODE TREE *************** */
@@ -4637,6 +4690,11 @@ static void direct_link_screen(FileData *fd, bScreen *sc)
                                sipo->ads= newdataadr(fd, sipo->ads);
                                sipo->ghostCurves.first= sipo->ghostCurves.last= NULL;
                        }
+                       else if (sl->spacetype==SPACE_NLA) {
+                               SpaceNla *snla= (SpaceNla*)sl;
+                               
+                               snla->ads= newdataadr(fd, snla->ads);
+                       }
                        else if (sl->spacetype==SPACE_OUTLINER) {
                                SpaceOops *soops= (SpaceOops*) sl;
                                
@@ -5629,7 +5687,7 @@ static void area_add_window_regions(ScrArea *sa, SpaceLink *sl, ListBase *lb)
                                ar->v2d.scroll = (V2D_SCROLL_BOTTOM|V2D_SCROLL_SCALE_HORIZONTAL);
                                ar->v2d.scroll |= (V2D_SCROLL_RIGHT);
                                ar->v2d.keepzoom= V2D_LOCKZOOM_Y;
-                               ar->v2d.align= V2D_ALIGN_NO_POS_Y;
+                               ar->v2d.align= V2D_ALIGN_NO_NEG_Y;
                                ar->v2d.flag = V2D_VIEWSYNC_AREA_VERTICAL;
                                break;
                        }
@@ -9343,6 +9401,8 @@ static void expand_keyingsets(FileData *fd, Main *mainvar, ListBase *list)
 static void expand_animdata(FileData *fd, Main *mainvar, AnimData *adt)
 {
        FCurve *fcd;
+       NlaTrack *nlt;
+       NlaStrip *strip;
        
        /* own action */
        expand_doit(fd, mainvar, adt->action);
@@ -9355,6 +9415,12 @@ static void expand_animdata(FileData *fd, Main *mainvar, AnimData *adt)
                for (dtar= driver->targets.first; dtar; dtar= dtar->next)
                        expand_doit(fd, mainvar, dtar->id);
        }
+       
+       /* nla-data - referenced actions */
+       for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next) {
+               for (strip= nlt->strips.first; strip; strip= strip->next) 
+                       expand_doit(fd, mainvar, strip->act);
+       }
 }      
 
 static void expand_particlesettings(FileData *fd, Main *mainvar, ParticleSettings *part)
index 943e23861adc2c086468a351837d355af380084b..5cf46b6fd281e33da33436181f33eaee8004c163 100644 (file)
@@ -760,10 +760,59 @@ static void write_actuators(WriteData *wd, ListBase *lb)
        }
 }
 
+static void write_fmodifiers(WriteData *wd, ListBase *fmodifiers)
+{
+       FModifier *fcm;
+       
+       /* Modifiers */
+       for (fcm= fmodifiers->first; fcm; fcm= fcm->next) {
+               FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm);
+               
+               /* Write the specific data */
+               if (fmi && fcm->data) {
+                       /* firstly, just write the plain fmi->data struct */
+                       writestruct(wd, DATA, fmi->structName, 1, fcm->data);
+                       
+                       /* do any modifier specific stuff */
+                       switch (fcm->type) {
+                               case FMODIFIER_TYPE_GENERATOR:
+                               {
+                                       FMod_Generator *data= (FMod_Generator *)fcm->data;
+                                       
+                                       /* write coefficients array */
+                                       if (data->coefficients)
+                                               writedata(wd, DATA, sizeof(float)*(data->arraysize), data->coefficients);
+                               }
+                                       break;
+                               case FMODIFIER_TYPE_ENVELOPE:
+                               {
+                                       FMod_Envelope *data= (FMod_Envelope *)fcm->data;
+                                       
+                                       /* write envelope data */
+                                       if (data->data)
+                                               writedata(wd, DATA, sizeof(FCM_EnvelopeData)*(data->totvert), data->data);
+                               }
+                                       break;
+                               case FMODIFIER_TYPE_PYTHON:
+                               {
+                                       FMod_Python *data = (FMod_Python *)fcm->data;
+                                       
+                                       /* Write ID Properties -- and copy this comment EXACTLY for easy finding
+                                        of library blocks that implement this.*/
+                                       IDP_WriteProperty(data->prop, wd);
+                               }
+                                       break;
+                       }
+               }
+               
+               /* Write the modifier */
+               writestruct(wd, DATA, "FModifier", 1, fcm);
+       }
+}
+
 static void write_fcurves(WriteData *wd, ListBase *fcurves)
 {
        FCurve *fcu;
-       FModifier *fcm;
        
        for (fcu=fcurves->first; fcu; fcu=fcu->next) {
                /* F-Curve */
@@ -794,50 +843,8 @@ static void write_fcurves(WriteData *wd, ListBase *fcurves)
                        }
                }
                
-               /* Modifiers */
-               for (fcm= fcu->modifiers.first; fcm; fcm= fcm->next) {
-                       FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm);
-                       
-                       /* Write the specific data */
-                       if (fmi && fcm->data) {
-                               /* firstly, just write the plain fmi->data struct */
-                               writestruct(wd, DATA, fmi->structName, 1, fcm->data);
-                               
-                               /* do any modifier specific stuff */
-                               switch (fcm->type) {
-                                       case FMODIFIER_TYPE_GENERATOR:
-                                       {
-                                               FMod_Generator *data= (FMod_Generator *)fcm->data;
-                                               
-                                               /* write coefficients array */
-                                               if (data->coefficients)
-                                                       writedata(wd, DATA, sizeof(float)*(data->arraysize), data->coefficients);
-                                       }
-                                               break;
-                                       case FMODIFIER_TYPE_ENVELOPE:
-                                       {
-                                               FMod_Envelope *data= (FMod_Envelope *)fcm->data;
-                                               
-                                               /* write envelope data */
-                                               if (data->data)
-                                                       writedata(wd, DATA, sizeof(FCM_EnvelopeData)*(data->totvert), data->data);
-                                       }
-                                               break;
-                                       case FMODIFIER_TYPE_PYTHON:
-                                       {
-                                               FMod_Python *data = (FMod_Python *)fcm->data;
-                                               
-                                               /* Write ID Properties -- and copy this comment EXACTLY for easy finding
-                                                of library blocks that implement this.*/
-                                               IDP_WriteProperty(data->prop, wd);
-                                       }
-                                               break;
-                               }
-                       }
-                       
-                       /* Write the modifier */
-                       writestruct(wd, DATA, "FModifier", 1, fcm);
-               }
+               /* write F-Modifiers */
+               write_fmodifiers(wd, &fcu->modifiers);
        }
 }
 
@@ -888,6 +895,29 @@ static void write_keyingsets(WriteData *wd, ListBase *list)
        }
 }
 
+static void write_nladata(WriteData *wd, ListBase *nlabase)
+{
+       NlaTrack *nlt;
+       NlaStrip *strip;
+       
+       /* write all the tracks */
+       for (nlt= nlabase->first; nlt; nlt= nlt->next) {
+               /* write the track first */
+               writestruct(wd, DATA, "NlaTrack", 1, nlt);
+               
+               for (strip= nlt->strips.first; strip; strip= strip->next) {
+                       /* write the strip first */
+                       writestruct(wd, DATA, "NlaStrip", 1, strip);
+                       
+                       /* write the strip's F-Curves and modifiers */
+                       write_fcurves(wd, &strip->fcurves);
+                       write_fmodifiers(wd, &strip->modifiers);
+                       
+                       // TODO write the remaps
+               }
+       }
+}
+
 static void write_animdata(WriteData *wd, AnimData *adt)
 {
        AnimOverride *aor;
@@ -899,14 +929,17 @@ static void write_animdata(WriteData *wd, AnimData *adt)
        write_fcurves(wd, &adt->drivers);
        
        /* write overrides */
+       // FIXME: are these needed?
        for (aor= adt->overrides.first; aor; aor= aor->next) {
                /* overrides consist of base data + rna_path */
                writestruct(wd, DATA, "AnimOverride", 1, aor);
                writedata(wd, DATA, strlen(aor->rna_path)+1, aor->rna_path);
        }
        
+       // TODO write the remaps (if they are needed)
+       
        /* write NLA data */
-       // XXX todo...
+       write_nladata(wd, &adt->nla_tracks);
 }
 
 static void write_constraints(WriteData *wd, ListBase *conlist)
@@ -1876,7 +1909,10 @@ static void write_screens(WriteData *wd, ListBase *scrbase)
                                        writestruct(wd, DATA, "SpaceSound", 1, sl);
                                }
                                else if(sl->spacetype==SPACE_NLA){
-                                       writestruct(wd, DATA, "SpaceNla", 1, sl);
+                                       SpaceNla *snla= (SpaceNla *)sl;
+                                       
+                                       writestruct(wd, DATA, "SpaceNla", 1, snla);
+                                       if(snla->ads) writestruct(wd, DATA, "bDopeSheet", 1, snla->ads);
                                }
                                else if(sl->spacetype==SPACE_TIME){
                                        writestruct(wd, DATA, "SpaceTime", 1, sl);
index 9e0e50a8de53f1f04725f06253d64babc2c7eecd..59fb56f3c3525484038f74b5f0f0d436696bb9e0 100644 (file)
 /* ************************************************************************** */
 /* CHANNELS API */
 
-/* -------------------------- Internal Macros ------------------------------- */
-
-/* set/clear/toggle macro 
- *     - channel - channel with a 'flag' member that we're setting
- *     - smode - 0=clear, 1=set, 2=toggle
- *     - sflag - bitflag to set
- */
-#define ACHANNEL_SET_FLAG(channel, smode, sflag) \
-       { \
-               if (smode == ACHANNEL_SETFLAG_TOGGLE)   (channel)->flag ^= (sflag); \
-               else if (smode == ACHANNEL_SETFLAG_ADD) (channel)->flag |= (sflag); \
-               else                                                                    (channel)->flag &= ~(sflag); \
-       }
-       
-/* set/clear/toggle macro, where the flag is negative 
- *     - channel - channel with a 'flag' member that we're setting
- *     - smode - 0=clear, 1=set, 2=toggle
- *     - sflag - bitflag to set
- */
-#define ACHANNEL_SET_FLAG_NEG(channel, smode, sflag) \
-       { \
-               if (smode == ACHANNEL_SETFLAG_TOGGLE)   (channel)->flag ^= (sflag); \
-               else if (smode == ACHANNEL_SETFLAG_ADD) (channel)->flag &= ~(sflag); \
-               else                                                                    (channel)->flag |= (sflag); \
-       }
-
 /* -------------------------- Exposed API ----------------------------------- */
 
 /* Set the given animation-channel as the active one for the active context */
-void ANIM_set_active_channel (void *data, short datatype, int filter, void *channel_data, short channel_type)
+void ANIM_set_active_channel (bAnimContext *ac, void *data, short datatype, int filter, void *channel_data, short channel_type)
 {
        ListBase anim_data = {NULL, NULL};
        bAnimListElem *ale;
        
        /* try to build list of filtered items */
-       // XXX we don't need/supply animcontext for now, since in this case, there's nothing really essential there that isn't already covered
-       ANIM_animdata_filter(NULL, &anim_data, filter, data, datatype);
+       ANIM_animdata_filter(ac, &anim_data, filter, data, datatype);
        if (anim_data.first == NULL)
                return;
                
@@ -149,6 +122,13 @@ void ANIM_set_active_channel (void *data, short datatype, int filter, void *chan
                                ACHANNEL_SET_FLAG(fcu, ACHANNEL_SETFLAG_CLEAR, FCURVE_ACTIVE);
                        }
                                break;
+                       case ANIMTYPE_NLATRACK:
+                       {
+                               NlaTrack *nlt= (NlaTrack *)ale->data;
+                               
+                               ACHANNEL_SET_FLAG(nlt, ACHANNEL_SETFLAG_CLEAR, NLATRACK_ACTIVE);
+                       }
+                               break;
                }
        }
        
@@ -167,6 +147,12 @@ void ANIM_set_active_channel (void *data, short datatype, int filter, void *chan
                                fcu->flag |= FCURVE_ACTIVE;
                        }
                                break;
+                       case ANIMTYPE_NLATRACK:
+                       {
+                               NlaTrack *nlt= (NlaTrack *)channel_data;
+                               nlt->flag |= NLATRACK_ACTIVE;
+                       }
+                               break;
                }
        }
        
@@ -217,6 +203,10 @@ void ANIM_deselect_anim_channels (void *data, short datatype, short test, short
                                        if (ale->flag & FCURVE_SELECTED)
                                                sel= ACHANNEL_SETFLAG_CLEAR;
                                        break;
+                               case ANIMTYPE_NLATRACK:
+                                       if (ale->flag & NLATRACK_SELECTED)
+                                               sel= ACHANNEL_SETFLAG_CLEAR;
+                                       break;
                        }
                }
        }
@@ -263,6 +253,14 @@ void ANIM_deselect_anim_channels (void *data, short datatype, short test, short
                                fcu->flag &= ~FCURVE_ACTIVE;
                        }
                                break;
+                       case ANIMTYPE_NLATRACK:
+                       {
+                               NlaTrack *nlt= (NlaTrack *)ale->data;
+                               
+                               ACHANNEL_SET_FLAG(nlt, sel, NLATRACK_SELECTED);
+                               nlt->flag &= ~NLATRACK_ACTIVE;
+                       }
+                               break;
                }
        }
        
@@ -1474,7 +1472,7 @@ static void mouse_anim_channels (bAnimContext *ac, float x, int channel_index, s
                                
                                /* if group is selected now, make group the 'active' one in the visible list */
                                if (agrp->flag & AGRP_SELECTED)
-                                       ANIM_set_active_channel(ac->data, ac->datatype, filter, agrp, ANIMTYPE_GROUP);
+                                       ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, agrp, ANIMTYPE_GROUP);
                        }
                }
                        break;
@@ -1520,7 +1518,7 @@ static void mouse_anim_channels (bAnimContext *ac, float x, int channel_index, s
                                
                                /* if F-Curve is selected now, make F-Curve the 'active' one in the visible list */
                                if (fcu->flag & FCURVE_SELECTED)
-                                       ANIM_set_active_channel(ac->data, ac->datatype, filter, fcu, ANIMTYPE_FCURVE);
+                                       ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, fcu, ANIMTYPE_FCURVE);
                        }
                }
                        break;
index afad396607bfb6991b23d388d649e27cb82863ad..32405571b579c9b8970c096fd387fc1d58ae9d53 100644 (file)
@@ -195,7 +195,7 @@ static short actedit_get_context (bAnimContext *ac, SpaceAction *saction)
        }
 }
 
-/* ----------- Private Stuff - IPO Editor ------------- */
+/* ----------- Private Stuff - Graph Editor ------------- */
 
 /* Get data being edited in Graph Editor (depending on current 'mode') */
 static short graphedit_get_context (bAnimContext *ac, SpaceIpo *sipo)
@@ -237,6 +237,26 @@ static short graphedit_get_context (bAnimContext *ac, SpaceIpo *sipo)
        }
 }
 
+/* ----------- Private Stuff - NLA Editor ------------- */
+
+/* Get data being edited in Graph Editor (depending on current 'mode') */
+static short nlaedit_get_context (bAnimContext *ac, SpaceNla *snla)
+{
+       /* init dopesheet data if non-existant (i.e. for old files) */
+       if (snla->ads == NULL)
+               snla->ads= MEM_callocN(sizeof(bDopeSheet), "NlaEdit DopeSheet");
+       
+       /* sync settings with current view status, then return appropriate data */
+       /* update scene-pointer (no need to check for pinning yet, as not implemented) */
+       snla->ads->source= (ID *)ac->scene;
+       snla->ads->filterflag |= ADS_FILTER_ONLYNLA;
+       
+       ac->datatype= ANIMCONT_NLA;
+       ac->data= snla->ads;
+       
+       return 1;
+}
+
 /* ----------- Public API --------------- */
 
 /* Obtain current anim-data context, given that context info from Blender context has already been set 
@@ -264,6 +284,13 @@ short ANIM_animdata_context_getdata (bAnimContext *ac)
                                ok= graphedit_get_context(ac, sipo);
                        }
                                break;
+                               
+                       case SPACE_NLA:
+                       {
+                               SpaceNla *snla= (SpaceNla *)sa->spacedata.first;
+                               ok= nlaedit_get_context(ac, snla);
+                       }
+                               break;
                }
        }
        
@@ -313,6 +340,68 @@ short ANIM_animdata_get_context (const bContext *C, bAnimContext *ac)
 /* quick macro to test if AnimData is usable for drivers */
 #define ANIMDATA_HAS_DRIVERS(id) ((id)->adt && (id)->adt->drivers.first)
 
+/* quick macro to test if AnimData is usable for NLA */
+#define ANIMDATA_HAS_NLA(id) ((id)->adt && (id)->adt->nla_tracks.first)
+
+
+/* Quick macro to test for all three avove usability tests, performing the appropriate provided 
+ * action for each when the AnimData context is appropriate. 
+ *
+ * Priority order for this goes (most important, to least): AnimData blocks, NLA, Drivers, Keyframes.
+ *
+ * For this to work correctly, a standard set of data needs to be available within the scope that this
+ * gets called in: 
+ *     - ListBase anim_data;
+ *     - bDopeSheet *ads;
+ *     - bAnimListElem *ale;
+ *     - int items;
+ *
+ *     - id: ID block which should have an AnimData pointer following it immediately, to use
+ *     - adtOk: line or block of code to execute for AnimData-blocks case (usually ANIMDATA_ADD_ANIMDATA)
+ *     - nlaOk: line or block of code to execute for NLA case
+ *     - driversOk: line or block of code to execute for Drivers case
+ *     - keysOk: line or block of code for Keyframes case
+ */
+#define ANIMDATA_FILTER_CASES(id, adtOk, nlaOk, driversOk, keysOk) \
+       {\
+               if (filter_mode & ANIMFILTER_ANIMDATA) {\
+                       if ((id)->adt) {\
+                               adtOk\
+                       }\
+               }\
+               else if (ads->filterflag & ADS_FILTER_ONLYNLA) {\
+                       if (ANIMDATA_HAS_NLA(id)) {\
+                               nlaOk\
+                       }\
+                       else if (!(ads->filterflag & ADS_FILTER_NLA_NOACT) && ANIMDATA_HAS_KEYS(id)) {\
+                               nlaOk\
+                       }\
+               }\
+               else if (ads->filterflag & ADS_FILTER_ONLYDRIVERS) {\
+                       if (ANIMDATA_HAS_DRIVERS(id)) {\
+                               driversOk\
+                       }\
+               }\
+               else {\
+                       if (ANIMDATA_HAS_KEYS(id)) {\
+                               keysOk\
+                       }\
+               }\
+       }
+
+
+/* quick macro to add a pointer to an AnimData block as a channel */
+#define ANIMDATA_ADD_ANIMDATA(id) \
+       {\
+               ale= make_new_animlistelem((id)->adt, ANIMTYPE_ANIMDATA, NULL, ANIMTYPE_NONE, (ID *)id);\
+               if (ale) {\
+                       BLI_addtail(anim_data, ale);\
+                       items++;\
+               }\
+       }
+       
+
+
 /* quick macro to test if a anim-channel (F-Curve, Group, etc.) is selected in an acceptable way */
 #define ANIMCHANNEL_SELOK(test_func) \
                ( !(filter_mode & (ANIMFILTER_SEL|ANIMFILTER_UNSEL)) || \
@@ -494,6 +583,25 @@ bAnimListElem *make_new_animlistelem (void *data, short datatype, void *owner, s
                                ale->datatype= ALE_GPFRAME;
                        }
                                break;
+                               
+                       case ANIMTYPE_NLATRACK:
+                       {
+                               NlaTrack *nlt= (NlaTrack *)data;
+                               
+                               ale->flag= nlt->flag;
+                               
+                                       // XXX or should this be done some other way?
+                               ale->key_data= &nlt->strips;
+                               ale->datatype= ALE_NLASTRIP;
+                       }
+                               break;
+                       case ANIMTYPE_NLAACTION:
+                       {
+                               /* nothing to include for now... nothing editable from NLA-perspective here */
+                               ale->key_data= NULL;
+                               ale->datatype= ALE_NONE;
+                       }
+                               break;
                }
        }
        
@@ -522,7 +630,6 @@ static int animdata_filter_fcurves (ListBase *anim_data, FCurve *first, bActionG
                                if ( ANIMCHANNEL_SELOK(SEL_FCU(fcu)) ) {
                                        /* only include if this curve is active */
                                        if (!(filter_mode & ANIMFILTER_ACTIVE) || (fcu->flag & FCURVE_ACTIVE)) {
-                                               /* owner/ownertype will be either object or action-channel, depending if it was dopesheet or part of an action */
                                                ale= make_new_animlistelem(fcu, ANIMTYPE_FCURVE, owner, ownertype, owner_id);
                                                
                                                if (ale) {
@@ -610,6 +717,60 @@ static int animdata_filter_action (ListBase *anim_data, bAction *act, int filter
        return items;
 }
 
+static int animdata_filter_nla (ListBase *anim_data, AnimData *adt, int filter_mode, void *owner, short ownertype, ID *owner_id)
+{
+       bAnimListElem *ale;
+       NlaTrack *nlt;
+       int items = 0;
+       
+       /* loop over NLA Tracks - assume that the caller of this has already checked that these should be included */
+       for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next) {
+               /* only work with this channel and its subchannels if it is editable */
+               if (!(filter_mode & ANIMFILTER_FOREDIT) || EDITABLE_NLT(nlt)) {
+                       /* only include this track if selected in a way consistent with the filtering requirements */
+                       if ( ANIMCHANNEL_SELOK(SEL_NLT(nlt)) ) {
+                               /* only include if this track is active */
+                               // XXX keep this?
+                               if (!(filter_mode & ANIMFILTER_ACTIVE) || (nlt->flag & NLATRACK_ACTIVE)) {
+                                       ale= make_new_animlistelem(nlt, ANIMTYPE_NLATRACK, owner, ownertype, owner_id);
+                                               
+                                       if (ale) {
+                                               BLI_addtail(anim_data, ale);
+                                               items++;
+                                       }
+                               }
+                               
+                               /* if we're in NLA-tweakmode, if this track was active, that means that it was the last active one */
+                               // FIXME: the channels after should still get drawn, just 'differently', and after an active-action channel
+                               if ((adt->flag & ADT_NLA_EDIT_ON) && (nlt->flag & NLATRACK_ACTIVE))
+                                       break;
+                       }
+               }
+       }
+       
+       /* if showing channels, include active action */
+       if (filter_mode & ANIMFILTER_CHANNELS) {
+               /* there isn't really anything editable here, so skip if need editable */
+               // TODO: currently, selection isn't checked since it doesn't matter
+               if ((filter_mode & ANIMFILTER_FOREDIT) == 0) { 
+                       /* just add the action track now (this MUST appear for drawing)
+                        *      - as AnimData may not have an action, we pass a dummy pointer just to get the list elem created, then
+                        *        overwrite this with the real value - REVIEW THIS...
+                        */
+                       ale= make_new_animlistelem((void *)(&adt->action), ANIMTYPE_NLAACTION, owner, ownertype, owner_id);
+                       ale->data= (adt->action) ? adt->action : NULL;
+                               
+                       if (ale) {
+                               BLI_addtail(anim_data, ale);
+                               items++;
+                       }
+               }
+       }
+       
+       /* return the number of items added to the list */
+       return items;
+}
+
 static int animdata_filter_shapekey (ListBase *anim_data, Key *key, int filter_mode, void *owner, short ownertype, ID *owner_id)
 {
        bAnimListElem *ale;
@@ -752,19 +913,19 @@ static int animdata_filter_dopesheet_mats (ListBase *anim_data, bDopeSheet *ads,
        /* firstly check that we actuallly have some materials, by gathering all materials in a temp list */
        for (a=0; a < ob->totcol; a++) {
                Material *ma= give_current_material(ob, a);
+               short ok = 0;
                
                /* for now, if no material returned, skip (this shouldn't confuse the user I hope) */
                if (ELEM(NULL, ma, ma->adt)) 
                        continue;
                
-               if ((ads->filterflag & ADS_FILTER_ONLYDRIVERS)==0) {
-                       if (ANIMDATA_HAS_KEYS(ma) == 0)
-                               continue;
-               }
-               else {
-                       if (ANIMDATA_HAS_DRIVERS(ma) == 0)
-                               continue;
-               }
+               /* check if ok */
+               ANIMDATA_FILTER_CASES(ma, 
+                       { /* AnimData blocks - do nothing... */ },
+                       ok=1;, 
+                       ok=1;, 
+                       ok=1;)
+               if (ok == 0) continue;
                
                /* make a temp list elem for this */
                ld= MEM_callocN(sizeof(LinkData), "DopeSheet-MaterialCache");
@@ -801,16 +962,13 @@ static int animdata_filter_dopesheet_mats (ListBase *anim_data, bDopeSheet *ads,
                                }
                        }
                        
-                       /* add material's F-Curve or Driver channels? */
+                       /* add material's animation data */
                        if (FILTER_MAT_OBJD(ma) || (filter_mode & ANIMFILTER_CURVESONLY)) {
-                               if ((ads->filterflag & ADS_FILTER_ONLYDRIVERS)==0) {
-                                       // XXX the 'owner' info here is still subject to improvement
-                                       items += animdata_filter_action(anim_data, ma->adt->action, filter_mode, ma, ANIMTYPE_DSMAT, (ID *)ma);
-                               }
-                               else {
-                                       // need to make the ownertype normal object here... (maybe type should be a separate one for clarity?)
-                                       items += animdata_filter_fcurves(anim_data, ma->adt->drivers.first, NULL, ma, ANIMTYPE_DSMAT, filter_mode, (ID *)ma);
-                               }       
+                               ANIMDATA_FILTER_CASES(ma, 
+                                       { /* AnimData blocks - do nothing... */ },
+                                       items += animdata_filter_nla(anim_data, ma->adt, filter_mode, ma, ANIMTYPE_DSMAT, (ID *)ma);, 
+                                       items += animdata_filter_fcurves(anim_data, ma->adt->drivers.first, NULL, ma, ANIMTYPE_DSMAT, filter_mode, (ID *)ma);, 
+                                       items += animdata_filter_action(anim_data, ma->adt->action, filter_mode, ma, ANIMTYPE_DSMAT, (ID *)ma);)
                        }
                }
        }
@@ -871,15 +1029,12 @@ static int animdata_filter_dopesheet_obdata (ListBase *anim_data, bDopeSheet *ad
        
        /* add object-data animation channels? */
        if ((expanded) || (filter_mode & ANIMFILTER_CURVESONLY)) {
-               /* Action or Drivers? */
-               if ((ads->filterflag & ADS_FILTER_ONLYDRIVERS) == 0) {
-                       // XXX the 'owner' info here is still subject to improvement
-                       items += animdata_filter_action(anim_data, iat->adt->action, filter_mode, iat, type, (ID *)iat);
-               }
-               else {
-                       // need to make the ownertype normal object here... (maybe type should be a separate one for clarity?)
-                       items += animdata_filter_fcurves(anim_data, adt->drivers.first, NULL, iat, type, filter_mode, (ID *)iat);
-               }
+               /* filtering for channels - nla, drivers, keyframes */
+               ANIMDATA_FILTER_CASES(iat, 
+                       { /* AnimData blocks - do nothing... */ },
+                       items+= animdata_filter_nla(anim_data, iat->adt, filter_mode, iat, type, (ID *)iat);,
+                       items+= animdata_filter_fcurves(anim_data, adt->drivers.first, NULL, iat, type, filter_mode, (ID *)iat);, 
+                       items += animdata_filter_action(anim_data, iat->adt->action, filter_mode, iat, type, (ID *)iat);)
        }
        
        /* return the number of items added to the list */
@@ -889,8 +1044,10 @@ static int animdata_filter_dopesheet_obdata (ListBase *anim_data, bDopeSheet *ad
 static int animdata_filter_dopesheet_ob (ListBase *anim_data, bDopeSheet *ads, Base *base, int filter_mode)
 {
        bAnimListElem *ale=NULL;
+       AnimData *adt = NULL;
        Object *ob= base->object;
        Key *key= ob_get_key(ob);
+       short obdata_ok = 0;
        int items = 0;
        
        /* add this object as a channel first */
@@ -909,73 +1066,83 @@ static int animdata_filter_dopesheet_ob (ListBase *anim_data, bDopeSheet *ads, B
        if ( (EXPANDED_OBJC(ob) == 0) && !(filter_mode & ANIMFILTER_CURVESONLY) )
                return items;
        
-       /* Action or Drivers */
-       if ((ads->filterflag & ADS_FILTER_ONLYDRIVERS) == 0) {
-               /* Action? */
-               if (ANIMDATA_HAS_KEYS(ob) /*&& !(ads->filterflag & ADS_FILTER_NOACTS)*/) {
-                       AnimData *adt= ob->adt;
-                       
-                       /* include action-expand widget? */
-                       if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & ANIMFILTER_CURVESONLY)) {
-                               ale= make_new_animlistelem(adt->action, ANIMTYPE_FILLACTD, base, ANIMTYPE_OBJECT, (ID *)ob);
-                               if (ale) {
-                                       BLI_addtail(anim_data, ale);
-                                       items++;
+       /* Action, Drivers, or NLA */
+       if (ob->adt) {
+               adt= ob->adt;
+               ANIMDATA_FILTER_CASES(ob,
+                       { /* AnimData blocks - do nothing... */ },
+                       { /* nla */
+#if 0
+                               /* include nla-expand widget? */
+                               if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & ANIMFILTER_CURVESONLY)) {
+                                       ale= make_new_animlistelem(adt->action, ANIMTYPE_FILLNLA, base, ANIMTYPE_OBJECT, (ID *)ob);
+                                       if (ale) {
+                                               BLI_addtail(anim_data, ale);
+                                               items++;
+                                       }
                                }
-                       }
-                       
-                       /* add F-Curve channels? */
-                       if (EXPANDED_ACTC(adt->action) || !(filter_mode & ANIMFILTER_CHANNELS)) {
-                               // need to make the ownertype normal object here... (maybe type should be a separate one for clarity?)
-                               items += animdata_filter_action(anim_data, adt->action, filter_mode, ob, ANIMTYPE_OBJECT, (ID *)ob); 
-                       }
-               }
-       }
-       else {
-               /* Drivers */
-               if (ANIMDATA_HAS_DRIVERS(ob)) {
-                       AnimData *adt= ob->adt;
-                       
-                       /* include drivers-expand widget? */
-                       if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & ANIMFILTER_CURVESONLY)) {
-                               ale= make_new_animlistelem(adt->action, ANIMTYPE_FILLDRIVERS, base, ANIMTYPE_OBJECT, (ID *)ob);
-                               if (ale) {
-                                       BLI_addtail(anim_data, ale);
-                                       items++;
+#endif
+                               
+                               /* add NLA tracks */
+                               items += animdata_filter_nla(anim_data, adt, filter_mode, ob, ANIMTYPE_OBJECT, (ID *)ob);
+                       },
+                       { /* drivers */
+                               /* include drivers-expand widget? */
+                               if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & ANIMFILTER_CURVESONLY)) {
+                                       ale= make_new_animlistelem(adt->action, ANIMTYPE_FILLDRIVERS, base, ANIMTYPE_OBJECT, (ID *)ob);
+                                       if (ale) {
+                                               BLI_addtail(anim_data, ale);
+                                               items++;
+                                       }
+                               }
+                               
+                               /* add F-Curve channels (drivers are F-Curves) */
+                               if (EXPANDED_DRVD(adt) || !(filter_mode & ANIMFILTER_CHANNELS)) {
+                                       // need to make the ownertype normal object here... (maybe type should be a separate one for clarity?)
+                                       items += animdata_filter_fcurves(anim_data, adt->drivers.first, NULL, ob, ANIMTYPE_OBJECT, filter_mode, (ID *)ob);
+                               }
+                       },
+                       { /* action (keyframes) */
+                               /* include action-expand widget? */
+                               if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & ANIMFILTER_CURVESONLY)) {
+                                       ale= make_new_animlistelem(adt->action, ANIMTYPE_FILLACTD, base, ANIMTYPE_OBJECT, (ID *)ob);
+                                       if (ale) {
+                                               BLI_addtail(anim_data, ale);
+                                               items++;
+                                       }
+                               }
+                               
+                               /* add F-Curve channels? */
+                               if (EXPANDED_ACTC(adt->action) || !(filter_mode & ANIMFILTER_CHANNELS)) {
+                                       // need to make the ownertype normal object here... (maybe type should be a separate one for clarity?)
+                                       items += animdata_filter_action(anim_data, adt->action, filter_mode, ob, ANIMTYPE_OBJECT, (ID *)ob); 
                                }
                        }
-                       
-                       /* add F-Curve channels (drivers are F-Curves) */
-                       if (EXPANDED_DRVD(adt) || !(filter_mode & ANIMFILTER_CHANNELS)) {
-                               // need to make the ownertype normal object here... (maybe type should be a separate one for clarity?)
-                               items += animdata_filter_fcurves(anim_data, adt->drivers.first, NULL, ob, ANIMTYPE_OBJECT, filter_mode, (ID *)ob);
-                       }
-               }
+               )
        }
        
+       
        /* ShapeKeys? */
        if ((key) && !(ads->filterflag & ADS_FILTER_NOSHAPEKEYS)) {
-               /* Animation or Drivers */
-               if ((ads->filterflag & ADS_FILTER_ONLYDRIVERS) == 0) {
-                       /* include shapekey-expand widget? */
-                       if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & ANIMFILTER_CURVESONLY)) {
-                               ale= make_new_animlistelem(key, ANIMTYPE_DSSKEY, base, ANIMTYPE_OBJECT, (ID *)ob);
-                               if (ale) {
-                                       BLI_addtail(anim_data, ale);
-                                       items++;
+               adt= key->adt;
+               ANIMDATA_FILTER_CASES(key,
+                       { /* AnimData blocks - do nothing... */ },
+                       { /* nla */
+#if 0
+                               /* include nla-expand widget? */
+                               if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & ANIMFILTER_CURVESONLY)) {
+                                       ale= make_new_animlistelem(adt->action, ANIMTYPE_FILLNLA, base, ANIMTYPE_OBJECT, (ID *)ob);
+                                       if (ale) {
+                                               BLI_addtail(anim_data, ale);
+                                               items++;
+                                       }
                                }
-                       }
-                       
-                       /* add channels */
-                       if (FILTER_SKE_OBJD(key) || (filter_mode & ANIMFILTER_CURVESONLY)) {
-                               items += animdata_filter_shapekey(anim_data, key, filter_mode, ob, ANIMTYPE_OBJECT, (ID *)ob);
-                       }
-               }
-               else {
-                       /* Drivers */
-                       if (ANIMDATA_HAS_DRIVERS(key)) {
-                               AnimData *adt= key->adt;
+#endif
                                
+                               /* add NLA tracks */
+                               items += animdata_filter_nla(anim_data, adt, filter_mode, ob, ANIMTYPE_OBJECT, (ID *)ob);
+                       },
+                       { /* drivers */
                                /* include shapekey-expand widget? */
                                if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & ANIMFILTER_CURVESONLY)) {
                                        ale= make_new_animlistelem(key, ANIMTYPE_DSSKEY, base, ANIMTYPE_OBJECT, (ID *)ob);
@@ -985,15 +1152,28 @@ static int animdata_filter_dopesheet_ob (ListBase *anim_data, bDopeSheet *ads, B
                                        }
                                }
                                
-                               /* add F-Curve channels (drivers are F-Curves) */
-                               if (FILTER_SKE_OBJD(key)/*EXPANDED_DRVD(adt)*/ || !(filter_mode & ANIMFILTER_CHANNELS)) {
-                                       // XXX owner info is messed up now...
-                                       items += animdata_filter_fcurves(anim_data, adt->drivers.first, NULL, ob, ANIMTYPE_OBJECT, filter_mode, (ID *)key);
+                               /* add channels */
+                               if (FILTER_SKE_OBJD(key) || (filter_mode & ANIMFILTER_CURVESONLY)) {
+                                       items += animdata_filter_shapekey(anim_data, key, filter_mode, ob, ANIMTYPE_OBJECT, (ID *)ob);
+                               }
+                       },
+                       { /* action (keyframes) */
+                               /* include shapekey-expand widget? */
+                               if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & ANIMFILTER_CURVESONLY)) {
+                                       ale= make_new_animlistelem(key, ANIMTYPE_DSSKEY, base, ANIMTYPE_OBJECT, (ID *)ob);
+                                       if (ale) {
+                                               BLI_addtail(anim_data, ale);
+                                               items++;
+                                       }
+                               }
+                               
+                               /* add channels */
+                               if (FILTER_SKE_OBJD(key) || (filter_mode & ANIMFILTER_CURVESONLY)) {
+                                       items += animdata_filter_shapekey(anim_data, key, filter_mode, ob, ANIMTYPE_OBJECT, (ID *)ob);
                                }
                        }
-               }
+               )
        }
-       
 
        /* Materials? */
        if ((ob->totcol) && !(ads->filterflag & ADS_FILTER_NOMAT))
@@ -1006,14 +1186,11 @@ static int animdata_filter_dopesheet_ob (ListBase *anim_data, bDopeSheet *ads, B
                        Camera *ca= (Camera *)ob->data;
                        
                        if ((ads->filterflag & ADS_FILTER_NOCAM) == 0) {
-                               if ((ads->filterflag & ADS_FILTER_ONLYDRIVERS)==0) {
-                                       if (ANIMDATA_HAS_KEYS(ca))
-                                               items += animdata_filter_dopesheet_obdata(anim_data, ads, base, filter_mode);
-                               }
-                               else {
-                                       if (ANIMDATA_HAS_DRIVERS(ca))
-                                               items += animdata_filter_dopesheet_obdata(anim_data, ads, base, filter_mode);
-                               }
+                               ANIMDATA_FILTER_CASES(ca,
+                                       { /* AnimData blocks - do nothing... */ },
+                                       obdata_ok= 1;,
+                                       obdata_ok= 1;,
+                                       obdata_ok= 1;)
                        }
                }
                        break;
@@ -1022,14 +1199,11 @@ static int animdata_filter_dopesheet_ob (ListBase *anim_data, bDopeSheet *ads, B
                        Lamp *la= (Lamp *)ob->data;
                        
                        if ((ads->filterflag & ADS_FILTER_NOLAM) == 0) {
-                               if ((ads->filterflag & ADS_FILTER_ONLYDRIVERS)==0) {
-                                       if (ANIMDATA_HAS_KEYS(la))
-                                               items += animdata_filter_dopesheet_obdata(anim_data, ads, base, filter_mode);
-                               }
-                               else {
-                                       if (ANIMDATA_HAS_DRIVERS(la))
-                                               items += animdata_filter_dopesheet_obdata(anim_data, ads, base, filter_mode);
-                               }
+                               ANIMDATA_FILTER_CASES(la,
+                                       { /* AnimData blocks - do nothing... */ },
+                                       obdata_ok= 1;,
+                                       obdata_ok= 1;,
+                                       obdata_ok= 1;)
                        }
                }
                        break;
@@ -1038,18 +1212,17 @@ static int animdata_filter_dopesheet_ob (ListBase *anim_data, bDopeSheet *ads, B
                        Curve *cu= (Curve *)ob->data;
                        
                        if ((ads->filterflag & ADS_FILTER_NOCUR) == 0) {
-                               if ((ads->filterflag & ADS_FILTER_ONLYDRIVERS)==0) {
-                                       if (ANIMDATA_HAS_KEYS(cu))
-                                               items += animdata_filter_dopesheet_obdata(anim_data, ads, base, filter_mode);
-                               }
-                               else {
-                                       if (ANIMDATA_HAS_DRIVERS(cu))
-                                               items += animdata_filter_dopesheet_obdata(anim_data, ads, base, filter_mode);
-                               }
+                               ANIMDATA_FILTER_CASES(cu,
+                                       { /* AnimData blocks - do nothing... */ },
+                                       obdata_ok= 1;,
+                                       obdata_ok= 1;,
+                                       obdata_ok= 1;)
                        }
                }
                        break;
        }
+       if (obdata_ok) 
+               items += animdata_filter_dopesheet_obdata(anim_data, ads, base, filter_mode);
        
        /* return the number of items added to the list */
        return items;
@@ -1058,6 +1231,7 @@ static int animdata_filter_dopesheet_ob (ListBase *anim_data, bDopeSheet *ads, B
 static int animdata_filter_dopesheet_scene (ListBase *anim_data, bDopeSheet *ads, Scene *sce, int filter_mode)
 {
        World *wo= sce->world;
+       AnimData *adt= NULL;
        bAnimListElem *ale;
        int items = 0;
        
@@ -1077,74 +1251,82 @@ static int animdata_filter_dopesheet_scene (ListBase *anim_data, bDopeSheet *ads
        if ( (EXPANDED_SCEC(sce) == 0) && !(filter_mode & ANIMFILTER_CURVESONLY) )
                return items;
                
-       /* Action or Drivers */
-       if ((ads->filterflag & ADS_FILTER_ONLYDRIVERS) == 0) {
-               /* Action? */
-               if (ANIMDATA_HAS_KEYS(sce) && !(ads->filterflag & ADS_FILTER_NOSCE)) {
-                       AnimData *adt= sce->adt;
-                       
-                       /* include action-expand widget? */
-                       if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & ANIMFILTER_CURVESONLY)) {
-                               ale= make_new_animlistelem(adt->action, ANIMTYPE_FILLACTD, sce, ANIMTYPE_SCENE, (ID *)sce);
-                               if (ale) {
-                                       BLI_addtail(anim_data, ale);
-                                       items++;
+       /* Action, Drivers, or NLA  for Scene */
+       if ((ads->filterflag & ADS_FILTER_NOSCE) == 0) {
+               adt= sce->adt;
+               ANIMDATA_FILTER_CASES(sce,
+                       { /* AnimData blocks - do nothing... */ },
+                       { /* nla */
+#if 0
+                               /* include nla-expand widget? */
+                               if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & ANIMFILTER_CURVESONLY)) {
+                                       ale= make_new_animlistelem(adt->action, ANIMTYPE_FILLNLA, base, ANIMTYPE_SCENE (ID *)sce);
+                                       if (ale) {
+                                               BLI_addtail(anim_data, ale);
+                                               items++;
+                                       }
                                }
-                       }
-                       
-                       /* add F-Curve channels? */
-                       if (EXPANDED_ACTC(adt->action) || !(filter_mode & ANIMFILTER_CHANNELS)) {
-                               items += animdata_filter_action(anim_data, adt->action, filter_mode, sce, ANIMTYPE_SCENE, (ID *)sce); 
-                       }
-               }
-       }
-       else {
-               /* Drivers */
-               if (ANIMDATA_HAS_DRIVERS(sce) && !(ads->filterflag & ADS_FILTER_NOSCE)) {
-                       AnimData *adt= sce->adt;
-                       
-                       /* include drivers-expand widget? */
-                       if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & ANIMFILTER_CURVESONLY)) {
-                               ale= make_new_animlistelem(adt->action, ANIMTYPE_FILLDRIVERS, sce, ANIMTYPE_SCENE, (ID *)sce);
-                               if (ale) {
-                                       BLI_addtail(anim_data, ale);
-                                       items++;
+#endif
+                               
+                               /* add NLA tracks */
+                               items += animdata_filter_nla(anim_data, adt, filter_mode, sce, ANIMTYPE_SCENE, (ID *)sce);
+                       },
+                       { /* drivers */
+                               /* include drivers-expand widget? */
+                               if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & ANIMFILTER_CURVESONLY)) {
+                                       ale= make_new_animlistelem(adt->action, ANIMTYPE_FILLDRIVERS, sce, ANIMTYPE_SCENE, (ID *)sce);
+                                       if (ale) {
+                                               BLI_addtail(anim_data, ale);
+                                               items++;
+                                       }
+                               }
+                               
+                               /* add F-Curve channels (drivers are F-Curves) */
+                               if (EXPANDED_DRVD(adt) || !(filter_mode & ANIMFILTER_CHANNELS)) {
+                                       items += animdata_filter_fcurves(anim_data, adt->drivers.first, NULL, sce, ANIMTYPE_SCENE, filter_mode, (ID *)sce);
+                               }
+                       },
+                       { /* action */
+                               /* include action-expand widget? */
+                               if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & ANIMFILTER_CURVESONLY)) {
+                                       ale= make_new_animlistelem(adt->action, ANIMTYPE_FILLACTD, sce, ANIMTYPE_SCENE, (ID *)sce);
+                                       if (ale) {
+                                               BLI_addtail(anim_data, ale);
+                                               items++;
+                                       }
+                               }
+                               
+                               /* add F-Curve channels? */
+                               if (EXPANDED_ACTC(adt->action) || !(filter_mode & ANIMFILTER_CHANNELS)) {
+                                       items += animdata_filter_action(anim_data, adt->action, filter_mode, sce, ANIMTYPE_SCENE, (ID *)sce); 
                                }
                        }
-                       
-                       /* add F-Curve channels (drivers are F-Curves) */
-                       if (EXPANDED_DRVD(adt) || !(filter_mode & ANIMFILTER_CHANNELS)) {
-                               items += animdata_filter_fcurves(anim_data, adt->drivers.first, NULL, sce, ANIMTYPE_SCENE, filter_mode, (ID *)sce);
-                       }
-               }
+               )
        }
-               
+       
        /* world */
        if ((wo && wo->adt) && !(ads->filterflag & ADS_FILTER_NOWOR)) {
-               /* Animation or Drivers */
-               if ((ads->filterflag & ADS_FILTER_ONLYDRIVERS) == 0) {
-                       AnimData *adt= wo->adt;
-                       
-                       /* include world-expand widget? */
-                       if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & ANIMFILTER_CURVESONLY)) {
-                               ale= make_new_animlistelem(wo, ANIMTYPE_DSWOR, sce, ANIMTYPE_SCENE, (ID *)sce);
-                               if (ale) {
-                                       BLI_addtail(anim_data, ale);
-                                       items++;
+               /* Action, Drivers, or NLA for World */
+               adt= wo->adt;
+               ANIMDATA_FILTER_CASES(wo,
+                       { /* AnimData blocks - do nothing... */ },
+                       { /* nla */
+#if 0
+                               /* include nla-expand widget? */
+                               if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & ANIMFILTER_CURVESONLY)) {
+                                       ale= make_new_animlistelem(adt->action, ANIMTYPE_FILLNLA, base, ANIMTYPE_DSWOR (ID *)wo);
+                                       if (ale) {
+                                               BLI_addtail(anim_data, ale);
+                                               items++;
+                                       }
                                }
-                       }
-                       
-                       /* add channels */
-                       if (FILTER_WOR_SCED(wo) || (filter_mode & ANIMFILTER_CURVESONLY)) {
-                               items += animdata_filter_action(anim_data, adt->action, filter_mode, wo, ANIMTYPE_DSWOR, (ID *)wo); 
-                       }
-               }
-               else {
-                       /* Drivers */
-                       if (ANIMDATA_HAS_DRIVERS(wo)) {
-                               AnimData *adt= wo->adt;
+#endif
                                
-                               /* include shapekey-expand widget? */
+                               /* add NLA tracks */
+                               items += animdata_filter_nla(anim_data, adt, filter_mode, wo, ANIMTYPE_DSWOR, (ID *)wo);
+                       },
+                       { /* drivers */
+                               /* include world-expand widget? */
                                if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & ANIMFILTER_CURVESONLY)) {
                                        ale= make_new_animlistelem(wo, ANIMTYPE_DSWOR, sce, ANIMTYPE_SCENE, (ID *)wo);
                                        if (ale) {
@@ -1158,8 +1340,23 @@ static int animdata_filter_dopesheet_scene (ListBase *anim_data, bDopeSheet *ads
                                        // XXX owner info is messed up now...
                                        items += animdata_filter_fcurves(anim_data, adt->drivers.first, NULL, wo, ANIMTYPE_DSWOR, filter_mode, (ID *)wo);
                                }
+                       },
+                       { /* action */
+                               /* include world-expand widget? */
+                               if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & ANIMFILTER_CURVESONLY)) {
+                                       ale= make_new_animlistelem(wo, ANIMTYPE_DSWOR, sce, ANIMTYPE_SCENE, (ID *)sce);
+                                       if (ale) {
+                                               BLI_addtail(anim_data, ale);
+                                               items++;
+                                       }
+                               }
+                               
+                               /* add channels */
+                               if (FILTER_WOR_SCED(wo) || (filter_mode & ANIMFILTER_CURVESONLY)) {
+                                       items += animdata_filter_action(anim_data, adt->action, filter_mode, wo, ANIMTYPE_DSWOR, (ID *)wo); 
+                               }
                        }
-               }
+               )
        }
        
        /* return the number of items added to the list */
@@ -1171,6 +1368,7 @@ static int animdata_filter_dopesheet (ListBase *anim_data, bDopeSheet *ads, int
 {
        Scene *sce= (Scene *)ads->source;
        Base *base;
+       bAnimListElem *ale;
        int items = 0;
        
        /* check that we do indeed have a scene */
@@ -1182,22 +1380,32 @@ static int animdata_filter_dopesheet (ListBase *anim_data, bDopeSheet *ads, int
        /* scene-linked animation */
        // TODO: sequencer, composite nodes - are we to include those here too?
        {
-               short sceOk, worOk;
+               short sceOk= 0, worOk= 0;
                
                /* check filtering-flags if ok */
-               if (ads->filterflag) {
-                       if (ads->filterflag & ADS_FILTER_ONLYDRIVERS) {
-                               sceOk= (ANIMDATA_HAS_DRIVERS(sce) && !(ads->filterflag & ADS_FILTER_NOSCE));
-                               worOk= ((sce->world) && ANIMDATA_HAS_DRIVERS(sce->world) && !(ads->filterflag & ADS_FILTER_NOWOR));
-                       }
-                       else {
-                               sceOk= (ANIMDATA_HAS_KEYS(sce) && !(ads->filterflag & ADS_FILTER_NOSCE));
-                               worOk= ((sce->world) && ANIMDATA_HAS_KEYS(sce->world) && !(ads->filterflag & ADS_FILTER_NOWOR));
-                       }
-               }
-               else {
-                       sceOk= (ANIMDATA_HAS_KEYS(sce));
-                       worOk= ((sce->world) && ANIMDATA_HAS_KEYS(sce->world));
+               ANIMDATA_FILTER_CASES(sce, 
+                       {
+                               /* for the special AnimData blocks only case, we only need to add
+                                * the block if it is valid... then other cases just get skipped (hence ok=0)
+                                */
+                               ANIMDATA_ADD_ANIMDATA(sce);
+                               sceOk=0;
+                       },
+                       sceOk= !(ads->filterflag & ADS_FILTER_NOSCE);, 
+                       sceOk= !(ads->filterflag & ADS_FILTER_NOSCE);, 
+                       sceOk= !(ads->filterflag & ADS_FILTER_NOSCE);)
+               if (sce->world) {
+                       ANIMDATA_FILTER_CASES(sce->world, 
+                               {
+                                       /* for the special AnimData blocks only case, we only need to add
+                                        * the block if it is valid... then other cases just get skipped (hence ok=0)
+                                        */
+                                       ANIMDATA_ADD_ANIMDATA(sce->world);
+                                       worOk=0;
+                               },
+                               worOk= !(ads->filterflag & ADS_FILTER_NOWOR);, 
+                               worOk= !(ads->filterflag & ADS_FILTER_NOWOR);, 
+                               worOk= !(ads->filterflag & ADS_FILTER_NOWOR);)
                }
                
                /* check if not all bad (i.e. so there is something to show) */
@@ -1239,13 +1447,33 @@ static int animdata_filter_dopesheet (ListBase *anim_data, bDopeSheet *ads, int
                                }
                                
                                /* check filters for datatypes */
-                               if (ads->filterflag & ADS_FILTER_ONLYDRIVERS) {
-                                       actOk= (ANIMDATA_HAS_DRIVERS(ob));
-                                       keyOk= ((key) && ANIMDATA_HAS_DRIVERS(key) && !(ads->filterflag & ADS_FILTER_NOSHAPEKEYS));
-                               }
-                               else {
-                                       actOk= ANIMDATA_HAS_KEYS(ob);
-                                       keyOk= ((key) && ANIMDATA_HAS_KEYS(key) && !(ads->filterflag & ADS_FILTER_NOSHAPEKEYS));
+                                       /* object */
+                               actOk= 0;
+                               keyOk= 0;
+                               ANIMDATA_FILTER_CASES(ob, 
+                                       {
+                                               /* for the special AnimData blocks only case, we only need to add
+                                                * the block if it is valid... then other cases just get skipped (hence ok=0)
+                                                */
+                                               ANIMDATA_ADD_ANIMDATA(ob);
+                                               actOk=0;
+                                       },
+                                       actOk= 1;, 
+                                       actOk= 1;, 
+                                       actOk= 1;)
+                               if (key) {
+                                       /* shapekeys */
+                                       ANIMDATA_FILTER_CASES(key, 
+                                               {
+                                                       /* for the special AnimData blocks only case, we only need to add
+                                                        * the block if it is valid... then other cases just get skipped (hence ok=0)
+                                                        */
+                                                       ANIMDATA_ADD_ANIMDATA(key);
+                                                       keyOk=0;
+                                               },
+                                               keyOk= 1;, 
+                                               keyOk= 1;, 
+                                               keyOk= 1;)
                                }
                                
                                /* materials - only for geometric types */
@@ -1260,18 +1488,20 @@ static int animdata_filter_dopesheet (ListBase *anim_data, bDopeSheet *ads, int
                                                Material *ma= give_current_material(ob, a);
                                                
                                                /* if material has relevant animation data, break */
-                                               if (ads->filterflag & ADS_FILTER_ONLYDRIVERS) {
-                                                       if (ANIMDATA_HAS_DRIVERS(ma)) {
-                                                               matOk= 1;
-                                                               break;
-                                                       }
-                                               }
-                                               else {
-                                                       if (ANIMDATA_HAS_KEYS(ma)) {
-                                                               matOk= 1;
-                                                               break;
-                                                       }
-                                               }
+                                               ANIMDATA_FILTER_CASES(ma, 
+                                                       {
+                                                               /* for the special AnimData blocks only case, we only need to add
+                                                                * the block if it is valid... then other cases just get skipped (hence ok=0)
+                                                                */
+                                                               ANIMDATA_ADD_ANIMDATA(ma);
+                                                               matOk=0;
+                                                       },
+                                                       matOk= 1;, 
+                                                       matOk= 1;, 
+                                                       matOk= 1;)
+                                                       
+                                               if (matOk) 
+                                                       break;
                                        }
                                }
                                
@@ -1280,19 +1510,52 @@ static int animdata_filter_dopesheet (ListBase *anim_data, bDopeSheet *ads, int
                                        case OB_CAMERA: /* ------- Camera ------------ */
                                        {
                                                Camera *ca= (Camera *)ob->data;
-                                               if (ads->filterflag & ADS_FILTER_ONLYDRIVERS)
-                                                       dataOk= (ANIMDATA_HAS_DRIVERS(ca) && !(ads->filterflag & ADS_FILTER_NOCAM));
-                                               else
-                                                       dataOk= (ANIMDATA_HAS_KEYS(ca) && !(ads->filterflag & ADS_FILTER_NOCAM));                                               
+                                               dataOk= 0;
+                                               ANIMDATA_FILTER_CASES(ca, 
+                                                       if ((ads->filterflag & ADS_FILTER_NOCAM)==0) {
+                                                               /* for the special AnimData blocks only case, we only need to add
+                                                                * the block if it is valid... then other cases just get skipped (hence ok=0)
+                                                                */
+                                                               ANIMDATA_ADD_ANIMDATA(ca);
+                                                               dataOk=0;
+                                                       },
+                                                       dataOk= !(ads->filterflag & ADS_FILTER_NOCAM);, 
+                                                       dataOk= !(ads->filterflag & ADS_FILTER_NOCAM);, 
+                                                       dataOk= !(ads->filterflag & ADS_FILTER_NOCAM);)
                                        }
                                                break;
                                        case OB_LAMP: /* ---------- Lamp ----------- */
                                        {
                                                Lamp *la= (Lamp *)ob->data;
-                                               if (ads->filterflag & ADS_FILTER_ONLYDRIVERS)
-                                                       dataOk= (ANIMDATA_HAS_DRIVERS(la) && !(ads->filterflag & ADS_FILTER_NOLAM));
-                                               else
-                                                       dataOk= (ANIMDATA_HAS_KEYS(la) && !(ads->filterflag & ADS_FILTER_NOLAM));       
+                                               dataOk= 0;
+                                               ANIMDATA_FILTER_CASES(la, 
+                                                       if ((ads->filterflag & ADS_FILTER_NOLAM)==0) {
+                                                               /* for the special AnimData blocks only case, we only need to add
+                                                                * the block if it is valid... then other cases just get skipped (hence ok=0)
+                                                                */
+                                                               ANIMDATA_ADD_ANIMDATA(la);
+                                                               dataOk=0;
+                                                       },
+                                                       dataOk= !(ads->filterflag & ADS_FILTER_NOLAM);, 
+                                                       dataOk= !(ads->filterflag & ADS_FILTER_NOLAM);, 
+                                                       dataOk= !(ads->filterflag & ADS_FILTER_NOLAM);)
+                                       }
+                                               break;
+                                       case OB_CURVE: /* ------- Curve ---------- */
+                                       {
+                                               Curve *cu= (Curve *)ob->data;
+                                               dataOk= 0;
+                                               ANIMDATA_FILTER_CASES(cu, 
+                                                       if ((ads->filterflag & ADS_FILTER_NOCUR)==0) {
+                                                               /* for the special AnimData blocks only case, we only need to add
+                                                                * the block if it is valid... then other cases just get skipped (hence ok=0)
+                                                                */
+                                                               ANIMDATA_ADD_ANIMDATA(cu);
+                                                               dataOk=0;
+                                                       },
+                                                       dataOk= !(ads->filterflag & ADS_FILTER_NOCUR);, 
+                                                       dataOk= !(ads->filterflag & ADS_FILTER_NOCUR);, 
+                                                       dataOk= !(ads->filterflag & ADS_FILTER_NOCUR);)
                                        }
                                                break;
                                        default: /* --- other --- */
@@ -1400,6 +1663,7 @@ int ANIM_animdata_filter (bAnimContext *ac, ListBase *anim_data, int filter_mode
                        case ANIMCONT_DOPESHEET:
                        case ANIMCONT_FCURVES:
                        case ANIMCONT_DRIVERS:
+                       case ANIMCONT_NLA:
                                items= animdata_filter_dopesheet(anim_data, data, filter_mode);
                                break;
                }
index cfbd6d2bced9f21360ae30c064a10caddc85d8e6..144cd68f6df1aef9c63b07ee01b3c446c27d8b6f 100644 (file)
@@ -412,8 +412,6 @@ void scene_to_keylist(Scene *sce, ListBase *keys, ListBase *blocks, ActKeysInc *
                /* get filterflag */
                if (ads)
                        filterflag= ads->filterflag;
-               else if ((aki) && (aki->actmode == -1)) /* only set like this by NLA */
-                       filterflag= ADS_FILTER_NLADUMMY;
                else
                        filterflag= 0;
                        
index efc0a0b9a57382c49dd2ed001755b045a75eaa12..dcaabb4b36931997b092add19aebb35cd63e3777 100644 (file)
@@ -75,8 +75,9 @@ typedef enum eAnimCont_Types {
        ANIMCONT_SHAPEKEY,              /* shapekey (Key) */
        ANIMCONT_GPENCIL,               /* grease pencil (screen) */
        ANIMCONT_DOPESHEET,             /* dopesheet (bDopesheet) */
-       ANIMCONT_FCURVES,               /* animation F-Curves (bDopesheet) */           // XXX 
+       ANIMCONT_FCURVES,               /* animation F-Curves (bDopesheet) */
        ANIMCONT_DRIVERS,               /* drivers (bDopesheet) */
+       ANIMCONT_NLA,                   /* nla (bDopesheet) */
 } eAnimCont_Types;
 
 /* --------------- Channels -------------------- */
@@ -92,10 +93,10 @@ typedef struct bAnimListElem {
        int             flag;           /* copy of elem's flags for quick access */
        int     index;          /* copy of adrcode where applicable */
        
-       void    *key_data;      /* motion data - ipo or ipo-curve */
+       void    *key_data;      /* motion data - mostly F-Curves, but can be other types too */
        short   datatype;       /* type of motion data to expect */
        
-       struct ID *id;                          /* ID block that channel is attached to (may be used  */
+       struct ID *id;          /* ID block that channel is attached to (may be used  */
        
        void    *owner;         /* group or channel which acts as this channel's owner */
        short   ownertype;      /* type of owner */
@@ -106,6 +107,7 @@ typedef struct bAnimListElem {
 // XXX was ACTTYPE_*
 typedef enum eAnim_ChannelType {
        ANIMTYPE_NONE= 0,
+       ANIMTYPE_ANIMDATA,
        ANIMTYPE_SPECIALDATA,
        
        ANIMTYPE_SCENE,
@@ -128,6 +130,9 @@ typedef enum eAnim_ChannelType {
        
        ANIMTYPE_GPDATABLOCK,
        ANIMTYPE_GPLAYER,
+       
+       ANIMTYPE_NLATRACK,
+       ANIMTYPE_NLAACTION,
 } eAnim_ChannelType;
 
 /* types of keyframe data in bAnimListElem */
@@ -135,6 +140,7 @@ typedef enum eAnim_KeyType {
        ALE_NONE = 0,           /* no keyframe data */
        ALE_FCURVE,                     /* F-Curve */
        ALE_GPFRAME,            /* Grease Pencil Frames */
+       ALE_NLASTRIP,           /* NLA Strips */
        
        // XXX the following are for summaries... should these be kept?
        ALE_SCE,                        /* Scene summary */
@@ -157,6 +163,7 @@ typedef enum eAnimFilter_Flags {
        ANIMFILTER_ACTGROUPED   = (1<<6),       /* belongs to the active actiongroup */
        ANIMFILTER_CURVEVISIBLE = (1<<7),       /* F-Curve is visible for editing/viewing in Graph Editor */
        ANIMFILTER_ACTIVE               = (1<<8),       /* channel should be 'active' */  // FIXME: this is only relevant for F-Curves for now
+       ANIMFILTER_ANIMDATA             = (1<<9),       /* only return the underlying AnimData blocks (not the tracks, etc.) data comes from */
 } eAnimFilter_Flags;
 
 
@@ -202,6 +209,10 @@ typedef enum eAnimFilter_Flags {
 #define EDITABLE_GPL(gpl) ((gpl->flag & GP_LAYER_LOCKED)==0)
 #define SEL_GPL(gpl) ((gpl->flag & GP_LAYER_ACTIVE) || (gpl->flag & GP_LAYER_SELECT))
 
+/* NLA only */
+#define SEL_NLT(nlt) (nlt->flag & NLATRACK_SELECTED)
+#define EDITABLE_NLT(nlt) ((nlt->flag & NLATRACK_PROTECTED)==0)
+
 /* -------------- Channel Defines -------------- */
 
 /* channel heights */
@@ -245,7 +256,7 @@ short ANIM_animdata_context_getdata(bAnimContext *ac);
 void ANIM_deselect_anim_channels(void *data, short datatype, short test, short sel);
 
 /* Set the 'active' channel of type channel_type, in the given action */
-void ANIM_set_active_channel(void *data, short datatype, int filter, void *channel_data, short channel_type);
+void ANIM_set_active_channel(bAnimContext *ac, void *data, short datatype, int filter, void *channel_data, short channel_type);
 
 /* --------------- Settings and/or Defines -------------- */
 
@@ -299,6 +310,8 @@ void ipo_rainbow(int cur, int tot, float *out);
 /* ------------- NLA-Mapping ----------------------- */
 /* anim_draw.c */
 
+// XXX these are soon to be depreceated?
+
 /* Obtain the Object providing NLA-scaling for the given channel if applicable */
 struct Object *ANIM_nla_mapping_get(bAnimContext *ac, bAnimListElem *ale);
 
@@ -308,10 +321,35 @@ void ANIM_nla_mapping_draw(struct gla2DDrawInfo *di, struct Object *ob, short re
 /* Apply/Unapply NLA mapping to all keyframes in the nominated IPO block */
 void ANIM_nla_mapping_apply_fcurve(struct Object *ob, struct FCurve *fcu, short restore, short only_keys);
 
-/* ------------- xxx macros ----------------------- */
+/* ------------- Utility macros ----------------------- */
 
+/* checks if the given BezTriple is selected */
 #define BEZSELECTED(bezt) ((bezt->f2 & SELECT) || (bezt->f1 & SELECT) || (bezt->f3 & SELECT))
 
+/* set/clear/toggle macro 
+ *     - channel - channel with a 'flag' member that we're setting
+ *     - smode - 0=clear, 1=set, 2=toggle
+ *     - sflag - bitflag to set
+ */
+#define ACHANNEL_SET_FLAG(channel, smode, sflag) \
+       { \
+               if (smode == ACHANNEL_SETFLAG_TOGGLE)   (channel)->flag ^= (sflag); \
+               else if (smode == ACHANNEL_SETFLAG_ADD) (channel)->flag |= (sflag); \
+               else                                                                    (channel)->flag &= ~(sflag); \
+       }
+       
+/* set/clear/toggle macro, where the flag is negative 
+ *     - channel - channel with a 'flag' member that we're setting
+ *     - smode - 0=clear, 1=set, 2=toggle
+ *     - sflag - bitflag to set
+ */
+#define ACHANNEL_SET_FLAG_NEG(channel, smode, sflag) \
+       { \
+               if (smode == ACHANNEL_SETFLAG_TOGGLE)   (channel)->flag ^= (sflag); \
+               else if (smode == ACHANNEL_SETFLAG_ADD) (channel)->flag &= ~(sflag); \
+               else                                                                    (channel)->flag |= (sflag); \
+       }
+
 
 /* --------- anim_deps.c, animation updates -------- */
 
index c2beb34e7b5e704f0545584731ae4a66c16535e7..e3b6572c03a03fbf3cf272997f4857893dd979ef 100644 (file)
@@ -118,6 +118,7 @@ int         ED_operator_node_active(struct bContext *C);
 int            ED_operator_ipo_active(struct bContext *C);
 int            ED_operator_sequencer_active(struct bContext *C);
 int            ED_operator_image_active(struct bContext *C);
+int            ED_operator_nla_active(struct bContext *C);
 
 int            ED_operator_object_active(struct bContext *C);
 int            ED_operator_editmesh(struct bContext *C);
index 4813b6957952e8de7214205e50f91b1734b61c57..50283113b624b243b8a24938059bfdf288b3c88e 100644 (file)
@@ -52,6 +52,8 @@ enum {
        V2D_COMMONVIEW_STANDARD,
                /* listview (i.e. Outliner) */
        V2D_COMMONVIEW_LIST,
+               /* stackview (this is basically a list where new items are added at the top) */
+       V2D_COMMONVIEW_STACK,
                /* headers (this is basically the same as listview, but no y-panning) */
        V2D_COMMONVIEW_HEADER,
                /* ui region containing panels */
index 1cb58c986d036886cd526d8afee53ec0d61c7a07..f00328096317c459d8fe50b475e61782274c145b 100644 (file)
@@ -501,7 +501,10 @@ void ui_theme_init_userdef(void)
        btheme->tact= btheme->tipo;
        SETCOL(btheme->tact.strip,                      12, 10, 10, 128); 
        SETCOL(btheme->tact.strip_select,       255, 140, 0, 255); 
-
+       
+       /* space nla */
+       btheme->tnla= btheme->tact;
+       
        /* space file */
        /* to have something initialized */
        btheme->tfile= btheme->tv3d;
@@ -518,20 +521,6 @@ void ui_theme_init_userdef(void)
        SETCOL(btheme->tfile.scene,     250, 250, 250, 255);
 
        
-       
-
-       /* space nla */
-       btheme->tnla= btheme->tv3d;
-       SETCOL(btheme->tnla.back,       116, 116, 116, 255);
-       SETCOL(btheme->tnla.text,       0, 0, 0, 255);
-       SETCOL(btheme->tnla.text_hi, 255, 255, 255, 255);
-       SETCOL(btheme->tnla.grid,  94, 94, 94, 255);    
-       SETCOL(btheme->tnla.shade1,  172, 172, 172, 255);               // sliders
-       SETCOL(btheme->tnla.shade2,  84, 44, 31, 100);  // bar
-       SETCOL(btheme->tnla.hilite,  17, 27, 60, 100);  // bar
-       SETCOL(btheme->tnla.strip_select,       0xff, 0xff, 0xaa, 255);
-       SETCOL(btheme->tnla.strip, 0xe4, 0x9c, 0xc6, 255);
-       
        /* space seq */
        btheme->tseq= btheme->tv3d;
        SETCOL(btheme->tseq.back,       116, 116, 116, 255);
index b363f1f627279d941dbf8217ea3fafcef5d0bcae..19c95ce7ce002ecbd273fac53ff2f474ddaa5369 100644 (file)
@@ -207,6 +207,23 @@ void UI_view2d_region_reinit(View2D *v2d, short type, int winx, int winy)
                        }
                                break;
                                
+                       /* 'stack view' - practically the same as list/channel view, except is located in the pos y half instead. 
+                        *      zoom, aspect ratio, and alignment restrictions are set here */
+                       case V2D_COMMONVIEW_STACK:
+                       {
+                               /* zoom + aspect ratio are locked */
+                               v2d->keepzoom = (V2D_LOCKZOOM_X|V2D_LOCKZOOM_Y|V2D_KEEPZOOM|V2D_KEEPASPECT);
+                               v2d->minzoom= v2d->maxzoom= 1.0f;
+                               
+                               /* tot rect has strictly regulated placement, and must only occur in +/+ quadrant */
+                               v2d->align = (V2D_ALIGN_NO_NEG_X|V2D_ALIGN_NO_NEG_Y);
+                               v2d->keeptot = V2D_KEEPTOT_STRICT;
+                               tot_changed= 1;
+                               
+                               /* scroller settings are currently not set here... that is left for regions... */
+                       }
+                               break;
+                               
                        /* 'header' regions - zoom, aspect ratio, alignment, and panning restrictions are set here */
                        case V2D_COMMONVIEW_HEADER:
                        {
@@ -245,14 +262,14 @@ void UI_view2d_region_reinit(View2D *v2d, short type, int winx, int winy)
                                
                                v2d->tot.xmin= 0.0f;
                                v2d->tot.xmax= winx;
-
+                               
                                v2d->tot.ymax= 0.0f;
                                v2d->tot.ymin= -winy;
-
+                               
                                v2d->cur= v2d->tot;
                        }
                                break;
-
+                               
                                /* other view types are completely defined using their own settings already */
                        default:
                                /* we don't do anything here, as settings should be fine, but just make sure that rect */
index a61db5d5e1a48bc43f41ef890dab349b2c717a44..3e237bff46a6ec5247813c1ae0441c968c22ed97 100644 (file)
@@ -164,6 +164,7 @@ int ED_operator_node_active(bContext *C)
        return 0;
 }
 
+// XXX rename
 int ED_operator_ipo_active(bContext *C)
 {
        return ed_spacetype_test(C, SPACE_IPO);
@@ -179,6 +180,11 @@ int ED_operator_image_active(bContext *C)
        return ed_spacetype_test(C, SPACE_IMAGE);
 }
 
+int ED_operator_nla_active(bContext *C)
+{
+       return ed_spacetype_test(C, SPACE_NLA);
+}
+
 int ED_operator_object_active(bContext *C)
 {
        return NULL != CTX_data_active_object(C);
index d4782418be70f774046f65c50917543a640710b1..d8ed3fd10681cdeffe8b9c1d4d342b7d1f52cfd6 100644 (file)
@@ -174,7 +174,7 @@ static int actkeys_deselectall_exec(bContext *C, wmOperator *op)
                deselect_action_keys(&ac, 1, SELECT_ADD);
        
        /* set notifier that things have changed */
-       ED_area_tag_redraw(CTX_wm_area(C)); // FIXME... should be updating 'keyframes' data context or so instead!
+       ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_BOTH);
        
        return OPERATOR_FINISHED;
 }
@@ -766,6 +766,7 @@ static void mouse_action_keys (bAnimContext *ac, int mval[2], short select_mode,
        if (ale == NULL) {
                /* channel not found */
                printf("Error: animation channel (index = %d) not found in mouse_action_keys() \n", channel_index);
+               BLI_freelistN(&anim_data);
                return;
        }
        else {
@@ -861,13 +862,13 @@ static void mouse_action_keys (bAnimContext *ac, int mval[2], short select_mode,
                                bActionGroup *agrp= ale->data;
                                
                                agrp->flag |= AGRP_SELECTED;
-                               ANIM_set_active_channel(ac->data, ac->datatype, filter, agrp, ANIMTYPE_GROUP);
+                               ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, agrp, ANIMTYPE_GROUP);
                        }       
                        else if (ale->type == ANIMTYPE_FCURVE) {
                                FCurve *fcu= ale->data;
                                
                                fcu->flag |= FCURVE_SELECTED;
-                               ANIM_set_active_channel(ac->data, ac->datatype, filter, fcu, ANIMTYPE_FCURVE);
+                               ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, fcu, ANIMTYPE_FCURVE);
                        }
                }
                else if (ac->datatype == ANIMCONT_GPENCIL) {
index d57fc7f90bc4ef7c73bdd146f195a0dac3831c25..4c2def72de06530e07e7f419befa1775e85f7133 100644 (file)
@@ -131,6 +131,9 @@ int ED_fileselect_layout_offset(FileLayout* layout, int x, int y)
        int offsetx, offsety;
        int active_file;
 
+       if (layout == NULL)
+               return NULL;
+       
        offsetx = (x)/(layout->tile_w + 2*layout->tile_border_x);
        offsety = (y)/(layout->tile_h + 2*layout->tile_border_y);
        
index bb923ca6f95bcb0886e0287f46214823a4f815d2..21320b60ead1eba7a0f757af5e5e3b2dd138a79e 100644 (file)
@@ -722,7 +722,7 @@ static void mouse_graph_keys (bAnimContext *ac, int mval[], short select_mode, s
        /* set active F-Curve (NOTE: sync the filter flags with findnearest_fcurve_vert) */
        if (fcu->flag & FCURVE_SELECTED) {
                filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_CURVESONLY);
-               ANIM_set_active_channel(ac->data, ac->datatype, filter, fcu, ANIMTYPE_FCURVE);
+               ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, fcu, ANIMTYPE_FCURVE);
        }
 }
 
index 74002f641879af3b4eac898915548f94b9521c19..ef42b009bd428cc1203e13e5a778014df81093be 100644 (file)
@@ -192,7 +192,7 @@ static SpaceLink *graph_duplicate(SpaceLink *sl)
        SpaceIpo *sipon= MEM_dupallocN(sl);
        
        /* clear or remove stuff from old */
-       //sipon->ipokey.first= sipon->ipokey.last= NULL;
+       BLI_duplicatelist(&sipon->ghostCurves, &((SpaceIpo *)sl)->ghostCurves);
        sipon->ads= MEM_dupallocN(sipon->ads);
        
        return (SpaceLink *)sipon;
diff --git a/source/blender/editors/space_nla/nla_channels.c b/source/blender/editors/space_nla/nla_channels.c
new file mode 100644 (file)
index 0000000..ed401c3
--- /dev/null
@@ -0,0 +1,461 @@
+/**
+ * $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 <stdlib.h>
+#include <math.h>
+
+#include "DNA_listBase.h"
+#include "DNA_anim_types.h"
+#include "DNA_action_types.h"
+#include "DNA_armature_types.h"
+#include "DNA_camera_types.h"
+#include "DNA_curve_types.h"
+#include "DNA_object_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_space_types.h"
+#include "DNA_key_types.h"
+#include "DNA_lamp_types.h"
+#include "DNA_material_types.h"
+#include "DNA_userdef_types.h"
+#include "DNA_windowmanager_types.h"
+#include "DNA_world_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_arithb.h"
+#include "BLI_rand.h"
+
+#include "BKE_animsys.h"
+#include "BKE_nla.h"
+#include "BKE_context.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 "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "UI_interface.h"
+#include "UI_interface_icons.h"
+#include "UI_resources.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 */
+
+/* ******************** Borderselect Operator *********************** */
+
+static void borderselect_nla_channels (bAnimContext *ac, rcti *rect, short selectmode)
+{
+       ListBase anim_data = {NULL, NULL};
+       bAnimListElem *ale;
+       int filter;
+       
+       View2D *v2d= &ac->ar->v2d;
+       rctf rectf;
+       float ymin=(float)(-NLACHANNEL_HEIGHT), ymax=0;
+       
+       /* convert border-region to view coordinates */
+       UI_view2d_region_to_view(v2d, rect->xmin, rect->ymin+2, &rectf.xmin, &rectf.ymin);
+       UI_view2d_region_to_view(v2d, rect->xmax, rect->ymax-2, &rectf.xmax, &rectf.ymax);
+       
+       /* filter data */
+       filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CHANNELS);
+       ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
+       
+       /* loop over data, doing border select */
+       for (ale= anim_data.first; ale; ale= ale->next) {
+               ymax= ymin + NLACHANNEL_STEP;
+               
+               /* if channel is within border-select region, alter it */
+               if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))) {
+                       /* only the following types can be selected */
+                       switch (ale->type) {
+                               case ANIMTYPE_OBJECT: /* object */
+                               {
+                                       Base *base= (Base *)ale->data;
+                                       Object *ob= base->object;
+                                       
+                                       ACHANNEL_SET_FLAG(base, selectmode, SELECT);
+                                       ACHANNEL_SET_FLAG(ob, selectmode, SELECT);
+                               }
+                                       break;
+                               case ANIMTYPE_NLATRACK: /* nla-track */
+                               {
+                                       NlaTrack *nlt= (NlaTrack *)ale->data;
+                                       
+                                       ACHANNEL_SET_FLAG(nlt, selectmode, NLATRACK_SELECTED);
+                               }
+                                       break;
+                       }
+               }
+               
+               /* set maximum extent to be the minimum of the next channel */
+               ymin= ymax;
+       }
+       
+       /* cleanup */
+       BLI_freelistN(&anim_data);
+}
+
+/* ------------------- */
+
+static int nlachannels_borderselect_exec(bContext *C, wmOperator *op)
+{
+       bAnimContext ac;
+       rcti rect;
+       short selectmode=0;
+       int event;
+       
+       /* get editor data */
+       if (ANIM_animdata_get_context(C, &ac) == 0)
+               return OPERATOR_CANCELLED;
+       
+       /* get settings from operator */
+       rect.xmin= RNA_int_get(op->ptr, "xmin");
+       rect.ymin= RNA_int_get(op->ptr, "ymin");
+       rect.xmax= RNA_int_get(op->ptr, "xmax");
+       rect.ymax= RNA_int_get(op->ptr, "ymax");
+       
+       event= RNA_int_get(op->ptr, "event_type");
+       if (event == LEFTMOUSE) // FIXME... hardcoded
+               selectmode = ACHANNEL_SETFLAG_ADD;
+       else
+               selectmode = ACHANNEL_SETFLAG_CLEAR;
+       
+       /* apply borderselect animation channels */
+       borderselect_nla_channels(&ac, &rect, selectmode);
+       
+       return OPERATOR_FINISHED;
+} 
+
+void NLA_OT_channels_select_border(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name= "Border Select";
+       ot->idname= "NLA_OT_channels_select_border";
+       
+       /* api callbacks */
+       ot->invoke= WM_border_select_invoke;
+       ot->exec= nlachannels_borderselect_exec;
+       ot->modal= WM_border_select_modal;
+       
+       ot->poll= ED_operator_areaactive;
+       
+       /* flags */
+       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+       
+       /* rna */
+       RNA_def_int(ot->srna, "event_type", 0, INT_MIN, INT_MAX, "Event Type", "", INT_MIN, INT_MAX);
+       RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
+       RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
+       RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
+       RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
+}
+
+/* ******************** Mouse-Click Operator *********************** */
+/* Depending on the channel that was clicked on, the mouse click will activate whichever
+ * part of the channel is relevant.
+ *
+ * NOTE: eventually, this should probably be phased out when many of these things are replaced with buttons
+ */
+
+static void mouse_nla_channels (bAnimContext *ac, float x, int channel_index, short selectmode)
+{
+       ListBase anim_data = {NULL, NULL};
+       bAnimListElem *ale;
+       int filter;
+       
+       /* get the channel that was clicked on */
+               /* filter channels */
+       filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CHANNELS);
+       filter= ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
+       
+               /* get channel from index */
+       ale= BLI_findlink(&anim_data, channel_index);
+       if (ale == NULL) {
+               /* channel not found */
+               printf("Error: animation channel (index = %d) not found in mouse_anim_channels() \n", channel_index);
+               
+               BLI_freelistN(&anim_data);
+               return;
+       }
+       
+       /* action to take depends on what channel we've got */
+       switch (ale->type) {
+               case ANIMTYPE_SCENE:
+               {
+                       Scene *sce= (Scene *)ale->data;
+                       
+                       if (x < 16) {
+                               /* toggle expand */
+                               sce->flag ^= SCE_DS_COLLAPSED;
+                       }
+                       else {
+                               /* set selection status */
+                               if (selectmode == SELECT_INVERT) {
+                                       /* swap select */
+                                       sce->flag ^= SCE_DS_SELECTED;
+                               }
+                               else {
+                                       sce->flag |= SCE_DS_SELECTED;
+                               }
+                       }
+               }
+                       break;
+               case ANIMTYPE_OBJECT:
+               {
+                       bDopeSheet *ads= (bDopeSheet *)ac->data;
+                       Scene *sce= (Scene *)ads->source;
+                       Base *base= (Base *)ale->data;
+                       Object *ob= base->object;
+                       
+                       if (x < 16) {
+                               /* toggle expand */
+                               ob->nlaflag ^= OB_ADS_COLLAPSED; // XXX 
+                       }
+                       else {
+                               /* set selection status */
+                               if (selectmode == SELECT_INVERT) {
+                                       /* swap select */
+                                       base->flag ^= SELECT;
+                                       ob->flag= base->flag;
+                               }
+                               else {
+                                       Base *b;
+                                       
+                                       /* deleselect all */
+                                       for (b= sce->base.first; b; b= b->next) {
+                                               b->flag &= ~SELECT;
+                                               b->object->flag= b->flag;
+                                       }
+                                       
+                                       /* select object now */
+                                       base->flag |= SELECT;
+                                       ob->flag |= SELECT;
+                               }
+                               
+                               /* xxx should be ED_base_object_activate(), but we need context pointer for that... */
+                               //set_active_base(base);
+                       }
+               }
+                       break;
+               case ANIMTYPE_FILLMATD:
+               {
+                       Object *ob= (Object *)ale->data;
+                       ob->nlaflag ^= OB_ADS_SHOWMATS; // XXX 
+               }
+                       break;
+                               
+               case ANIMTYPE_DSMAT:
+               {
+                       Material *ma= (Material *)ale->data;
+                       ma->flag ^= MA_DS_EXPAND;
+               }
+                       break;
+               case ANIMTYPE_DSLAM:
+               {
+                       Lamp *la= (Lamp *)ale->data;
+                       la->flag ^= LA_DS_EXPAND;
+               }
+                       break;
+               case ANIMTYPE_DSCAM:
+               {
+                       Camera *ca= (Camera *)ale->data;
+                       ca->flag ^= CAM_DS_EXPAND;
+               }
+                       break;
+               case ANIMTYPE_DSCUR:
+               {
+                       Curve *cu= (Curve *)ale->data;
+                       cu->flag ^= CU_DS_EXPAND;
+               }
+                       break;
+               case ANIMTYPE_DSSKEY:
+               {
+                       Key *key= (Key *)ale->data;
+                       key->flag ^= KEYBLOCK_DS_EXPAND;
+               }
+                       break;
+               case ANIMTYPE_DSWOR:
+               {
+                       World *wo= (World *)ale->data;
+                       wo->flag ^= WO_DS_EXPAND;
+               }
+                       break;
+                       
+               case ANIMTYPE_NLATRACK:
+               {
+                       NlaTrack *nlt= (NlaTrack *)ale->data;
+                       AnimData *adt= BKE_animdata_from_id(ale->id);
+                       short offset;
+                       
+                       /* offset for start of channel (on LHS of channel-list) */
+                       if (ale->id) {
+                               /* special exception for materials */
+                               if (GS(ale->id->name) == ID_MA)
+                                       offset= 21 + NLACHANNEL_BUTTON_WIDTH;
+                               else
+                                       offset= 14;
+                       }
+                       else
+                               offset= 0;
+                       
+                       if (x >= (NLACHANNEL_NAMEWIDTH-NLACHANNEL_BUTTON_WIDTH)) {
+                               /* toggle protection (only if there's a toggle there) */
+                               nlt->flag ^= NLATRACK_PROTECTED;
+                       }
+                       else if (x >= (NLACHANNEL_NAMEWIDTH-2*NLACHANNEL_BUTTON_WIDTH)) {
+                               /* toggle mute */
+                               nlt->flag ^= NLATRACK_MUTED;
+                       }
+                       else if (x <= ((NLACHANNEL_BUTTON_WIDTH*2)+offset)) {
+                               /* toggle 'solo' */
+                               BKE_nlatrack_solo_toggle(adt, nlt);
+                       }
+                       else {
+                               /* 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_deselect_anim_channels(ac->data, ac->datatype, 0, 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);
+                       }
+               }
+                       break;
+               case ANIMTYPE_NLAACTION:
+               {
+                       AnimData *adt= BKE_animdata_from_id(ale->owner); /* this won't crash, right? */
+                       
+                       /* for now, only do something if user clicks on the 'push-down' button */
+                       if (x >= (NLACHANNEL_NAMEWIDTH-NLACHANNEL_BUTTON_WIDTH)) {
+                               /* activate push-down function */
+                               // TODO: make this use the operator instead of calling the function directly
+                               //      however, calling the operator requires that we supply the args, and that works with proper buttons only
+                               BKE_nla_action_pushdown(adt);
+                       }
+               }
+                       break;
+                       
+               default:
+                       printf("Error: Invalid channel type in mouse_nla_channels() \n");
+       }
+       
+       /* free channels */
+       BLI_freelistN(&anim_data);
+}
+
+/* ------------------- */
+
+/* handle clicking */
+static int nlachannels_mouseclick_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+       bAnimContext ac;
+       Scene *scene;
+       ARegion *ar;
+       View2D *v2d;
+       int mval[2], channel_index;
+       short selectmode;
+       float x, y;
+       
+       /* get editor data */
+       if (ANIM_animdata_get_context(C, &ac) == 0)
+               return OPERATOR_CANCELLED;
+               
+       /* get useful pointers from animation context data */
+       scene= ac.scene;
+       ar= ac.ar;
+       v2d= &ar->v2d;
+       
+       /* get mouse coordinates (in region coordinates) */
+       mval[0]= (event->x - ar->winrct.xmin);
+       mval[1]= (event->y - ar->winrct.ymin);
+       
+       /* select mode is either replace (deselect all, then add) or add/extend */
+       if (RNA_boolean_get(op->ptr, "extend"))
+               selectmode= SELECT_INVERT;
+       else
+               selectmode= SELECT_REPLACE;
+       
+       /* figure out which channel user clicked in 
+        * Note: although channels technically start at y= ACHANNEL_FIRST, we need to adjust by half a channel's height
+        *              so that the tops of channels get caught ok. Since NLACHANNEL_FIRST is really NLACHANNEL_HEIGHT, we simply use
+        *              NLACHANNEL_HEIGHT_HALF.
+        */
+       UI_view2d_region_to_view(v2d, mval[0], mval[1], &x, &y);
+       UI_view2d_listview_view_to_cell(v2d, NLACHANNEL_NAMEWIDTH, NLACHANNEL_STEP, 0, (float)NLACHANNEL_HEIGHT_HALF, x, y, NULL, &channel_index);
+       
+       /* handle mouse-click in the relevant channel then */
+       mouse_nla_channels(&ac, x, channel_index, selectmode);
+       
+       /* set notifier tha things have changed */
+       ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_CHANNELS);
+       
+       return OPERATOR_FINISHED;
+}
+void NLA_OT_channels_click (wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name= "Mouse Click on Channels";
+       ot->idname= "NLA_OT_channels_click";
+       
+       /* api callbacks */
+       ot->invoke= nlachannels_mouseclick_invoke;
+       ot->poll= ED_operator_areaactive;
+       
+       /* flags */
+       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+       
+       /* id-props */
+       RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", ""); // SHIFTKEY
+}
+
+/* *********************************************** */
diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c
new file mode 100644 (file)
index 0000000..4fa27f4
--- /dev/null
@@ -0,0 +1,773 @@
+/**
+ * $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 <stdlib.h>
+#include <math.h>
+#include <float.h>
+
+#include "DNA_listBase.h"
+#include "DNA_anim_types.h"
+#include "DNA_action_types.h"
+#include "DNA_armature_types.h"
+#include "DNA_camera_types.h"
+#include "DNA_curve_types.h"
+#include "DNA_object_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_space_types.h"
+#include "DNA_constraint_types.h"
+#include "DNA_key_types.h"
+#include "DNA_lamp_types.h"
+#include "DNA_material_types.h"
+#include "DNA_userdef_types.h"
+#include "DNA_windowmanager_types.h"
+#include "DNA_world_types.h"
+#include "DNA_vec_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_arithb.h"
+#include "BLI_rand.h"
+
+#include "BKE_animsys.h"
+#include "BKE_nla.h"
+#include "BKE_context.h"
+#include "BKE_screen.h"
+#include "BKE_utildefines.h"
+
+#include "ED_anim_api.h"
+#include "ED_space_api.h"
+#include "ED_screen.h"
+
+#include "BIF_gl.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "UI_interface.h"
+#include "UI_interface_icons.h"
+#include "UI_resources.h"
+#include "UI_view2d.h"
+
+#include "ED_markers.h"
+
+#include "nla_intern.h"        // own include
+
+/* XXX */
+extern void gl_round_box(int mode, float minx, float miny, float maxx, float maxy, float rad);
+extern void gl_round_box_shade(int mode, float minx, float miny, float maxx, float maxy, float rad, float shadetop, float shadedown);
+
+/* *********************************************** */
+/* Strips */
+
+static void nla_strip_get_color_inside (AnimData *adt, NlaStrip *strip, float color[3])
+{
+       if ((strip->flag & NLASTRIP_FLAG_ACTIVE) && (adt && (adt->flag & ADT_NLA_EDIT_ON))) {
+               /* active strip should be drawn green when it is acting as the tweaking strip.
+                * however, this case should be skipped for when not in EditMode...
+                */
+               // FIXME: hardcoded temp-hack colors
+               color[0]= 0.3f;
+               color[1]= 0.95f;
+               color[2]= 0.1f;
+       }
+       else if (strip->flag & NLASTRIP_FLAG_TWEAKUSER) {
+               /* alert user that this strip is also used by the tweaking track (this is set when going into
+                * 'editmode' for that strip), since the edits made here may not be what the user anticipated
+                */
+               // FIXME: hardcoded temp-hack colors
+               color[0]= 0.85f;
+               color[1]= 0.0f;
+               color[2]= 0.0f;
+       }
+       else if (strip->flag & NLASTRIP_FLAG_SELECT) {
+               /* selected strip - use theme color for selected */
+               UI_GetThemeColor3fv(TH_STRIP_SELECT, color);
+       }
+       else {
+               /* normal, unselected strip - use standard strip theme color */
+               UI_GetThemeColor3fv(TH_STRIP, color);
+       }
+}
+
+static void nla_draw_strip (AnimData *adt, NlaTrack *nlt, NlaStrip *strip, View2D *v2d, float yminc, float ymaxc)
+{
+       float color[3];
+       
+       /* get color of strip */
+       nla_strip_get_color_inside(adt, strip, color);
+       
+       /* draw extrapolation info first (as backdrop) */
+       if (strip->extendmode != NLASTRIP_EXTEND_NOTHING) {
+               /* enable transparency... */
+               glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+               glEnable(GL_BLEND);
+               
+               switch (strip->extendmode) {
+                       /* since this does both sides, only do the 'before' side, and leave the rest to the next case */
+                       case NLASTRIP_EXTEND_HOLD: 
+                               /* only need to draw here if there's no strip before since 
+                                * it only applies in such a situation 
+                                */
+                               if (strip->prev == NULL) {
+                                       /* set the drawing color to the color of the strip, but with very faint alpha */
+                                       glColor4f(color[0], color[1], color[2], 0.15f);
+                                       
+                                       /* draw the rect to the edge of the screen */
+                                       glBegin(GL_QUADS);
+                                               glVertex2f(v2d->cur.xmin, yminc);
+                                               glVertex2f(v2d->cur.xmin, ymaxc);
+                                               glVertex2f(strip->start, ymaxc);
+                                               glVertex2f(strip->start, yminc);
+                                       glEnd();
+                               }
+                               /* no break needed... */
+                               
+                       /* this only draws after the strip */
+                       case NLASTRIP_EXTEND_HOLD_FORWARD: 
+                               /* only need to try and draw if the next strip doesn't occur immediately after */
+                               if ((strip->next == NULL) || (IS_EQ(strip->next->start, strip->end)==0)) {
+                                       /* set the drawing color to the color of the strip, but this time less faint */
+                                       glColor4f(color[0], color[1], color[2], 0.3f);
+                                       
+                                       /* draw the rect to the next strip or the edge of the screen */
+                                       glBegin(GL_QUADS);
+                                               glVertex2f(strip->end, yminc);
+                                               glVertex2f(strip->end, ymaxc);
+                                               
+                                               if (strip->next) {
+                                                       glVertex2f(strip->next->start, ymaxc);
+                                                       glVertex2f(strip->next->start, yminc);
+                                               }
+                                               else {
+                                                       glVertex2f(v2d->cur.xmax, ymaxc);
+                                                       glVertex2f(v2d->cur.xmax, yminc);
+                                               }
+                                       glEnd();
+                               }
+                               break;
+               }
+               
+               glDisable(GL_BLEND);
+       }
+       
+       /* draw 'inside' of strip itself */
+       glColor3fv(color);
+       uiSetRoundBox(15); /* all corners rounded */
+       gl_round_box_shade(GL_POLYGON, strip->start, yminc, strip->end, ymaxc, 0.0, 0.5, 0.1);
+       
+       /* draw strip outline - different colors are used here... */
+       if (strip->flag & NLASTRIP_FLAG_ACTIVE) {
+               /* strip should appear 'sunken', so draw a light border around it */
+               glColor3f(0.9f, 1.0f, 0.9f); // FIXME: hardcoded temp-hack colors
+       }
+       else {
+               /* strip should appear to stand out, so draw a dark border around it */
+               glColor3f(0.0f, 0.0f, 0.0f);
+       }
+       gl_round_box_shade(GL_LINE_LOOP, strip->start, yminc, strip->end, ymaxc, 0.0, 0.0, 0.1);
+} 
+
+/* add the relevant text to the cache of text-strings to draw in pixelspace */
+static void nla_draw_strip_text (NlaTrack *nlt, NlaStrip *strip, int index, View2D *v2d, float yminc, float ymaxc)
+{
+       char str[256];
+       rctf rect;
+       
+       /* for now, just init the string with a fixed-format */
+       if (strip->act)
+               sprintf(str, "%d | Act: %s | %.2f <-> %.2f", index, strip->act->id.name+2, strip->start, strip->end);
+       else
+               sprintf(str, "%d | Act: <NONE>", index);
+       
+       /* set text colour - if colours (see above) are light, draw black text, otherwise draw white */
+       if (strip->flag & (NLASTRIP_FLAG_ACTIVE|NLASTRIP_FLAG_SELECT|NLASTRIP_FLAG_TWEAKUSER))
+               glColor3f(0.0f, 0.0f, 0.0f);
+       else
+               glColor3f(1.0f, 1.0f, 1.0f);
+       
+       /* set bounding-box for text 
+        *      - padding of 2 'units' on either side
+        */
+       // TODO: make this centered?
+       rect.xmin= strip->start + 2;
+       rect.ymin= yminc;
+       rect.xmax= strip->end - 2;
+       rect.ymax= ymaxc;
+       
+       /* add this string to the cache of texts to draw*/
+       UI_view2d_text_cache_rectf(v2d, &rect, str);
+}
+
+/* ---------------------- */
+
+void draw_nla_main_data (bAnimContext *ac, SpaceNla *snla, ARegion *ar)
+{
+       ListBase anim_data = {NULL, NULL};
+       bAnimListElem *ale;
+       int filter;
+       
+       View2D *v2d= &ar->v2d;
+       float y= 0.0f;
+       int items, height;
+       
+       /* build list of channels to draw */
+       filter= (ANIMFILTER_VISIBLE|ANIMFILTER_CHANNELS);
+       items= ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
+       
+       /* Update max-extent of channels here (taking into account scrollers):
+        *      - this is done to allow the channel list to be scrollable, but must be done here
+        *        to avoid regenerating the list again and/or also because channels list is drawn first
+        *      - offset of NLACHANNEL_HEIGHT*2 is added to the height of the channels, as first is for 
+        *        start of list offset, and the second is as a correction for the scrollers.
+        */
+       height= ((items*NLACHANNEL_STEP) + (NLACHANNEL_HEIGHT*2));
+       if (height > (v2d->mask.ymax - v2d->mask.ymin)) {
+               /* don't use totrect set, as the width stays the same 
+                * (NOTE: this is ok here, the configuration is pretty straightforward) 
+                */
+               v2d->tot.ymax= (float)(height);
+       }
+       
+       /* loop through channels, and set up drawing depending on their type  */        
+       y= (float)(-NLACHANNEL_FIRST);
+       
+       for (ale= anim_data.first; ale; ale= ale->next) {
+               const float yminc= (float)(y - NLACHANNEL_HEIGHT_HALF);
+               const float ymaxc= (float)(y + NLACHANNEL_HEIGHT_HALF);
+               
+               /* check if visible */
+               if ( IN_RANGE(yminc, v2d->cur.ymin, v2d->cur.ymax) ||
+                        IN_RANGE(ymaxc, v2d->cur.ymin, v2d->cur.ymax) ) 
+               {
+                       /* data to draw depends on the type of channel */
+                       switch (ale->type) {
+                               case ANIMTYPE_NLATRACK:
+                               {
+                                       AnimData *adt= BKE_animdata_from_id(ale->id);
+                                       NlaTrack *nlt= (NlaTrack *)ale->data;
+                                       NlaStrip *strip;
+                                       int index;
+                                       
+                                       /* draw backdrop? */
+                                       // TODO...
+                                       
+                                       /* draw each strip in the track (if visible) */
+                                       for (strip=nlt->strips.first, index=1; strip; strip=strip->next, index++) {
+                                               if (BKE_nlastrip_within_bounds(strip, v2d->cur.xmin, v2d->cur.xmax)) {
+                                                       /* draw the visualisation of the strip */
+                                                       nla_draw_strip(adt, nlt, strip, v2d, yminc, ymaxc);
+                                                       
+                                                       /* add the text for this strip to the cache */
+                                                       nla_draw_strip_text(nlt, strip, index, v2d, yminc, ymaxc);
+                                               }
+                                       }
+                               }
+                                       break;
+                                       
+                               case ANIMTYPE_NLAACTION:
+                               {
+                                       AnimData *adt= BKE_animdata_from_id(ale->id);
+                                       
+                                       /* just draw a semi-shaded rect spanning the width of the viewable area if there's data */
+                                       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+                                       glEnable(GL_BLEND);
+                                       
+                                       // TODO: if tweaking some action, use the same color as for the tweaked track (quick hack done for now)
+                                       if (adt && (adt->flag & ADT_NLA_EDIT_ON)) {
+                                               // greenish color (same as tweaking strip) - hardcoded for now
+                                               glColor4f(0.3f, 0.95f, 0.1f, 0.3f); // FIXME: only draw the actual range of the action darker?
+                                       }
+                                       else {
+                                               if (ale->data)
+                                                       glColor4f(0.8f, 0.2f, 0.0f, 0.4f);      // reddish color - hardcoded for now 
+                                               else
+                                                       glColor4f(0.6f, 0.5f, 0.5f, 0.3f);      // greyish-red color - hardcoded for now
+                                       }
+                                               
+                                       /* draw slightly shifted up for greater separation from standard channels,
+                                        * but also slightly shorter for some more contrast when viewing the strips
+                                        */
+                                       glBegin(GL_QUADS);
+                                               glVertex2f(v2d->cur.xmin, yminc+NLACHANNEL_SKIP);
+                                               glVertex2f(v2d->cur.xmin, ymaxc-NLACHANNEL_SKIP);
+                                               glVertex2f(v2d->cur.xmax, ymaxc-NLACHANNEL_SKIP);
+                                               glVertex2f(v2d->cur.xmax, yminc+NLACHANNEL_SKIP);
+                                       glEnd();
+                                       
+                                       glDisable(GL_BLEND);
+                               }
+                                       break;
+                       }
+               }
+               
+               /* adjust y-position for next one */
+               y += NLACHANNEL_STEP;
+       }
+       
+       /* free tempolary channels */
+       BLI_freelistN(&anim_data);
+}
+
+/* *********************************************** */
+/* Channel List */
+
+void draw_nla_channel_list (bAnimContext *ac, SpaceNla *snla, ARegion *ar)
+{
+       ListBase anim_data = {NULL, NULL};
+       bAnimListElem *ale;
+       int filter;
+       
+       View2D *v2d= &ar->v2d;
+       float x= 0.0f, y= 0.0f;
+       int items, height;
+       
+       /* build list of channels to draw */
+       filter= (ANIMFILTER_VISIBLE|ANIMFILTER_CHANNELS);
+       items= ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
+       
+       /* Update max-extent of channels here (taking into account scrollers):
+        *      - this is done to allow the channel list to be scrollable, but must be done here
+        *        to avoid regenerating the list again and/or also because channels list is drawn first
+        *      - offset of NLACHANNEL_HEIGHT*2 is added to the height of the channels, as first is for 
+        *        start of list offset, and the second is as a correction for the scrollers.
+        */
+       height= ((items*NLACHANNEL_STEP) + (NLACHANNEL_HEIGHT*2));
+       if (height > (v2d->mask.ymax - v2d->mask.ymin)) {
+               /* don't use totrect set, as the width stays the same 
+                * (NOTE: this is ok here, the configuration is pretty straightforward) 
+                */
+               v2d->tot.ymax= (float)(height);
+       }
+       
+       /* loop through channels, and set up drawing depending on their type  */        
+       y= (float)(-NLACHANNEL_FIRST);
+       
+       for (ale= anim_data.first; ale; ale= ale->next) {
+               const float yminc= (float)(y - NLACHANNEL_HEIGHT_HALF);
+               const float ymaxc= (float)(y + NLACHANNEL_HEIGHT_HALF);
+               const float ydatac= (float)(y - 7);
+               
+               /* check if visible */
+               if ( IN_RANGE(yminc, v2d->cur.ymin, v2d->cur.ymax) ||
+                        IN_RANGE(ymaxc, v2d->cur.ymin, v2d->cur.ymax) ) 
+               {
+                       short indent= 0, offset= 0, sel= 0, group= 0;
+                       int expand= -1, protect = -1, special= -1, mute = -1;
+                       char name[128];
+                       
+                       /* determine what needs to be drawn */
+                       switch (ale->type) {
+                               case ANIMTYPE_SCENE: /* scene */
+                               {
+                                       Scene *sce= (Scene *)ale->data;
+                                       
+                                       group= 4;
+                                       indent= 0;
+                                       
+                                       special= ICON_SCENE_DATA;
+                                       
+                                       /* only show expand if there are any channels */
+                                       if (EXPANDED_SCEC(sce))
+                                               expand= ICON_TRIA_UP;
+                                       else
+                                               expand= ICON_TRIA_RIGHT;
+                                       
+                                       sel = SEL_SCEC(sce);
+                                       strcpy(name, sce->id.name+2);
+                               }
+                                       break;
+                               case ANIMTYPE_OBJECT: /* object */
+                               {
+                                       Base *base= (Base *)ale->data;
+                                       Object *ob= base->object;
+                                       
+                                       group= 4;
+                                       indent= 0;
+                                       
+                                       /* icon depends on object-type */
+                                       if (ob->type == OB_ARMATURE)
+                                               special= ICON_ARMATURE_DATA;
+                                       else    
+                                               special= ICON_OBJECT_DATA;
+                                               
+                                       /* only show expand if there are any channels */
+                                       if (EXPANDED_OBJC(ob))
+                                               expand= ICON_TRIA_UP;
+                                       else
+                                               expand= ICON_TRIA_RIGHT;
+                                       
+                                       sel = SEL_OBJC(base);
+                                       strcpy(name, ob->id.name+2);
+                               }
+                                       break;
+                               case ANIMTYPE_FILLMATD: /* object materials (dopesheet) expand widget */
+                               {
+                                       Object *ob = (Object *)ale->data;
+                                       
+                                       group = 4;
+                                       indent = 1;
+                                       special = ICON_MATERIAL_DATA;
+                                       
+                                       if (FILTER_MAT_OBJC(ob))
+                                               expand = ICON_TRIA_UP;
+                                       else
+                                               expand = ICON_TRIA_RIGHT;
+                                               
+                                       strcpy(name, "Materials");
+                               }
+                                       break;
+                               
+                               
+                               case ANIMTYPE_DSMAT: /* single material (dopesheet) expand widget */
+                               {
+                                       Material *ma = (Material *)ale->data;
+                                       
+                                       group = 0;
+                                       indent = 0;
+                                       special = ICON_MATERIAL_DATA;
+                                       offset = 21;
+                                       
+                                       if (FILTER_MAT_OBJD(ma))
+                                               expand = ICON_TRIA_UP;
+                                       else
+                                               expand = ICON_TRIA_RIGHT;
+                                       
+                                       strcpy(name, ma->id.name+2);
+                               }
+                                       break;
+                               case ANIMTYPE_DSLAM: /* lamp (dopesheet) expand widget */
+                               {
+                                       Lamp *la = (Lamp *)ale->data;
+                                       
+                                       group = 4;
+                                       indent = 1;
+                                       special = ICON_LAMP_DATA;
+                                       
+                                       if (FILTER_LAM_OBJD(la))
+                                               expand = ICON_TRIA_UP;
+                                       else
+                                               expand = ICON_TRIA_RIGHT;
+                                       
+                                       strcpy(name, la->id.name+2);
+                               }
+                                       break;
+                               case ANIMTYPE_DSCAM: /* camera (dopesheet) expand widget */
+                               {
+                                       Camera *ca = (Camera *)ale->data;
+                                       
+                                       group = 4;
+                                       indent = 1;
+                                       special = ICON_CAMERA_DATA;
+                                       
+                                       if (FILTER_CAM_OBJD(ca))
+                                               expand = ICON_TRIA_UP;
+                                       else
+                                               expand = ICON_TRIA_RIGHT;
+                                       
+                                       strcpy(name, ca->id.name+2);
+                               }
+                                       break;
+                               case ANIMTYPE_DSCUR: /* curve (dopesheet) expand widget */
+                               {
+                                       Curve *cu = (Curve *)ale->data;
+                                       
+                                       group = 4;
+                                       indent = 1;
+                                       special = ICON_CURVE_DATA;
+                                       
+                                       if (FILTER_CUR_OBJD(cu))
+                                               expand = ICON_TRIA_UP;
+                                       else
+                                               expand = ICON_TRIA_RIGHT;
+                                       
+                                       strcpy(name, cu->id.name+2);
+                               }
+                                       break;
+                               case ANIMTYPE_DSSKEY: /* shapekeys (dopesheet) expand widget */
+                               {
+                                       Key *key= (Key *)ale->data;
+                                       
+                                       group = 4;
+                                       indent = 1;
+                                       special = ICON_SHAPEKEY_DATA; // XXX 
+                                       
+                                       if (FILTER_SKE_OBJD(key))       
+                                               expand = ICON_TRIA_UP;
+                                       else
+                                               expand = ICON_TRIA_RIGHT;
+                                               
+                                       //sel = SEL_OBJC(base);
+                                       strcpy(name, "Shape Keys");
+                               }
+                                       break;
+                               case ANIMTYPE_DSWOR: /* world (dopesheet) expand widget */
+                               {
+                                       World *wo= (World *)ale->data;
+                                       
+                                       group = 4;
+                                       indent = 1;
+                                       special = ICON_WORLD_DATA;
+                                       
+                                       if (FILTER_WOR_SCED(wo))        
+                                               expand = ICON_TRIA_DOWN;
+                                       else
+                                               expand = ICON_TRIA_RIGHT;
+                                       
+                                       strcpy(name, wo->id.name+2);
+                               }
+                                       break;
+                               
+                               case ANIMTYPE_NLATRACK: /* NLA Track */
+                               {
+                                       NlaTrack *nlt= (NlaTrack *)ale->data;
+                                       
+                                       indent= 0;
+                                       
+                                       if (ale->id) {
+                                               /* special exception for materials */
+                                               if (GS(ale->id->name) == ID_MA) {
+                                                       offset= 21;
+                                                       indent= 1;
+                                               }
+                                               else
+                                                       offset= 14;
+                                       }
+                                       else
+                                               offset= 0;
+                                       
+                                       /* FIXME: 'solo' as the 'special' button?
+                                        *      - need special icons for these
+                                        */
+                                       if (nlt->flag & NLATRACK_SOLO)
+                                               special= ICON_LAYER_ACTIVE;
+                                       else
+                                               special= ICON_LAYER_USED;
+                                               
+                                       /* if this track is active and we're tweaking it, don't draw these toggles */
+                                       // TODO: need a special macro for this...
+                                       if ( ((nlt->flag & NLATRACK_ACTIVE) && (nlt->flag & NLATRACK_DISABLED)) == 0 ) 
+                                       {
+                                               if (nlt->flag & NLATRACK_MUTED)
+                                                       mute = ICON_MUTE_IPO_ON;
+                                               else    
+                                                       mute = ICON_MUTE_IPO_OFF;
+                                                       
+                                               if (EDITABLE_NLT(nlt))
+                                                       protect = ICON_UNLOCKED;
+                                               else
+                                                       protect = ICON_LOCKED;
+                                       }
+                                               
+                                       sel = SEL_NLT(nlt);
+                                       strcpy(name, nlt->name);
+                               }
+                                       break;
+                               case ANIMTYPE_NLAACTION: /* NLA Action-Line */
+                               {
+                                       bAction *act= (bAction *)ale->data;
+                                       
+                                       group = 5;
+                                       
+                                       if (ale->id) {
+                                               /* special exception for materials */
+                                               if (GS(ale->id->name) == ID_MA) {
+                                                       offset= 21;
+                                                       indent= 1;
+                                               }
+                                               else
+                                                       offset= 14;
+                                       }
+                                       else
+                                               offset= 0;
+                                       
+                                       special = ICON_ACTION;
+                                       
+                                       if (act)
+                                               sprintf(name, "ActAction: <%s>", act->id.name+2);
+                                       else
+                                               sprintf(name, "<No Action>");
+                               }
+                                       break;
+                       }       
+                       
+                       /* now, start drawing based on this information */
+                       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+                       glEnable(GL_BLEND);
+                       
+                       /* draw backing strip behind channel name */
+                       if (group == 4) {
+                               /* only used in dopesheet... */
+                               if (ELEM(ale->type, ANIMTYPE_SCENE, ANIMTYPE_OBJECT)) {
+                                       /* object channel - darker */
+                                       UI_ThemeColor(TH_DOPESHEET_CHANNELOB);
+                                       uiSetRoundBox((expand == ICON_TRIA_UP)? (8):(1|8));
+                                       gl_round_box(GL_POLYGON, x+offset,  yminc, (float)NLACHANNEL_NAMEWIDTH, ymaxc, 10);
+                               }
+                               else {
+                                       /* sub-object folders - lighter */
+                                       UI_ThemeColor(TH_DOPESHEET_CHANNELSUBOB);
+                                       
+                                       offset += 7 * indent;
+                                       glBegin(GL_QUADS);
+                                               glVertex2f(x+offset, yminc);
+                                               glVertex2f(x+offset, ymaxc);
+                                               glVertex2f((float)ACHANNEL_NAMEWIDTH, ymaxc);
+                                               glVertex2f((float)ACHANNEL_NAMEWIDTH, yminc);
+                                       glEnd();
+                                       
+                                       /* clear group value, otherwise we cause errors... */
+                                       group = 0;
+                               }
+                       }
+                       else if (group == 5) {
+                               /* Action Line */
+                               AnimData *adt= BKE_animdata_from_id(ale->id);
+                               
+                               // TODO: if tweaking some action, use the same color as for the tweaked track (quick hack done for now)
+                               if (adt && (adt->flag & ADT_NLA_EDIT_ON)) {
+                                       // greenish color (same as tweaking strip) - hardcoded for now
+                                       glColor3f(0.3f, 0.95f, 0.1f);
+                               }
+                               else {
+                                       if (ale->data)
+                                               glColor3f(0.8f, 0.2f, 0.0f);    // reddish color - hardcoded for now 
+                                       else
+                                               glColor3f(0.6f, 0.5f, 0.5f);    // greyish-red color - hardcoded for now
+                               }
+                               
+                               offset += 7 * indent;
+                               
+                               /* only on top two corners, to show that this channel sits on top of the preceeding ones */
+                               uiSetRoundBox((1|2)); 
+                               
+                               /* draw slightly shifted up vertically to look like it has more separtion from other channels,
+                                * but we then need to slightly shorten it so that it doesn't look like it overlaps
+                                */
+                               gl_round_box(GL_POLYGON, x+offset,  yminc+NLACHANNEL_SKIP, (float)NLACHANNEL_NAMEWIDTH, ymaxc+NLACHANNEL_SKIP-1, 8);
+                               
+                               /* clear group value, otherwise we cause errors... */
+                               group = 0;
+                       }
+                       else {
+                               /* for normal channels 
+                                *      - use 3 shades of color group/standard color for 3 indention level
+                                */
+                               UI_ThemeColorShade(TH_HEADER, ((indent==0)?20: (indent==1)?-20: -40));
+                               
+                               indent += group;
+                               offset += 7 * indent;
+                               glBegin(GL_QUADS);
+                                       glVertex2f(x+offset, yminc);
+                                       glVertex2f(x+offset, ymaxc);
+                                       glVertex2f((float)NLACHANNEL_NAMEWIDTH, ymaxc);
+                                       glVertex2f((float)NLACHANNEL_NAMEWIDTH, yminc);
+                               glEnd();
+                       }
+                       
+                       /* draw expand/collapse triangle */
+                       if (expand > 0) {
+                               UI_icon_draw(x+offset, ydatac, expand);
+                               offset += 17;
+                       }
+                       
+                       /* draw special icon indicating certain data-types */
+                       if (special > -1) {
+                               /* for normal channels */
+                               UI_icon_draw(x+offset, ydatac, special);
+                               offset += 17;
+                       }
+                       glDisable(GL_BLEND);
+                       
+                       /* draw name */
+                       if (sel)
+                               UI_ThemeColor(TH_TEXT_HI);
+                       else
+                               UI_ThemeColor(TH_TEXT);
+                       offset += 3;
+                       UI_DrawString(x+offset, y-4, name);
+                       
+                       /* reset offset - for RHS of panel */
+                       offset = 0;
+                       
+                       /* set blending again, as text drawing may clear it */
+                       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+                       glEnable(GL_BLEND);
+                       
+                       /* draw protect 'lock' */
+                       if (protect > -1) {
+                               offset = 16;
+                               UI_icon_draw((float)NLACHANNEL_NAMEWIDTH-offset, ydatac, protect);
+                       }
+                       
+                       /* draw mute 'eye' */
+                       if (mute > -1) {
+                               offset += 16;
+                               UI_icon_draw((float)(NLACHANNEL_NAMEWIDTH-offset), ydatac, mute);
+                       }
+                       
+                       /* draw NLA-action line 'status-icons' - only when there's an action */
+                       if ((ale->type == ANIMTYPE_NLAACTION) && (ale->data)) {
+                               AnimData *adt= BKE_animdata_from_id(ale->id);
+                               
+                               offset += 16;
+                               
+                               /* now draw some indicator icons  */
+                               if ((adt) && (adt->flag & ADT_NLA_EDIT_ON)) {
+                                       /* 'tweaking action' - not a button */
+                                       UI_icon_draw((float)NLACHANNEL_NAMEWIDTH-offset, ydatac, ICON_EDIT); 
+                               }
+                               else {
+                                       /* XXX firstly draw a little rect to help identify that it's different from the toggles */
+                                       glBegin(GL_LINE_LOOP);
+                                               glVertex2f((float)NLACHANNEL_NAMEWIDTH-offset-1, y-7);
+                                               glVertex2f((float)NLACHANNEL_NAMEWIDTH-offset-1, y+9);
+                                               glVertex2f((float)NLACHANNEL_NAMEWIDTH-1, y+9);
+                                               glVertex2f((float)NLACHANNEL_NAMEWIDTH-1, y-7);
+                                       glEnd(); // GL_LINES
+                                       
+                                       /* 'push down' icon for normal active-actions */
+                                       UI_icon_draw((float)NLACHANNEL_NAMEWIDTH-offset, ydatac, ICON_FREEZE);
+                               }
+                       }
+                       
+                       glDisable(GL_BLEND);
+               }
+               
+               /* adjust y-position for next one */
+               y += NLACHANNEL_STEP;
+       }
+       
+       /* free tempolary channels */
+       BLI_freelistN(&anim_data);
+}
+
+/* *********************************************** */
diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c
new file mode 100644 (file)
index 0000000..f705395
--- /dev/null
@@ -0,0 +1,211 @@
+/**
+ * $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 "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_nla.h"
+#include "BKE_context.h"
+#include "BKE_report.h"
+#include "BKE_screen.h"
+
+#include "ED_anim_api.h"
+#include "ED_markers.h"
+#include "ED_space_api.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 "UI_resources.h"
+#include "UI_view2d.h"
+
+#include "nla_intern.h"        // own include
+
+/* *********************************************** */
+/* General 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 */
+               ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_BOTH);
+               WM_event_add_notifier(C, NC_SCENE, NULL);
+       }
+       
+       /* done */
+       return OPERATOR_FINISHED;
+}
+void NLAEDIT_OT_tweakmode_enter (wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name= "Enter Tweak Mode";
+       ot->idname= "NLAEDIT_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 */
+               ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_BOTH);
+               WM_event_add_notifier(C, NC_SCENE, NULL);
+       }
+       
+       /* done */
+       return OPERATOR_FINISHED;
+}
+void NLAEDIT_OT_tweakmode_exit (wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name= "Exit Tweak Mode";
+       ot->idname= "NLAEDIT_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;
+}
+
+/* *********************************************** */
+
+/* *********************************************** */
index 0f6b77da6f54a3eaf6d2d97af9f5c13aeb3c8f76..0dcd3198db03f25739de64c4e36baef696fdc822 100644 (file)
 #include <string.h>
 #include <stdio.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 "MEM_guardedalloc.h"
 
 #include "BLI_blenlib.h"
+#include "BLI_arithb.h"
+#include "BLI_rand.h"
 
+#include "BKE_animsys.h"
+#include "BKE_nla.h"
 #include "BKE_context.h"
+#include "BKE_report.h"
 #include "BKE_screen.h"
-
-#include "ED_screen.h"
 #include "ED_types.h"
 #include "ED_util.h"
 
-#include "WM_api.h"
-#include "WM_types.h"
+#include "ED_anim_api.h"
+#include "ED_space_api.h"
+#include "ED_screen.h"
 
 #include "BIF_gl.h"
-#include "BIF_glutil.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"
+#include "ED_markers.h"
 
+/* button events */
+enum {
+       B_REDR  = 0,
+} eActHeader_ButEvents;
 
 /* ************************ header area region *********************** */
 
@@ -93,13 +107,17 @@ static uiBlock *dummy_viewmenu(bContext *C, ARegion *ar, void *arg_unused)
 
 static void do_nla_buttons(bContext *C, void *arg, int event)
 {
-       switch(event) {
+       switch (event) {
+               case B_REDR:
+                       ED_area_tag_redraw(CTX_wm_area(C));
+                       break;
        }
 }
 
 
 void nla_header_buttons(const bContext *C, ARegion *ar)
 {
+       SpaceNla *snla= (SpaceNla *)CTX_wm_space_data(C);
        ScrArea *sa= CTX_wm_area(C);
        uiBlock *block;
        int xco, yco= 3;
@@ -109,7 +127,7 @@ void nla_header_buttons(const bContext *C, ARegion *ar)
        
        xco= ED_area_header_standardbuttons(C, block, yco);
        
-       if((sa->flag & HEADER_NO_PULLDOWN)==0) {
+       if ((sa->flag & HEADER_NO_PULLDOWN)==0) {
                int xmax;
                
                xmax= GetButStringLength("View");
@@ -119,7 +137,47 @@ void nla_header_buttons(const bContext *C, ARegion *ar)
        }
        
        uiBlockSetEmboss(block, UI_EMBOSS);
-
+       
+       /* filtering buttons */
+       if (snla->ads) {
+               uiBlockBeginAlign(block);
+                       uiDefIconButBitI(block, TOG, ADS_FILTER_ONLYSEL, B_REDR, ICON_RESTRICT_SELECT_OFF,      (short)(xco+=XIC),yco,XIC,YIC, &(snla->ads->filterflag), 0, 0, 0, 0, "Only display selected Objects");
+                       uiDefIconButBitI(block, TOGN, ADS_FILTER_NLA_NOACT, B_REDR, ICON_ACTION,        (short)(xco+=XIC),yco,XIC,YIC, &(snla->ads->filterflag), 0, 0, 0, 0, "Include AnimData blocks with no NLA Data");
+               uiBlockEndAlign(block);
+               xco += 5;
+               
+               uiBlockBeginAlign(block);
+                       uiDefIconButBitI(block, TOGN, ADS_FILTER_NOSCE, B_REDR, ICON_SCENE_DATA,        (short)(xco+=XIC),yco,XIC,YIC, &(snla->ads->filterflag), 0, 0, 0, 0, "Display Scene Animation");
+                       uiDefIconButBitI(block, TOGN, ADS_FILTER_NOWOR, B_REDR, ICON_WORLD_DATA,        (short)(xco+=XIC),yco,XIC,YIC, &(snla->ads->filterflag), 0, 0, 0, 0, "Display World Animation");
+                       uiDefIconButBitI(block, TOGN, ADS_FILTER_NOSHAPEKEYS, B_REDR, ICON_SHAPEKEY_DATA,       (short)(xco+=XIC),yco,XIC,YIC, &(snla->ads->filterflag), 0, 0, 0, 0, "Display ShapeKeys");
+                       uiDefIconButBitI(block, TOGN, ADS_FILTER_NOMAT, B_REDR, ICON_MATERIAL_DATA,     (short)(xco+=XIC),yco,XIC,YIC, &(snla->ads->filterflag), 0, 0, 0, 0, "Display Materials");
+                       uiDefIconButBitI(block, TOGN, ADS_FILTER_NOLAM, B_REDR, ICON_LAMP_DATA, (short)(xco+=XIC),yco,XIC,YIC, &(snla->ads->filterflag), 0, 0, 0, 0, "Display Lamps");
+                       uiDefIconButBitI(block, TOGN, ADS_FILTER_NOCAM, B_REDR, ICON_CAMERA_DATA,       (short)(xco+=XIC),yco,XIC,YIC, &(snla->ads->filterflag), 0, 0, 0, 0, "Display Cameras");
+                       uiDefIconButBitI(block, TOGN, ADS_FILTER_NOCUR, B_REDR, ICON_CURVE_DATA,        (short)(xco+=XIC),yco,XIC,YIC, &(snla->ads->filterflag), 0, 0, 0, 0, "Display Curves");
+               uiBlockEndAlign(block);
+               xco += 15;
+       }
+       else {
+               // XXX this case shouldn't happen at all... for now, just pad out same amount of space
+               xco += 7*XIC + 15;
+       }
+       xco += (XIC + 8);
+       
+       /* auto-snap selector */
+       if (snla->flag & SNLA_DRAWTIME) {
+               uiDefButS(block, MENU, B_REDR,
+                               "Auto-Snap Keyframes %t|No Time-Snap %x0|Nearest Second %x2|Nearest Marker %x3", 
+                               xco,yco,90,YIC, &snla->autosnap, 0, 1, 0, 0, 
+                               "Auto-snapping mode for times when transforming");
+       }
+       else {
+               uiDefButS(block, MENU, B_REDR, 
+                               "Auto-Snap Keyframes %t|No Time-Snap %x0|Nearest Frame %x2|Nearest Marker %x3", 
+                               xco,yco,90,YIC, &snla->autosnap, 0, 1, 0, 0, 
+                               "Auto-snapping mode for times when transforming");
+       }
+       xco += 98;
+       
        /* always as last  */
        UI_view2d_totRect_set(&ar->v2d, xco+XIC+80, ar->v2d.tot.ymax-ar->v2d.tot.ymin);
        
index c544bd9a40806b48bec499ed0ed39244103c4b83..3d8cb7548c0448edff7735af80c3f021febc0145 100644 (file)
  * 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) 2008 Blender Foundation.
+ * The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung.
  * All rights reserved.
  *
  * 
- * Contributor(s): Blender Foundation
+ * Contributor(s): Blender Foundation, Joshua Leung
  *
  * ***** END GPL LICENSE BLOCK *****
  */
 
 /* internal exports only */
 
+/* **************************************** */
+/* Macros, etc. only used by NLA */
 
+/* -------------- NLA Channel Defines -------------- */
+
+/* NLA channel heights */
+#define NLACHANNEL_FIRST                       -16
+#define        NLACHANNEL_HEIGHT                       24
+#define NLACHANNEL_HEIGHT_HALF 12
+#define        NLACHANNEL_SKIP                 2
+#define NLACHANNEL_STEP                        (NLACHANNEL_HEIGHT + NLACHANNEL_SKIP)
+
+/* channel widths */
+#define NLACHANNEL_NAMEWIDTH           200
+
+/* channel toggle-buttons */
+#define NLACHANNEL_BUTTON_WIDTH        16
+
+
+/* **************************************** */
+/* nla_draw.c */
+
+void draw_nla_main_data(bAnimContext *ac, SpaceNla *snla, ARegion *ar);
+void draw_nla_channel_list(bAnimContext *ac, SpaceNla *snla, ARegion *ar);
+
+/* **************************************** */
 /* nla_header.c */
+
 void nla_header_buttons(const bContext *C, ARegion *ar);
 
+/* **************************************** */
+/* nla_select.c */
+
+/* defines for left-right select tool */
+enum {
+       NLAEDIT_LRSEL_TEST      = -1,
+       NLAEDIT_LRSEL_NONE,
+       NLAEDIT_LRSEL_LEFT,
+       NLAEDIT_LRSEL_RIGHT,
+} eNlaEdit_LeftRightSelect_Mode;
+
+/* --- */
+
+void NLAEDIT_OT_select_all_toggle(wmOperatorType *ot);
+void NLAEDIT_OT_click_select(wmOperatorType *ot);
+
+/* **************************************** */
+/* nla_edit.c */
+
+void NLAEDIT_OT_tweakmode_enter(wmOperatorType *ot);
+void NLAEDIT_OT_tweakmode_exit(wmOperatorType *ot);
+
+/* **************************************** */
+/* nla_channels.c */
+
+void NLA_OT_channels_select_border(wmOperatorType *ot);
+void NLA_OT_channels_click(wmOperatorType *ot);
+
+/* **************************************** */
+/* nla_ops.c */
+
+int nlaop_poll_tweakmode_off(bContext *C);
+int nlaop_poll_tweakmode_on (bContext *C);
+
+/* --- */
+
+void nla_operatortypes(void);
+void nla_keymap(wmWindowManager *wm);
 
 #endif /* ED_NLA_INTERN_H */
 
diff --git a/source/blender/editors/space_nla/nla_ops.c b/source/blender/editors/space_nla/nla_ops.c
new file mode 100644 (file)
index 0000000..057e4b0
--- /dev/null
@@ -0,0 +1,214 @@
+/**
+ * $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 "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_nla.h"
+#include "BKE_context.h"
+#include "BKE_report.h"
+#include "BKE_screen.h"
+
+#include "ED_anim_api.h"
+#include "ED_space_api.h"
+#include "ED_screen.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "RNA_access.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+#include "UI_view2d.h"
+
+#include "nla_intern.h"        // own include
+
+/* ************************** poll callbacks for operators **********************************/
+
+/* tweakmode is NOT enabled */
+int nlaop_poll_tweakmode_off (bContext *C)
+{
+       Scene *scene;
+       
+       /* for now, we check 2 things: 
+        *      1) active editor must be NLA
+        *      2) tweakmode is currently set as a 'per-scene' flag 
+        *         so that it will affect entire NLA data-sets,
+        *         but not all AnimData blocks will be in tweakmode for 
+        *         various reasons
+        */
+       if (ED_operator_nla_active(C) == 0)
+               return 0;
+       
+       scene= CTX_data_scene(C);
+       if ((scene == NULL) || (scene->flag & SCE_NLA_EDIT_ON))
+               return 0;
+       
+       return 1;
+}
+
+/* tweakmode IS enabled */
+int nlaop_poll_tweakmode_on (bContext *C)
+{
+       Scene *scene;
+       
+       /* for now, we check 2 things: 
+        *      1) active editor must be NLA
+        *      2) tweakmode is currently set as a 'per-scene' flag 
+        *         so that it will affect entire NLA data-sets,
+        *         but not all AnimData blocks will be in tweakmode for 
+        *         various reasons
+        */
+       if (ED_operator_nla_active(C) == 0)
+               return 0;
+       
+       scene= CTX_data_scene(C);
+       if ((scene == NULL) || !(scene->flag & SCE_NLA_EDIT_ON))
+               return 0;
+       
+       return 1;
+}
+
+/* ************************** registration - operator types **********************************/
+
+void nla_operatortypes(void)
+{
+       /* channels */
+       WM_operatortype_append(NLA_OT_channels_click);
+       WM_operatortype_append(NLA_OT_channels_select_border);
+       
+       /* select */
+       WM_operatortype_append(NLAEDIT_OT_click_select);
+       WM_operatortype_append(NLAEDIT_OT_select_all_toggle);
+       
+       /* edit */
+       WM_operatortype_append(NLAEDIT_OT_tweakmode_enter);
+       WM_operatortype_append(NLAEDIT_OT_tweakmode_exit);
+}
+
+/* ************************** registration - keymaps **********************************/
+
+static void nla_keymap_channels (wmWindowManager *wm, ListBase *keymap)
+{
+       /* NLA-specific (different to standard channels keymap) -------------------------- */
+       /* selection */
+               /* click-select */
+               // XXX for now, only leftmouse.... 
+       WM_keymap_add_item(keymap, "NLA_OT_channels_click", LEFTMOUSE, KM_PRESS, 0, 0);
+       RNA_boolean_set(WM_keymap_add_item(keymap, "NLA_OT_channels_click", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0)->ptr, "extend", 1);
+       
+       /* borderselect */ 
+       WM_keymap_add_item(keymap, "NLA_OT_channels_select_border", BKEY, KM_PRESS, 0, 0);
+       
+       /* General Animation Channels keymap (see anim_channels.c) ----------------------- */
+               /* deselect all */
+       WM_keymap_add_item(keymap, "ANIM_OT_channels_select_all_toggle", AKEY, KM_PRESS, 0, 0);
+       RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_select_all_toggle", IKEY, KM_PRESS, KM_CTRL, 0)->ptr, "invert", 1);
+       
+       /* settings */
+       WM_keymap_add_item(keymap, "ANIM_OT_channels_setting_toggle", WKEY, KM_PRESS, KM_SHIFT, 0);
+       WM_keymap_add_item(keymap, "ANIM_OT_channels_setting_enable", WKEY, KM_PRESS, KM_CTRL|KM_SHIFT, 0);
+       WM_keymap_add_item(keymap, "ANIM_OT_channels_setting_disable", WKEY, KM_PRESS, KM_ALT, 0);
+       
+       /* settings - specialised hotkeys */
+       WM_keymap_add_item(keymap, "ANIM_OT_channels_editable_toggle", TABKEY, KM_PRESS, 0, 0);
+       
+       /* expand/collapse */
+       WM_keymap_add_item(keymap, "ANIM_OT_channels_expand", PADPLUSKEY, KM_PRESS, 0, 0);
+       WM_keymap_add_item(keymap, "ANIM_OT_channels_collapse", PADMINUS, KM_PRESS, 0, 0);
+       
+       RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_expand", PADPLUSKEY, KM_PRESS, KM_CTRL, 0)->ptr, "all", 1);
+       RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_collapse", PADMINUS, KM_PRESS, KM_CTRL, 0)->ptr, "all", 1);
+}
+
+static void nla_keymap_main (wmWindowManager *wm, ListBase *keymap)
+{
+       wmKeymapItem *kmi;
+       
+       /* selection */
+               /* click select */
+       WM_keymap_add_item(keymap, "NLAEDIT_OT_click_select", SELECTMOUSE, KM_PRESS, 0, 0);
+       kmi= WM_keymap_add_item(keymap, "NLAEDIT_OT_click_select", SELECTMOUSE, KM_PRESS, KM_SHIFT, 0);
+               RNA_boolean_set(kmi->ptr, "extend", 1);
+       kmi= WM_keymap_add_item(keymap, "NLAEDIT_OT_click_select", SELECTMOUSE, KM_PRESS, KM_CTRL, 0);
+               RNA_enum_set(kmi->ptr, "left_right", NLAEDIT_LRSEL_TEST);       
+       
+               /* deselect all */
+       WM_keymap_add_item(keymap, "NLAEDIT_OT_select_all_toggle", AKEY, KM_PRESS, 0, 0);
+       RNA_boolean_set(WM_keymap_add_item(keymap, "NLAEDIT_OT_select_all_toggle", IKEY, KM_PRESS, KM_CTRL, 0)->ptr, "invert", 1);
+       
+       /* editing */
+               /* tweakmode 
+                *      - enter and exit are separate operators with the same hotkey... 
+                *        This works as they use different poll()'s
+                */
+       WM_keymap_add_item(keymap, "NLAEDIT_OT_tweakmode_enter", TABKEY, KM_PRESS, 0, 0);
+       WM_keymap_add_item(keymap, "NLAEDIT_OT_tweakmode_exit", TABKEY, KM_PRESS, 0, 0);
+       
+       /* transform system */
+       //transform_keymap_for_space(wm, keymap, SPACE_NLA);
+}
+
+/* --------------- */
+
+void nla_keymap(wmWindowManager *wm)
+{
+       ListBase *keymap;
+       
+       /* channels */
+       /* Channels are not directly handled by the NLA Editor module, but are inherited from the Animation module. 
+        * Most of the relevant operations, keymaps, drawing, etc. can therefore all be found in that module instead, as there
+        * are many similarities with the other Animation Editors.
+        *
+        * However, those operations which involve clicking on channels and/or the placement of them in the view are implemented here instead
+        */
+       keymap= WM_keymap_listbase(wm, "NLA Channels", SPACE_NLA, 0);
+       nla_keymap_channels(wm, keymap);
+       
+       /* data */
+       keymap= WM_keymap_listbase(wm, "NLA Data", SPACE_NLA, 0);
+       nla_keymap_main(wm, keymap);
+}
+
diff --git a/source/blender/editors/space_nla/nla_select.c b/source/blender/editors/space_nla/nla_select.c
new file mode 100644 (file)
index 0000000..463479c
--- /dev/null
@@ -0,0 +1,455 @@
+/**
+ * $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 "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_nla.h"
+#include "BKE_context.h"
+#include "BKE_report.h"
+#include "BKE_screen.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 "RNA_access.h"
+#include "RNA_define.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
+
+/* ******************** Utilities ***************************************** */
+
+/* Convert SELECT_* flags to ACHANNEL_SETFLAG_* flags */
+static short selmodes_to_flagmodes (short sel)
+{
+       /* convert selection modes to selection modes */
+       switch (sel) {
+               case SELECT_SUBTRACT:
+                       return ACHANNEL_SETFLAG_CLEAR;
+                       break;
+                       
+               case SELECT_INVERT:
+                       return ACHANNEL_SETFLAG_TOGGLE;
+                       break;
+                       
+               case SELECT_ADD:
+               default:
+                       return ACHANNEL_SETFLAG_ADD;
+                       break;
+       }
+}
+
+
+/* ******************** Deselect All Operator ***************************** */
+/* This operator works in one of three ways:
+ *     1) (de)select all (AKEY) - test if select all or deselect all
+ *     2) invert all (CTRL-IKEY) - invert selection of all keyframes
+ *     3) (de)select all - no testing is done; only for use internal tools as normal function...
+ */
+
+enum {
+       DESELECT_STRIPS_NOTEST = 0,
+       DESELECT_STRIPS_TEST,
+       DESELECT_STRIPS_CLEARACTIVE,
+} eDeselectNlaStrips;
+/* Deselects strips in the NLA Editor
+ *     - This is called by the deselect all operator, as well as other ones!
+ *
+ *     - test: check if select or deselect all (1) or clear all active (2)
+ *     - sel: how to select keyframes 
+ *             0 = deselect
+ *             1 = select
+ *             2 = invert
+ */
+static void deselect_nla_strips (bAnimContext *ac, short test, short sel)
+{
+       ListBase anim_data = {NULL, NULL};
+       bAnimListElem *ale;
+       int filter;
+       short smode;
+       
+       /* determine type-based settings - curvesonly eliminates all the unnecessary channels... */
+       filter= (ANIMFILTER_VISIBLE|ANIMFILTER_CURVESONLY);