Dopesheet: Keyframe size can be adjusted as part of theme settings
[blender.git] / source / blender / editors / animation / anim_filter.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version. 
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2008 Blender Foundation, Joshua Leung
19  * All rights reserved.
20  *
21  * 
22  * Contributor(s): Joshua Leung (original author)
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/editors/animation/anim_filter.c
28  *  \ingroup edanimation
29  */
30
31
32 /* This file contains a system used to provide a layer of abstraction between sources
33  * of animation data and tools in Animation Editors. The method used here involves 
34  * generating a list of edit structures which enable tools to naively perform the actions 
35  * they require without all the boiler-plate associated with loops within loops and checking 
36  * for cases to ignore. 
37  *
38  * While this is primarily used for the Action/Dopesheet Editor (and its accessory modes),
39  * the Graph Editor also uses this for its channel list and for determining which curves
40  * are being edited. Likewise, the NLA Editor also uses this for its channel list and in
41  * its operators.
42  *
43  * Note: much of the original system this was based on was built before the creation of the RNA
44  * system. In future, it would be interesting to replace some parts of this code with RNA queries,
45  * however, RNA does not eliminate some of the boiler-plate reduction benefits presented by this 
46  * system, so if any such work does occur, it should only be used for the internals used here...
47  *
48  * -- Joshua Leung, Dec 2008 (Last revision July 2009)
49  */
50
51 #include <string.h>
52
53 #include "DNA_anim_types.h"
54 #include "DNA_armature_types.h"
55 #include "DNA_camera_types.h"
56 #include "DNA_lamp_types.h"
57 #include "DNA_lattice_types.h"
58 #include "DNA_linestyle_types.h"
59 #include "DNA_key_types.h"
60 #include "DNA_mask_types.h"
61 #include "DNA_material_types.h"
62 #include "DNA_mesh_types.h"
63 #include "DNA_meta_types.h"
64 #include "DNA_node_types.h"
65 #include "DNA_particle_types.h"
66 #include "DNA_space_types.h"
67 #include "DNA_sequence_types.h"
68 #include "DNA_scene_types.h"
69 #include "DNA_screen_types.h"
70 #include "DNA_speaker_types.h"
71 #include "DNA_world_types.h"
72 #include "DNA_gpencil_types.h"
73 #include "DNA_object_types.h"
74 #include "DNA_userdef_types.h"
75
76 #include "MEM_guardedalloc.h"
77
78 #include "BLI_blenlib.h"
79 #include "BLI_utildefines.h"
80 #include "BLI_alloca.h"
81 #include "BLI_ghash.h"
82 #include "BLI_string.h"
83
84 #include "BKE_animsys.h"
85 #include "BKE_action.h"
86 #include "BKE_fcurve.h"
87 #include "BKE_context.h"
88 #include "BKE_global.h"
89 #include "BKE_group.h"
90 #include "BKE_key.h"
91 #include "BKE_main.h"
92 #include "BKE_material.h"
93 #include "BKE_modifier.h"
94 #include "BKE_node.h"
95 #include "BKE_mask.h"
96 #include "BKE_sequencer.h"
97
98 #include "ED_anim_api.h"
99 #include "ED_markers.h"
100
101 #include "UI_resources.h"  /* for TH_KEYFRAME_SCALE lookup */
102
103 /* ************************************************************ */
104 /* Blender Context <-> Animation Context mapping */
105
106 /* ----------- Private Stuff - General -------------------- */
107
108 /* Get vertical scaling factor (i.e. typically used for keyframe size) */
109 static void animedit_get_yscale_factor(bAnimContext *ac)
110 {
111         bTheme *btheme = UI_GetTheme();
112         
113         /* grab scale factor directly from action editor setting
114          * NOTE: This theme setting doesn't have an ID, as it cannot be accessed normally
115          *       since it is a float, and the theem settings methods can only handle chars.
116          */
117         ac->yscale_fac = btheme->tact.keyframe_scale_fac;
118         
119         /* clamp to avoid problems with uninitialised values... */
120         if (ac->yscale_fac < 0.1f)
121                 ac->yscale_fac = 1.0f;
122         //printf("yscale_fac = %f\n", ac->yscale_fac);
123 }
124
125 /* ----------- Private Stuff - Action Editor ------------- */
126
127 /* Get shapekey data being edited (for Action Editor -> ShapeKey mode) */
128 /* Note: there's a similar function in key.c (BKE_key_from_object) */
129 static Key *actedit_get_shapekeys(bAnimContext *ac)
130 {
131         Scene *scene = ac->scene;
132         Object *ob;
133         Key *key;
134         
135         ob = OBACT;
136         if (ob == NULL) 
137                 return NULL;
138         
139         /* XXX pinning is not available in 'ShapeKey' mode... */
140         //if (saction->pin) return NULL;
141         
142         /* shapekey data is stored with geometry data */
143         key = BKE_key_from_object(ob);
144         
145         if (key) {
146                 if (key->type == KEY_RELATIVE)
147                         return key;
148         }
149         
150         return NULL;
151 }
152
153 /* Get data being edited in Action Editor (depending on current 'mode') */
154 static bool actedit_get_context(bAnimContext *ac, SpaceAction *saction)
155 {
156         /* get dopesheet */
157         ac->ads = &saction->ads;
158         
159         /* sync settings with current view status, then return appropriate data */
160         switch (saction->mode) {
161                 case SACTCONT_ACTION: /* 'Action Editor' */
162                         /* if not pinned, sync with active object */
163                         if (/*saction->pin == 0*/ true) {
164                                 if (ac->obact && ac->obact->adt)
165                                         saction->action = ac->obact->adt->action;
166                                 else
167                                         saction->action = NULL;
168                         }
169                         
170                         ac->datatype = ANIMCONT_ACTION;
171                         ac->data = saction->action;
172                         
173                         ac->mode = saction->mode;
174                         return true;
175                         
176                 case SACTCONT_SHAPEKEY: /* 'ShapeKey Editor' */
177                         ac->datatype = ANIMCONT_SHAPEKEY;
178                         ac->data = actedit_get_shapekeys(ac);
179                         
180                         /* if not pinned, sync with active object */
181                         if (/*saction->pin == 0*/ true) {
182                                 Key *key = (Key *)ac->data;
183                                 
184                                 if (key && key->adt)
185                                         saction->action = key->adt->action;
186                                 else
187                                         saction->action = NULL;
188                         }
189                         
190                         ac->mode = saction->mode;
191                         return true;
192                 
193                 case SACTCONT_GPENCIL: /* Grease Pencil */ /* XXX review how this mode is handled... */
194                         /* update scene-pointer (no need to check for pinning yet, as not implemented) */
195                         saction->ads.source = (ID *)ac->scene;
196                         
197                         ac->datatype = ANIMCONT_GPENCIL;
198                         ac->data = &saction->ads;
199                         
200                         ac->mode = saction->mode;
201                         return true;
202                         
203                 case SACTCONT_MASK: /* Mask */ /* XXX review how this mode is handled... */
204                 {
205                         /* TODO, other methods to get the mask */
206                         // Sequence *seq = BKE_sequencer_active_get(ac->scene);
207                         //MovieClip *clip = ac->scene->clip;
208 //                      struct Mask *mask = seq ? seq->mask : NULL;
209                         
210                         /* update scene-pointer (no need to check for pinning yet, as not implemented) */
211                         saction->ads.source = (ID *)ac->scene;
212                         
213                         ac->datatype = ANIMCONT_MASK;
214                         ac->data = &saction->ads;
215                         
216                         ac->mode = saction->mode;
217                         return true;
218                 }
219                 case SACTCONT_DOPESHEET: /* DopeSheet */
220                         /* update scene-pointer (no need to check for pinning yet, as not implemented) */
221                         saction->ads.source = (ID *)ac->scene;
222                         
223                         ac->datatype = ANIMCONT_DOPESHEET;
224                         ac->data = &saction->ads;
225                         
226                         ac->mode = saction->mode;
227                         return true;
228                 
229                 default: /* unhandled yet */
230                         ac->datatype = ANIMCONT_NONE;
231                         ac->data = NULL;
232                         
233                         ac->mode = -1;
234                         return false;
235         }
236 }
237
238 /* ----------- Private Stuff - Graph Editor ------------- */
239
240 /* Get data being edited in Graph Editor (depending on current 'mode') */
241 static bool graphedit_get_context(bAnimContext *ac, SpaceIpo *sipo)
242 {
243         /* init dopesheet data if non-existent (i.e. for old files) */
244         if (sipo->ads == NULL) {
245                 sipo->ads = MEM_callocN(sizeof(bDopeSheet), "GraphEdit DopeSheet");
246                 sipo->ads->source = (ID *)ac->scene;
247         }
248         ac->ads = sipo->ads;
249         
250         /* set settings for Graph Editor - "Selected = Editable" */
251         if (sipo->flag & SIPO_SELCUVERTSONLY)
252                 sipo->ads->filterflag |= ADS_FILTER_SELEDIT;
253         else
254                 sipo->ads->filterflag &= ~ADS_FILTER_SELEDIT;
255         
256         /* sync settings with current view status, then return appropriate data */
257         switch (sipo->mode) {
258                 case SIPO_MODE_ANIMATION:  /* Animation F-Curve Editor */
259                         /* update scene-pointer (no need to check for pinning yet, as not implemented) */
260                         sipo->ads->source = (ID *)ac->scene;
261                         sipo->ads->filterflag &= ~ADS_FILTER_ONLYDRIVERS;
262                         
263                         ac->datatype = ANIMCONT_FCURVES;
264                         ac->data = sipo->ads;
265                         
266                         ac->mode = sipo->mode;
267                         return true;
268                 
269                 case SIPO_MODE_DRIVERS:  /* Driver F-Curve Editor */
270                         /* update scene-pointer (no need to check for pinning yet, as not implemented) */
271                         sipo->ads->source = (ID *)ac->scene;
272                         sipo->ads->filterflag |= ADS_FILTER_ONLYDRIVERS;
273                         
274                         ac->datatype = ANIMCONT_DRIVERS;
275                         ac->data = sipo->ads;
276                         
277                         ac->mode = sipo->mode;
278                         return true;
279                 
280                 default: /* unhandled yet */
281                         ac->datatype = ANIMCONT_NONE;
282                         ac->data = NULL;
283                         
284                         ac->mode = -1;
285                         return false;
286         }
287 }
288
289 /* ----------- Private Stuff - NLA Editor ------------- */
290
291 /* Get data being edited in Graph Editor (depending on current 'mode') */
292 static bool nlaedit_get_context(bAnimContext *ac, SpaceNla *snla)
293 {
294         /* init dopesheet data if non-existent (i.e. for old files) */
295         if (snla->ads == NULL)
296                 snla->ads = MEM_callocN(sizeof(bDopeSheet), "NlaEdit DopeSheet");
297         ac->ads = snla->ads;
298         
299         /* sync settings with current view status, then return appropriate data */
300         /* update scene-pointer (no need to check for pinning yet, as not implemented) */
301         snla->ads->source = (ID *)ac->scene;
302         snla->ads->filterflag |= ADS_FILTER_ONLYNLA;
303         
304         ac->datatype = ANIMCONT_NLA;
305         ac->data = snla->ads;
306         
307         return true;
308 }
309
310 /* ----------- Public API --------------- */
311
312 /* Obtain current anim-data context, given that context info from Blender context has already been set 
313  *      - AnimContext to write to is provided as pointer to var on stack so that we don't have
314  *        allocation/freeing costs (which are not that avoidable with channels).
315  */
316 bool ANIM_animdata_context_getdata(bAnimContext *ac)
317 {
318         SpaceLink *sl = ac->sl;
319         bool ok = false;
320         
321         /* context depends on editor we are currently in */
322         if (sl) {
323                 switch (ac->spacetype) {
324                         case SPACE_ACTION:
325                         {
326                                 SpaceAction *saction = (SpaceAction *)sl;
327                                 ok = actedit_get_context(ac, saction);
328                                 break;
329                         }
330                         case SPACE_IPO:
331                         {
332                                 SpaceIpo *sipo = (SpaceIpo *)sl;
333                                 ok = graphedit_get_context(ac, sipo);
334                                 break;
335                         }
336                         case SPACE_NLA:
337                         {
338                                 SpaceNla *snla = (SpaceNla *)sl;
339                                 ok = nlaedit_get_context(ac, snla);
340                                 break;
341                         }
342                 }
343         }
344         
345         /* check if there's any valid data */
346         return (ok && ac->data);
347 }
348
349 /* Obtain current anim-data context from Blender Context info 
350  *      - AnimContext to write to is provided as pointer to var on stack so that we don't have
351  *        allocation/freeing costs (which are not that avoidable with channels).
352  *      - Clears data and sets the information from Blender Context which is useful
353  */
354 bool ANIM_animdata_get_context(const bContext *C, bAnimContext *ac)
355 {
356         ScrArea *sa = CTX_wm_area(C);
357         ARegion *ar = CTX_wm_region(C);
358         SpaceLink *sl = CTX_wm_space_data(C);
359         Scene *scene = CTX_data_scene(C);
360         
361         /* clear old context info */
362         if (ac == NULL) return false;
363         memset(ac, 0, sizeof(bAnimContext));
364         
365         /* get useful default context settings from context */
366         ac->scene = scene;
367         if (scene) {
368                 ac->markers = ED_context_get_markers(C);
369                 ac->obact = (scene->basact) ?  scene->basact->object : NULL;
370         }
371         ac->sa = sa;
372         ac->ar = ar;
373         ac->sl = sl;
374         ac->spacetype = (sa) ? sa->spacetype : 0;
375         ac->regiontype = (ar) ? ar->regiontype : 0;
376         
377         /* initialise default y-scale factor */
378         animedit_get_yscale_factor(ac);
379         
380         /* get data context info */
381         // XXX: if the below fails, try to grab this info from context instead... (to allow for scripting)
382         return ANIM_animdata_context_getdata(ac);
383 }
384
385 /* ************************************************************ */
386 /* Blender Data <-- Filter --> Channels to be operated on */
387
388 /* macros to use before/after getting the sub-channels of some channel,
389  * to abstract away some of the tricky logic involved
390  *
391  * cases:
392  *      1) Graph Edit main area (just data) OR channels visible in Channel List
393  *      2) If not showing channels, we're only interested in the data (Action Editor's editing)
394  *      3) We don't care what data, we just care there is some (so that a collapsed 
395  *         channel can be kept around). No need to clear channels-flag in order to 
396  *         keep expander channels with no sub-data out, as those cases should get
397  *         dealt with by the recursive detection idiom in place.
398  *
399  * Implementation Note:
400  *  YES the _doSubChannels variable is NOT read anywhere. BUT, this is NOT an excuse
401  *  to go steamrolling the logic into a single-line expression as from experience,
402  *  those are notoriously difficult to read + debug when extending later on. The code
403  *  below is purposefully laid out so that each case noted above corresponds clearly to
404  *  one case below.
405  */
406 #define BEGIN_ANIMFILTER_SUBCHANNELS(expanded_check) \
407         { \
408                 int _filter = filter_mode; \
409                 short _doSubChannels = 0; \
410                 if (!(filter_mode & ANIMFILTER_LIST_VISIBLE) || (expanded_check)) \
411                         _doSubChannels = 1; \
412                 else if (!(filter_mode & ANIMFILTER_LIST_CHANNELS)) \
413                         _doSubChannels = 2; \
414                 else { \
415                         filter_mode |= ANIMFILTER_TMP_PEEK; \
416                 } \
417                  \
418                 { \
419                         (void) _doSubChannels; \
420                 }
421 /* ... standard sub-channel filtering can go on here now ... */
422 #define END_ANIMFILTER_SUBCHANNELS \
423                 filter_mode = _filter; \
424         } (void)0
425
426 /* ............................... */
427
428 /* quick macro to test if AnimData is usable */
429 #define ANIMDATA_HAS_KEYS(id) ((id)->adt && (id)->adt->action)
430
431 /* quick macro to test if AnimData is usable for drivers */
432 #define ANIMDATA_HAS_DRIVERS(id) ((id)->adt && (id)->adt->drivers.first)
433
434 /* quick macro to test if AnimData is usable for NLA */
435 #define ANIMDATA_HAS_NLA(id) ((id)->adt && (id)->adt->nla_tracks.first)
436
437 /* Quick macro to test for all three above usability tests, performing the appropriate provided 
438  * action for each when the AnimData context is appropriate. 
439  *
440  * Priority order for this goes (most important, to least): AnimData blocks, NLA, Drivers, Keyframes.
441  *
442  * For this to work correctly, a standard set of data needs to be available within the scope that this
443  * gets called in: 
444  *  - ListBase anim_data;
445  *  - bDopeSheet *ads;
446  *  - bAnimListElem *ale;
447  *  - size_t items;
448  *
449  *  - id: ID block which should have an AnimData pointer following it immediately, to use
450  *  - adtOk: line or block of code to execute for AnimData-blocks case (usually ANIMDATA_ADD_ANIMDATA)
451  *  - nlaOk: line or block of code to execute for NLA tracks+strips case
452  *  - driversOk: line or block of code to execute for Drivers case
453  *  - nlaKeysOk: line or block of code for NLA Strip Keyframes case
454  *  - keysOk: line or block of code for Keyframes case
455  *
456  * The checks for the various cases are as follows:
457  *      0) top level: checks for animdata and also that all the F-Curves for the block will be visible
458  *      1) animdata check: for filtering animdata blocks only
459  *      2A) nla tracks: include animdata block's data as there are NLA tracks+strips there
460  *      2B) actions to convert to nla: include animdata block's data as there is an action that can be 
461  *              converted to a new NLA strip, and the filtering options allow this
462  *      2C) allow non-animated datablocks to be included so that datablocks can be added
463  *      3) drivers: include drivers from animdata block (for Drivers mode in Graph Editor)
464  *  4A) nla strip keyframes: these are the per-strip controls for time and influence
465  *      4B) normal keyframes: only when there is an active action
466  */
467 #define ANIMDATA_FILTER_CASES(id, adtOk, nlaOk, driversOk, nlaKeysOk, keysOk) \
468         { \
469                 if ((id)->adt) { \
470                         if (!(filter_mode & ANIMFILTER_CURVE_VISIBLE) || !((id)->adt->flag & ADT_CURVES_NOT_VISIBLE)) { \
471                                 if (filter_mode & ANIMFILTER_ANIMDATA) { \
472                                         adtOk \
473                                 } \
474                                 else if (ads->filterflag & ADS_FILTER_ONLYNLA) { \
475                                         if (ANIMDATA_HAS_NLA(id)) { \
476                                                 nlaOk \
477                                         } \
478                                         else if (!(ads->filterflag & ADS_FILTER_NLA_NOACT) || ANIMDATA_HAS_KEYS(id)) { \
479                                                 nlaOk \
480                                         } \
481                                 } \
482                                 else if (ads->filterflag & ADS_FILTER_ONLYDRIVERS) { \
483                                         if (ANIMDATA_HAS_DRIVERS(id)) { \
484                                                 driversOk \
485                                         } \
486                                 } \
487                                 else { \
488                                         if (ANIMDATA_HAS_NLA(id)) { \
489                                                 nlaKeysOk \
490                                         } \
491                                         if (ANIMDATA_HAS_KEYS(id)) { \
492                                                 keysOk \
493                                         } \
494                                 } \
495                         } \
496                 } \
497         } (void)0
498
499 /* ............................... */
500
501 /* Add a new animation channel, taking into account the "peek" flag, which is used to just check 
502  * whether any channels will be added (but without needing them to actually get created).
503  *
504  * ! This causes the calling function to return early if we're only "peeking" for channels
505  */
506 // XXX: ale_statement stuff is really a hack for one special case. It shouldn't really be needed...
507 #define ANIMCHANNEL_NEW_CHANNEL_FULL(channel_data, channel_type, owner_id, ale_statement) \
508         if (filter_mode & ANIMFILTER_TMP_PEEK) \
509                 return 1; \
510         else { \
511                 bAnimListElem *ale = make_new_animlistelem(channel_data, channel_type, (ID *)owner_id); \
512                 if (ale) { \
513                         BLI_addtail(anim_data, ale); \
514                         items ++; \
515                         ale_statement \
516                 } \
517         } (void)0
518         
519 #define ANIMCHANNEL_NEW_CHANNEL(channel_data, channel_type, owner_id) \
520         ANIMCHANNEL_NEW_CHANNEL_FULL(channel_data, channel_type, owner_id, {})
521         
522 /* ............................... */
523         
524 /* quick macro to test if an anim-channel representing an AnimData block is suitably active */
525 #define ANIMCHANNEL_ACTIVEOK(ale) \
526         (!(filter_mode & ANIMFILTER_ACTIVE) || !(ale->adt) || (ale->adt->flag & ADT_UI_ACTIVE) )
527
528 /* quick macro to test if an anim-channel (F-Curve, Group, etc.) is selected in an acceptable way */
529 #define ANIMCHANNEL_SELOK(test_func) \
530         (!(filter_mode & (ANIMFILTER_SEL | ANIMFILTER_UNSEL)) || \
531          ((filter_mode & ANIMFILTER_SEL) && test_func) || \
532          ((filter_mode & ANIMFILTER_UNSEL) && test_func == 0) )
533                   
534 /* quick macro to test if an anim-channel (F-Curve) is selected ok for editing purposes 
535  *      - _SELEDIT means that only selected curves will have visible+editable keyframes
536  *
537  * checks here work as follows:
538  *      1) seledit off - don't need to consider the implications of this option
539  *      2) foredit off - we're not considering editing, so channel is ok still
540  *      3) test_func (i.e. selection test) - only if selected, this test will pass
541  */
542 #define ANIMCHANNEL_SELEDITOK(test_func) \
543         (!(filter_mode & ANIMFILTER_SELEDIT) || \
544          !(filter_mode & ANIMFILTER_FOREDIT) || \
545          (test_func) )
546
547 /* ----------- 'Private' Stuff --------------- */
548
549 /* this function allocates memory for a new bAnimListElem struct for the 
550  * provided animation channel-data. 
551  */
552 static bAnimListElem *make_new_animlistelem(void *data, short datatype, ID *owner_id)
553 {
554         bAnimListElem *ale = NULL;
555         
556         /* only allocate memory if there is data to convert */
557         if (data) {
558                 /* allocate and set generic data */
559                 ale = MEM_callocN(sizeof(bAnimListElem), "bAnimListElem");
560                 
561                 ale->data = data;
562                 ale->type = datatype;
563                 
564                 ale->id = owner_id;
565                 ale->adt = BKE_animdata_from_id(owner_id);
566                 
567                 /* do specifics */
568                 switch (datatype) {
569                         case ANIMTYPE_SUMMARY:
570                         {
571                                 /* nothing to include for now... this is just a dummy wrappy around all the other channels 
572                                  * in the DopeSheet, and gets included at the start of the list
573                                  */
574                                 ale->key_data = NULL;
575                                 ale->datatype = ALE_ALL;
576                                 break;
577                         }
578                         case ANIMTYPE_SCENE:
579                         {
580                                 Scene *sce = (Scene *)data;
581                                 
582                                 ale->flag = sce->flag;
583                                 
584                                 ale->key_data = sce;
585                                 ale->datatype = ALE_SCE;
586                                 
587                                 ale->adt = BKE_animdata_from_id(data);
588                                 break;
589                         }
590                         case ANIMTYPE_OBJECT:
591                         {
592                                 Base *base = (Base *)data;
593                                 Object *ob = base->object;
594                                 
595                                 ale->flag = ob->flag;
596                                 
597                                 ale->key_data = ob;
598                                 ale->datatype = ALE_OB;
599                                 
600                                 ale->adt = BKE_animdata_from_id(&ob->id);
601                                 break;
602                         }
603                         case ANIMTYPE_FILLACTD:
604                         {
605                                 bAction *act = (bAction *)data;
606                                 
607                                 ale->flag = act->flag;
608                                 
609                                 ale->key_data = act;
610                                 ale->datatype = ALE_ACT;
611                                 break;
612                         }
613                         case ANIMTYPE_FILLDRIVERS:
614                         {
615                                 AnimData *adt = (AnimData *)data;
616                                 
617                                 ale->flag = adt->flag;
618                                 
619                                 // XXX... drivers don't show summary for now
620                                 ale->key_data = NULL;
621                                 ale->datatype = ALE_NONE;
622                                 break;
623                         }
624                         case ANIMTYPE_DSMAT:
625                         {
626                                 Material *ma = (Material *)data;
627                                 AnimData *adt = ma->adt;
628                                 
629                                 ale->flag = FILTER_MAT_OBJD(ma);
630                                 
631                                 ale->key_data = (adt) ? adt->action : NULL;
632                                 ale->datatype = ALE_ACT;
633                                 
634                                 ale->adt = BKE_animdata_from_id(data);
635                                 break;
636                         }
637                         case ANIMTYPE_DSLAM:
638                         {
639                                 Lamp *la = (Lamp *)data;
640                                 AnimData *adt = la->adt;
641                                 
642                                 ale->flag = FILTER_LAM_OBJD(la);
643                                 
644                                 ale->key_data = (adt) ? adt->action : NULL;
645                                 ale->datatype = ALE_ACT;
646                                 
647                                 ale->adt = BKE_animdata_from_id(data);
648                                 break;
649                         }
650                         case ANIMTYPE_DSCAM:
651                         {
652                                 Camera *ca = (Camera *)data;
653                                 AnimData *adt = ca->adt;
654                                 
655                                 ale->flag = FILTER_CAM_OBJD(ca);
656                                 
657                                 ale->key_data = (adt) ? adt->action : NULL;
658                                 ale->datatype = ALE_ACT;
659                                 
660                                 ale->adt = BKE_animdata_from_id(data);
661                                 break;
662                         }
663                         case ANIMTYPE_DSCUR:
664                         {
665                                 Curve *cu = (Curve *)data;
666                                 AnimData *adt = cu->adt;
667                                 
668                                 ale->flag = FILTER_CUR_OBJD(cu);
669                                 
670                                 ale->key_data = (adt) ? adt->action : NULL;
671                                 ale->datatype = ALE_ACT;
672                                 
673                                 ale->adt = BKE_animdata_from_id(data);
674                                 break;
675                         }
676                         case ANIMTYPE_DSARM:
677                         {
678                                 bArmature *arm = (bArmature *)data;
679                                 AnimData *adt = arm->adt;
680                                 
681                                 ale->flag = FILTER_ARM_OBJD(arm);
682                                 
683                                 ale->key_data = (adt) ? adt->action : NULL;
684                                 ale->datatype = ALE_ACT;
685                                 
686                                 ale->adt = BKE_animdata_from_id(data);
687                                 break;
688                         }
689                         case ANIMTYPE_DSMESH:
690                         {
691                                 Mesh *me = (Mesh *)data;
692                                 AnimData *adt = me->adt;
693                                 
694                                 ale->flag = FILTER_MESH_OBJD(me);
695                                 
696                                 ale->key_data = (adt) ? adt->action : NULL;
697                                 ale->datatype = ALE_ACT;
698                                 
699                                 ale->adt = BKE_animdata_from_id(data);
700                                 break;
701                         }
702                         case ANIMTYPE_DSLAT:
703                         {
704                                 Lattice *lt = (Lattice *)data;
705                                 AnimData *adt = lt->adt;
706                                 
707                                 ale->flag = FILTER_LATTICE_OBJD(lt);
708                                 
709                                 ale->key_data = (adt) ? adt->action : NULL;
710                                 ale->datatype = ALE_ACT;
711                                 
712                                 ale->adt = BKE_animdata_from_id(data);
713                                 break;
714                         }
715                         case ANIMTYPE_DSSPK:
716                         {
717                                 Speaker *spk = (Speaker *)data;
718                                 AnimData *adt = spk->adt;
719                                 
720                                 ale->flag = FILTER_SPK_OBJD(spk);
721                                 
722                                 ale->key_data = (adt) ? adt->action : NULL;
723                                 ale->datatype = ALE_ACT;
724                                 
725                                 ale->adt = BKE_animdata_from_id(data);
726                                 break;
727                         }
728                         case ANIMTYPE_DSSKEY:
729                         {
730                                 Key *key = (Key *)data;
731                                 AnimData *adt = key->adt;
732                                 
733                                 ale->flag = FILTER_SKE_OBJD(key);
734                                 
735                                 ale->key_data = (adt) ? adt->action : NULL;
736                                 ale->datatype = ALE_ACT;
737                                 
738                                 ale->adt = BKE_animdata_from_id(data);
739                                 break;
740                         }
741                         case ANIMTYPE_DSWOR:
742                         {
743                                 World *wo = (World *)data;
744                                 AnimData *adt = wo->adt;
745                                 
746                                 ale->flag = FILTER_WOR_SCED(wo);
747                                 
748                                 ale->key_data = (adt) ? adt->action : NULL;
749                                 ale->datatype = ALE_ACT;
750                                 
751                                 ale->adt = BKE_animdata_from_id(data);
752                                 break;
753                         }
754                         case ANIMTYPE_DSNTREE:
755                         {
756                                 bNodeTree *ntree = (bNodeTree *)data;
757                                 AnimData *adt = ntree->adt;
758                                 
759                                 ale->flag = FILTER_NTREE_DATA(ntree);
760                                 
761                                 ale->key_data = (adt) ? adt->action : NULL;
762                                 ale->datatype = ALE_ACT;
763                                 
764                                 ale->adt = BKE_animdata_from_id(data);
765                                 break;
766                         }
767                         case ANIMTYPE_DSLINESTYLE:
768                         {
769                                 FreestyleLineStyle *linestyle = (FreestyleLineStyle *)data;
770                                 AnimData *adt = linestyle->adt;
771                                 
772                                 ale->flag = FILTER_LS_SCED(linestyle); 
773                                 
774                                 ale->key_data = (adt) ? adt->action : NULL;
775                                 ale->datatype = ALE_ACT;
776                                 
777                                 ale->adt = BKE_animdata_from_id(data);
778                                 break;
779                         }
780                         case ANIMTYPE_DSPART:
781                         {
782                                 ParticleSettings *part = (ParticleSettings *)ale->data;
783                                 AnimData *adt = part->adt;
784                                 
785                                 ale->flag = FILTER_PART_OBJD(part);
786                                 
787                                 ale->key_data = (adt) ? adt->action : NULL;
788                                 ale->datatype = ALE_ACT;
789                                 
790                                 ale->adt = BKE_animdata_from_id(data);
791                                 break;
792                         }
793                         case ANIMTYPE_DSTEX:
794                         {
795                                 Tex *tex = (Tex *)data;
796                                 AnimData *adt = tex->adt;
797                                 
798                                 ale->flag = FILTER_TEX_DATA(tex);
799                                 
800                                 ale->key_data = (adt) ? adt->action : NULL;
801                                 ale->datatype = ALE_ACT;
802                                 
803                                 ale->adt = BKE_animdata_from_id(data);
804                                 break;
805                         }
806                         case ANIMTYPE_DSGPENCIL:
807                         {
808                                 bGPdata *gpd = (bGPdata *)data;
809                                 AnimData *adt = gpd->adt;
810                                 
811                                 /* NOTE: we just reuse the same expand filter for this case */
812                                 ale->flag = EXPANDED_GPD(gpd);
813                                 
814                                 // XXX: currently, this is only used for access to its animation data
815                                 ale->key_data = (adt) ? adt->action : NULL;
816                                 ale->datatype = ALE_ACT;
817                                 
818                                 ale->adt = BKE_animdata_from_id(data);
819                                 break;
820                         }
821                         case ANIMTYPE_NLACONTROLS:
822                         {
823                                 AnimData *adt = (AnimData *)data;
824                                 
825                                 ale->flag = adt->flag;
826                                 
827                                 ale->key_data = NULL;
828                                 ale->datatype = ALE_NONE;
829                                 break;
830                         }
831                         case ANIMTYPE_GROUP:
832                         {
833                                 bActionGroup *agrp = (bActionGroup *)data;
834                                 
835                                 ale->flag = agrp->flag;
836                                 
837                                 ale->key_data = NULL;
838                                 ale->datatype = ALE_GROUP;
839                                 break;
840                         }
841                         case ANIMTYPE_FCURVE:
842                         {
843                                 FCurve *fcu = (FCurve *)data;
844                                 
845                                 ale->flag = fcu->flag;
846                                 
847                                 ale->key_data = fcu;
848                                 ale->datatype = ALE_FCURVE;
849                                 break;
850                         }
851                         case ANIMTYPE_SHAPEKEY:
852                         {
853                                 KeyBlock *kb = (KeyBlock *)data;
854                                 Key *key = (Key *)ale->id;
855                                 
856                                 ale->flag = kb->flag;
857                                 
858                                 /* whether we have keyframes depends on whether there is a Key block to find it from */
859                                 if (key) {
860                                         /* index of shapekey is defined by place in key's list */
861                                         ale->index = BLI_findindex(&key->block, kb);
862                                         
863                                         /* the corresponding keyframes are from the animdata */
864                                         if (ale->adt && ale->adt->action) {
865                                                 bAction *act = ale->adt->action;
866                                                 char *rna_path = BKE_keyblock_curval_rnapath_get(key, kb);
867                                                 
868                                                 /* try to find the F-Curve which corresponds to this exactly,
869                                                  * then free the MEM_alloc'd string
870                                                  */
871                                                 if (rna_path) {
872                                                         ale->key_data = (void *)list_find_fcurve(&act->curves, rna_path, 0);
873                                                         MEM_freeN(rna_path);
874                                                 }
875                                         }
876                                         ale->datatype = (ale->key_data) ? ALE_FCURVE : ALE_NONE;
877                                 }
878                                 break;
879                         }
880                         case ANIMTYPE_GPLAYER:
881                         {
882                                 bGPDlayer *gpl = (bGPDlayer *)data;
883                                 
884                                 ale->flag = gpl->flag;
885                                 
886                                 ale->key_data = NULL;
887                                 ale->datatype = ALE_GPFRAME;
888                                 break;
889                         }
890                         case ANIMTYPE_MASKLAYER:
891                         {
892                                 MaskLayer *masklay = (MaskLayer *)data;
893                                 
894                                 ale->flag = masklay->flag;
895                                 
896                                 ale->key_data = NULL;
897                                 ale->datatype = ALE_MASKLAY;
898                                 break;
899                         }
900                         case ANIMTYPE_NLATRACK:
901                         {
902                                 NlaTrack *nlt = (NlaTrack *)data;
903                                 
904                                 ale->flag = nlt->flag;
905                                 
906                                 ale->key_data = &nlt->strips;
907                                 ale->datatype = ALE_NLASTRIP;
908                                 break;
909                         }
910                         case ANIMTYPE_NLAACTION:
911                         {
912                                 /* nothing to include for now... nothing editable from NLA-perspective here */
913                                 ale->key_data = NULL;
914                                 ale->datatype = ALE_NONE;
915                                 break;
916                         }
917                 }
918         }
919         
920         /* return created datatype */
921         return ale;
922 }
923  
924 /* ----------------------------------------- */
925
926 /* 'Only Selected' selected data and/or 'Include Hidden' filtering
927  * NOTE: when this function returns true, the F-Curve is to be skipped 
928  */
929 static bool skip_fcurve_selected_data(bDopeSheet *ads, FCurve *fcu, ID *owner_id, int filter_mode)
930 {
931         /* hidden items should be skipped if we only care about visible data, but we aren't interested in hidden stuff */
932         const bool skip_hidden = (filter_mode & ANIMFILTER_DATA_VISIBLE) && !(ads->filterflag & ADS_FILTER_INCL_HIDDEN);
933         
934         if (GS(owner_id->name) == ID_OB) {
935                 Object *ob = (Object *)owner_id;
936                 
937                 /* only consider if F-Curve involves pose.bones */
938                 if ((fcu->rna_path) && strstr(fcu->rna_path, "pose.bones")) {
939                         bPoseChannel *pchan;
940                         char *bone_name;
941                         
942                         /* get bone-name, and check if this bone is selected */
943                         bone_name = BLI_str_quoted_substrN(fcu->rna_path, "pose.bones[");
944                         pchan = BKE_pose_channel_find_name(ob->pose, bone_name);
945                         if (bone_name) MEM_freeN(bone_name);
946                         
947                         /* check whether to continue or skip */
948                         if ((pchan) && (pchan->bone)) {
949                                 /* if only visible channels, skip if bone not visible unless user wants channels from hidden data too */
950                                 if (skip_hidden) {
951                                         bArmature *arm = (bArmature *)ob->data;
952                                         
953                                         /* skipping - not visible on currently visible layers */
954                                         if ((arm->layer & pchan->bone->layer) == 0)
955                                                 return true;
956                                         /* skipping - is currently hidden */
957                                         if (pchan->bone->flag & BONE_HIDDEN_P)
958                                                 return true;
959                                 }
960                                 
961                                 /* can only add this F-Curve if it is selected */
962                                 if (ads->filterflag & ADS_FILTER_ONLYSEL) {
963                                         if ((pchan->bone->flag & BONE_SELECTED) == 0)
964                                                 return true;
965                                 }
966                         }
967                 }
968         }
969         else if (GS(owner_id->name) == ID_SCE) {
970                 Scene *scene = (Scene *)owner_id;
971                 
972                 /* only consider if F-Curve involves sequence_editor.sequences */
973                 if ((fcu->rna_path) && strstr(fcu->rna_path, "sequences_all")) {
974                         Editing *ed = BKE_sequencer_editing_get(scene, false);
975                         Sequence *seq = NULL;
976                         char *seq_name;
977                         
978                         if (ed) {
979                                 /* get strip name, and check if this strip is selected */
980                                 seq_name = BLI_str_quoted_substrN(fcu->rna_path, "sequences_all[");
981                                 seq = BKE_sequence_get_by_name(ed->seqbasep, seq_name, false);
982                                 if (seq_name) MEM_freeN(seq_name);
983                         }
984                         
985                         /* can only add this F-Curve if it is selected */
986                         if (ads->filterflag & ADS_FILTER_ONLYSEL) {
987                                 if ((seq == NULL) || (seq->flag & SELECT) == 0)
988                                         return true;
989                         }
990                 }
991         }
992         else if (GS(owner_id->name) == ID_NT) {
993                 bNodeTree *ntree = (bNodeTree *)owner_id;
994                 
995                 /* check for selected nodes */
996                 if ((fcu->rna_path) && strstr(fcu->rna_path, "nodes")) {
997                         bNode *node;
998                         char *node_name;
999                         
1000                         /* get strip name, and check if this strip is selected */
1001                         node_name = BLI_str_quoted_substrN(fcu->rna_path, "nodes[");
1002                         node = nodeFindNodebyName(ntree, node_name);
1003                         if (node_name) MEM_freeN(node_name);
1004                         
1005                         /* can only add this F-Curve if it is selected */
1006                         if (ads->filterflag & ADS_FILTER_ONLYSEL) {
1007                                 if ((node) && (node->flag & NODE_SELECT) == 0)
1008                                         return true;
1009                         }
1010                 }
1011         }
1012         
1013         return false;
1014 }
1015
1016 /* Helper for name-based filtering - Perform "partial/fuzzy matches" (as in 80a7efd) */
1017 static bool name_matches_dopesheet_filter(bDopeSheet *ads, char *name)
1018 {
1019         if (ads->flag & ADS_FLAG_FUZZY_NAMES) {
1020                 /* full fuzzy, multi-word, case insensitive matches */
1021                 const size_t str_len = strlen(ads->searchstr);
1022                 const int words_max = (str_len / 2) + 1;
1023                 
1024                 int (*words)[2] = BLI_array_alloca(words, words_max);
1025                 const int words_len = BLI_string_find_split_words(ads->searchstr, str_len, ' ', words, words_max);
1026                 bool found = false;
1027                 
1028                 /* match name against all search words */
1029                 for (int index = 0; index < words_len; index++) {
1030                         if (BLI_strncasestr(name, ads->searchstr + words[index][0], words[index][1])) {
1031                                 found = true;
1032                                 break;
1033                         }
1034                 }
1035                 
1036                 /* if we have a match somewhere, this returns true */
1037                 return found;
1038         }
1039         else {
1040                 /* fallback/default - just case insensitive, but starts from start of word */
1041                 return BLI_strcasestr(name, ads->searchstr) != NULL;
1042         }
1043 }
1044
1045 /* (Display-)Name-based F-Curve filtering
1046  * NOTE: when this function returns true, the F-Curve is to be skipped 
1047  */
1048 static bool skip_fcurve_with_name(bDopeSheet *ads, FCurve *fcu, ID *owner_id)
1049 {
1050         bAnimListElem ale_dummy = {NULL};
1051         const bAnimChannelType *acf;
1052         
1053         /* create a dummy wrapper for the F-Curve */
1054         ale_dummy.type = ANIMTYPE_FCURVE;
1055         ale_dummy.id = owner_id;
1056         ale_dummy.data = fcu;
1057         
1058         /* get type info for channel */
1059         acf = ANIM_channel_get_typeinfo(&ale_dummy);
1060         if (acf && acf->name) {
1061                 char name[256]; /* hopefully this will be enough! */
1062                 
1063                 /* get name */
1064                 acf->name(&ale_dummy, name);
1065                 
1066                 /* check for partial match with the match string, assuming case insensitive filtering 
1067                  * if match, this channel shouldn't be ignored!
1068                  */
1069                 return !name_matches_dopesheet_filter(ads, name);
1070         }
1071         
1072         /* just let this go... */
1073         return true;
1074 }
1075
1076 /**
1077  * Check if F-Curve has errors and/or is disabled
1078  *
1079  * \return true if F-Curve has errors/is disabled
1080  */
1081 static bool fcurve_has_errors(FCurve *fcu)
1082 {
1083         /* F-Curve disabled - path eval error */
1084         if (fcu->flag & FCURVE_DISABLED) {
1085                 return true;
1086         }
1087         
1088         /* driver? */
1089         if (fcu->driver) {
1090                 ChannelDriver *driver = fcu->driver;
1091                 DriverVar *dvar;
1092                 
1093                 /* error flag on driver usually means that there is an error
1094                  * BUT this may not hold with PyDrivers as this flag gets cleared
1095                  *     if no critical errors prevent the driver from working...
1096                  */
1097                 if (driver->flag & DRIVER_FLAG_INVALID)
1098                         return true;
1099                         
1100                 /* check variables for other things that need linting... */
1101                 // TODO: maybe it would be more efficient just to have a quick flag for this?
1102                 for (dvar = driver->variables.first; dvar; dvar = dvar->next) {
1103                         DRIVER_TARGETS_USED_LOOPER(dvar)
1104                         {
1105                                 if (dtar->flag & DTAR_FLAG_INVALID)
1106                                         return true;
1107                         }
1108                         DRIVER_TARGETS_LOOPER_END
1109                 }
1110         }
1111         
1112         /* no errors found */
1113         return false;
1114 }
1115
1116 /* find the next F-Curve that is usable for inclusion */
1117 static FCurve *animfilter_fcurve_next(bDopeSheet *ads, FCurve *first, bActionGroup *grp, int filter_mode, ID *owner_id)
1118 {
1119         FCurve *fcu = NULL;
1120         
1121         /* loop over F-Curves - assume that the caller of this has already checked that these should be included 
1122          * NOTE: we need to check if the F-Curves belong to the same group, as this gets called for groups too...
1123          */
1124         for (fcu = first; ((fcu) && (fcu->grp == grp)); fcu = fcu->next) {
1125                 /* special exception for Pose-Channel/Sequence-Strip/Node Based F-Curves:
1126                  *      - the 'Only Selected' and 'Include Hidden' data filters should be applied to sub-ID data which
1127                  *        can be independently selected/hidden, such as Pose-Channels, Sequence Strips, and Nodes.
1128                  *        Since these checks were traditionally done as first check for objects, we do the same here
1129                  *      - we currently use an 'approximate' method for getting these F-Curves that doesn't require
1130                  *        carefully checking the entire path
1131                  *      - this will also affect things like Drivers, and also works for Bone Constraints
1132                  */
1133                 if (ads && owner_id) {
1134                         if ((filter_mode & ANIMFILTER_TMP_IGNORE_ONLYSEL) == 0) {
1135                                 if ((ads->filterflag & ADS_FILTER_ONLYSEL) || (ads->filterflag & ADS_FILTER_INCL_HIDDEN) == 0) {
1136                                         if (skip_fcurve_selected_data(ads, fcu, owner_id, filter_mode))
1137                                                 continue;
1138                                 }
1139                         }
1140                 }
1141                 
1142                 /* only include if visible (Graph Editor check, not channels check) */
1143                 if (!(filter_mode & ANIMFILTER_CURVE_VISIBLE) || (fcu->flag & FCURVE_VISIBLE)) {
1144                         /* only work with this channel and its subchannels if it is editable */
1145                         if (!(filter_mode & ANIMFILTER_FOREDIT) || EDITABLE_FCU(fcu)) {
1146                                 /* only include this curve if selected in a way consistent with the filtering requirements */
1147                                 if (ANIMCHANNEL_SELOK(SEL_FCU(fcu)) && ANIMCHANNEL_SELEDITOK(SEL_FCU(fcu))) {
1148                                         /* only include if this curve is active */
1149                                         if (!(filter_mode & ANIMFILTER_ACTIVE) || (fcu->flag & FCURVE_ACTIVE)) {
1150                                                 /* name based filtering... */
1151                                                 if ( ((ads) && (ads->filterflag & ADS_FILTER_BY_FCU_NAME)) && (owner_id) ) {
1152                                                         if (skip_fcurve_with_name(ads, fcu, owner_id))
1153                                                                 continue;
1154                                                 }
1155                                                 
1156                                                 /* error-based filtering... */
1157                                                 if ((ads) && (ads->filterflag & ADS_FILTER_ONLY_ERRORS)) {
1158                                                         /* skip if no errors... */
1159                                                         if (fcurve_has_errors(fcu) == false)
1160                                                                 continue;
1161                                                 }
1162                                                 
1163                                                 /* this F-Curve can be used, so return it */
1164                                                 return fcu;
1165                                         }
1166                                 }
1167                         }
1168                 }
1169         }
1170         
1171         /* no (more) F-Curves from the list are suitable... */
1172         return NULL;
1173 }
1174
1175 static size_t animfilter_fcurves(ListBase *anim_data, bDopeSheet *ads, FCurve *first, bActionGroup *grp, int filter_mode, ID *owner_id)
1176 {
1177         FCurve *fcu;
1178         size_t items = 0;
1179         
1180         /* loop over every F-Curve able to be included 
1181          *      - this for-loop works like this: 
1182          *              1) the starting F-Curve is assigned to the fcu pointer so that we have a starting point to search from
1183          *              2) the first valid F-Curve to start from (which may include the one given as 'first') in the remaining 
1184          *                 list of F-Curves is found, and verified to be non-null
1185          *              3) the F-Curve referenced by fcu pointer is added to the list
1186          *              4) the fcu pointer is set to the F-Curve after the one we just added, so that we can keep going through 
1187          *                 the rest of the F-Curve list without an eternal loop. Back to step 2 :)
1188          */
1189         for (fcu = first; ( (fcu = animfilter_fcurve_next(ads, fcu, grp, filter_mode, owner_id)) ); fcu = fcu->next) {
1190                 ANIMCHANNEL_NEW_CHANNEL(fcu, ANIMTYPE_FCURVE, owner_id);
1191         }
1192         
1193         /* return the number of items added to the list */
1194         return items;
1195 }
1196
1197 static size_t animfilter_act_group(bAnimContext *ac, ListBase *anim_data, bDopeSheet *ads, bAction *UNUSED(act), bActionGroup *agrp, int filter_mode, ID *owner_id)
1198 {
1199         ListBase tmp_data = {NULL, NULL};
1200         size_t tmp_items = 0;
1201         size_t items = 0;
1202         //int ofilter = filter_mode;
1203         
1204         /* if we care about the selection status of the channels, 
1205          * but the group isn't expanded (1)...
1206          *  (1) this only matters if we actually care about the hierarchy though.
1207          *              - Hierarchy matters: this hack should be applied
1208          *              - Hierarchy ignored: cases like [#21276] won't work properly, unless we skip this hack
1209          */
1210         if ( ((filter_mode & ANIMFILTER_LIST_VISIBLE) && EXPANDED_AGRP(ac, agrp) == 0) &&     /* care about hierarchy but group isn't expanded */
1211              (filter_mode & (ANIMFILTER_SEL | ANIMFILTER_UNSEL)) )                          /* care about selection status */
1212         {
1213                 /* if the group itself isn't selected appropriately, we shouldn't consider it's children either */
1214                 if (ANIMCHANNEL_SELOK(SEL_AGRP(agrp)) == 0)
1215                         return 0;
1216                 
1217                 /* if we're still here, then the selection status of the curves within this group should not matter,
1218                  * since this creates too much overhead for animators (i.e. making a slow workflow)
1219                  *
1220                  * Tools affected by this at time of coding (2010 Feb 09):
1221                  *      - inserting keyframes on selected channels only
1222                  *      - pasting keyframes
1223                  *      - creating ghost curves in Graph Editor
1224                  */
1225                 filter_mode &= ~(ANIMFILTER_SEL | ANIMFILTER_UNSEL | ANIMFILTER_LIST_VISIBLE);
1226         }
1227         
1228         /* add grouped F-Curves */
1229         BEGIN_ANIMFILTER_SUBCHANNELS(EXPANDED_AGRP(ac, agrp))
1230         {
1231                 /* special filter so that we can get just the F-Curves within the active group */
1232                 if (!(filter_mode & ANIMFILTER_ACTGROUPED) || (agrp->flag & AGRP_ACTIVE)) {
1233                         /* for the Graph Editor, curves may be set to not be visible in the view to lessen clutter,
1234                          * but to do this, we need to check that the group doesn't have it's not-visible flag set preventing 
1235                          * all its sub-curves to be shown
1236                          */
1237                         if (!(filter_mode & ANIMFILTER_CURVE_VISIBLE) || !(agrp->flag & AGRP_NOTVISIBLE)) {
1238                                 /* group must be editable for its children to be editable (if we care about this) */
1239                                 if (!(filter_mode & ANIMFILTER_FOREDIT) || EDITABLE_AGRP(agrp)) {
1240                                         /* get first F-Curve which can be used here */
1241                                         FCurve *first_fcu = animfilter_fcurve_next(ads, agrp->channels.first, agrp, filter_mode, owner_id);
1242                                         
1243                                         /* filter list, starting from this F-Curve */
1244                                         tmp_items += animfilter_fcurves(&tmp_data, ads, first_fcu, agrp, filter_mode, owner_id);
1245                                 }
1246                         }
1247                 }
1248         }
1249         END_ANIMFILTER_SUBCHANNELS;
1250         
1251         /* did we find anything? */
1252         if (tmp_items) {
1253                 /* add this group as a channel first */
1254                 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
1255                         /* restore original filter mode so that this next step works ok... */
1256                         //filter_mode = ofilter;
1257                         
1258                         /* filter selection of channel specially here again, since may be open and not subject to previous test */
1259                         if (ANIMCHANNEL_SELOK(SEL_AGRP(agrp)) ) {
1260                                 ANIMCHANNEL_NEW_CHANNEL(agrp, ANIMTYPE_GROUP, owner_id);
1261                         }
1262                 }
1263                 
1264                 /* now add the list of collected channels */
1265                 BLI_movelisttolist(anim_data, &tmp_data);
1266                 BLI_assert(BLI_listbase_is_empty(&tmp_data));
1267                 items += tmp_items;
1268         }
1269         
1270         /* return the number of items added to the list */
1271         return items;
1272 }
1273
1274 static size_t animfilter_action(bAnimContext *ac, ListBase *anim_data, bDopeSheet *ads, bAction *act, int filter_mode, ID *owner_id)
1275 {
1276         bActionGroup *agrp;
1277         FCurve *lastchan = NULL;
1278         size_t items = 0;
1279         
1280         /* don't include anything from this action if it is linked in from another file,
1281          * and we're getting stuff for editing...
1282          */
1283         if ((filter_mode & ANIMFILTER_FOREDIT) && ID_IS_LINKED_DATABLOCK(act))
1284                 return 0;
1285                 
1286         /* do groups */
1287         // TODO: do nested groups?
1288         for (agrp = act->groups.first; agrp; agrp = agrp->next) {
1289                 /* store reference to last channel of group */
1290                 if (agrp->channels.last) 
1291                         lastchan = agrp->channels.last;
1292                         
1293                 /* action group's channels */
1294                 items += animfilter_act_group(ac, anim_data, ads, act, agrp, filter_mode, owner_id);
1295         }
1296         
1297         /* un-grouped F-Curves (only if we're not only considering those channels in the active group) */
1298         if (!(filter_mode & ANIMFILTER_ACTGROUPED)) {
1299                 FCurve *firstfcu = (lastchan) ? (lastchan->next) : (act->curves.first);
1300                 items += animfilter_fcurves(anim_data, ads, firstfcu, NULL, filter_mode, owner_id);
1301         }
1302         
1303         /* return the number of items added to the list */
1304         return items;
1305 }
1306
1307 /* Include NLA-Data for NLA-Editor:
1308  *      - when ANIMFILTER_LIST_CHANNELS is used, that means we should be filtering the list for display
1309  *        Although the evaluation order is from the first track to the last and then apply the Action on top,
1310  *        we present this in the UI as the Active Action followed by the last track to the first so that we 
1311  *        get the evaluation order presented as per a stack.
1312  *      - for normal filtering (i.e. for editing), we only need the NLA-tracks but they can be in 'normal' evaluation
1313  *        order, i.e. first to last. Otherwise, some tools may get screwed up.
1314  */
1315 static size_t animfilter_nla(bAnimContext *UNUSED(ac), ListBase *anim_data, bDopeSheet *ads, AnimData *adt, int filter_mode, ID *owner_id)
1316 {
1317         NlaTrack *nlt;
1318         NlaTrack *first = NULL, *next = NULL;
1319         size_t items = 0;
1320         
1321         /* if showing channels, include active action */
1322         if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
1323                 /* if NLA action-line filtering is off, don't show unless there are keyframes, 
1324                  * in order to keep things more compact for doing transforms
1325                  */
1326                 if (!(ads->filterflag & ADS_FILTER_NLA_NOACT) || (adt->action)) {
1327                         /* there isn't really anything editable here, so skip if need editable */
1328                         if ((filter_mode & ANIMFILTER_FOREDIT) == 0) {
1329                                 /* just add the action track now (this MUST appear for drawing)
1330                                  *      - as AnimData may not have an action, we pass a dummy pointer just to get the list elem created, then
1331                                  *        overwrite this with the real value - REVIEW THIS...
1332                                  */
1333                                 ANIMCHANNEL_NEW_CHANNEL_FULL((void *)(&adt->action), ANIMTYPE_NLAACTION, owner_id, 
1334                                         {
1335                                                 ale->data = adt->action ? adt->action : NULL; 
1336                                         });
1337                         }
1338                 }
1339                 
1340                 /* first track to include will be the last one if we're filtering by channels */
1341                 first = adt->nla_tracks.last;
1342         }
1343         else {
1344                 /* first track to include will the first one (as per normal) */
1345                 first = adt->nla_tracks.first;
1346         }
1347         
1348         /* loop over NLA Tracks - assume that the caller of this has already checked that these should be included */
1349         for (nlt = first; nlt; nlt = next) {
1350                 /* 'next' NLA-Track to use depends on whether we're filtering for drawing or not */
1351                 if (filter_mode & ANIMFILTER_LIST_CHANNELS) 
1352                         next = nlt->prev;
1353                 else
1354                         next = nlt->next;
1355                 
1356                 /* if we're in NLA-tweakmode, don't show this track if it was disabled (due to tweaking) for now 
1357                  *      - active track should still get shown though (even though it has disabled flag set)
1358                  */
1359                 // FIXME: the channels after should still get drawn, just 'differently', and after an active-action channel
1360                 if ((adt->flag & ADT_NLA_EDIT_ON) && (nlt->flag & NLATRACK_DISABLED) && (adt->act_track != nlt))
1361                         continue;
1362                 
1363                 /* only work with this channel and its subchannels if it is editable */
1364                 if (!(filter_mode & ANIMFILTER_FOREDIT) || EDITABLE_NLT(nlt)) {
1365                         /* only include this track if selected in a way consistent with the filtering requirements */
1366                         if (ANIMCHANNEL_SELOK(SEL_NLT(nlt))) {
1367                                 /* only include if this track is active */
1368                                 if (!(filter_mode & ANIMFILTER_ACTIVE) || (nlt->flag & NLATRACK_ACTIVE)) {
1369                                         /* name based filtering... */
1370                                         if (((ads) && (ads->filterflag & ADS_FILTER_BY_FCU_NAME)) && (owner_id)) {
1371                                                 bool track_ok = false, strip_ok = false;
1372                                                 
1373                                                 /* check if the name of the track, or the strips it has are ok... */
1374                                                 track_ok = name_matches_dopesheet_filter(ads, nlt->name);
1375                                                 
1376                                                 if (track_ok == false) {
1377                                                         NlaStrip *strip;
1378                                                         for (strip = nlt->strips.first; strip; strip = strip->next) {
1379                                                                 if (name_matches_dopesheet_filter(ads, strip->name)) {
1380                                                                         strip_ok = true;
1381                                                                         break;
1382                                                                 }
1383                                                         }
1384                                                 }
1385                                                 
1386                                                 /* skip if both fail this test... */
1387                                                 if (!track_ok && !strip_ok) {
1388                                                         continue;
1389                                                 }
1390                                         }
1391                                         
1392                                         /* add the track now that it has passed all our tests */
1393                                         ANIMCHANNEL_NEW_CHANNEL(nlt, ANIMTYPE_NLATRACK, owner_id);
1394                                 }
1395                         }
1396                 }
1397         }
1398         
1399         /* return the number of items added to the list */
1400         return items;
1401 }
1402
1403 /* Include the control FCurves per NLA Strip in the channel list
1404  * NOTE: This is includes the expander too...
1405  */
1406 static size_t animfilter_nla_controls(ListBase *anim_data, bDopeSheet *ads, AnimData *adt, int filter_mode, ID *owner_id)
1407 {
1408         ListBase tmp_data = {NULL, NULL};
1409         size_t tmp_items = 0;
1410         size_t items = 0;
1411         
1412         /* add control curves from each NLA strip... */
1413         /* NOTE: ANIMTYPE_FCURVES are created here, to avoid duplicating the code needed */
1414         BEGIN_ANIMFILTER_SUBCHANNELS(((adt->flag & ADT_NLA_SKEYS_COLLAPSED) == 0))
1415         {
1416                 NlaTrack *nlt;
1417                 NlaStrip *strip;
1418                 
1419                 /* for now, we only go one level deep - so controls on grouped FCurves are not handled */
1420                 for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next) {
1421                         for (strip = nlt->strips.first; strip; strip = strip->next) {
1422                                 ListBase strip_curves = {NULL, NULL};
1423                                 size_t strip_items = 0;
1424                                 
1425                                 /* create the raw items */
1426                                 strip_items += animfilter_fcurves(&strip_curves, ads, strip->fcurves.first, NULL, filter_mode, owner_id);
1427                                 
1428                                 /* change their types and add extra data
1429                                  * - There is no point making a separate copy of animfilter_fcurves for this now/yet,
1430                                  *   unless we later get per-element control curves for other stuff too
1431                                  */
1432                                 if (strip_items) {
1433                                         bAnimListElem *ale, *ale_n = NULL;
1434                                         
1435                                         for (ale = strip_curves.first; ale; ale = ale_n) {
1436                                                 ale_n = ale->next;
1437                                                 
1438                                                 /* change the type to being a FCurve for editing NLA strip controls */
1439                                                 BLI_assert(ale->type == ANIMTYPE_FCURVE);
1440                                                 
1441                                                 ale->type = ANIMTYPE_NLACURVE;
1442                                                 ale->owner = strip;
1443                                                 
1444                                                 ale->adt  = NULL; /* XXX: This way, there are no problems with time mapping errors */
1445                                         }
1446                                 }
1447                                 
1448                                 /* add strip curves to the set of channels inside the group being collected */
1449                                 BLI_movelisttolist(&tmp_data, &strip_curves);
1450                                 BLI_assert(BLI_listbase_is_empty(&strip_curves));
1451                                 tmp_items += strip_items;
1452                         }
1453                 }
1454         }
1455         END_ANIMFILTER_SUBCHANNELS;
1456         
1457         /* did we find anything? */
1458         if (tmp_items) {
1459                 /* add the expander as a channel first */
1460                 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
1461                         /* currently these channels cannot be selected, so they should be skipped */
1462                         if ((filter_mode & (ANIMFILTER_SEL | ANIMFILTER_UNSEL)) == 0) {
1463                                 ANIMCHANNEL_NEW_CHANNEL(adt, ANIMTYPE_NLACONTROLS, owner_id);
1464                         }
1465                 }
1466                 
1467                 /* now add the list of collected channels */
1468                 BLI_movelisttolist(anim_data, &tmp_data);
1469                 BLI_assert(BLI_listbase_is_empty(&tmp_data));
1470                 items += tmp_items;
1471         }
1472         
1473         /* return the numebr of items added to the list */
1474         return items;
1475 }
1476
1477 /* determine what animation data from AnimData block should get displayed */
1478 static size_t animfilter_block_data(bAnimContext *ac, ListBase *anim_data, bDopeSheet *ads, ID *id, int filter_mode)
1479 {
1480         AnimData *adt = BKE_animdata_from_id(id);
1481         size_t items = 0;
1482         
1483         /* image object datablocks have no anim-data so check for NULL */
1484         if (adt) {
1485                 IdAdtTemplate *iat = (IdAdtTemplate *)id;
1486                 
1487                 /* NOTE: this macro is used instead of inlining the logic here, since this sort of filtering is still needed
1488                  * in a few places in the rest of the code still - notably for the few cases where special mode-based
1489                  * different types of data expanders are required.
1490                  */
1491                 ANIMDATA_FILTER_CASES(iat,
1492                         { /* AnimData */
1493                                 /* specifically filter animdata block */
1494                                 if (ANIMCHANNEL_SELOK(SEL_ANIMDATA(adt)) ) {
1495                                         ANIMCHANNEL_NEW_CHANNEL(adt, ANIMTYPE_ANIMDATA, id);
1496                                 }
1497                         },
1498                         { /* NLA */
1499                                 items += animfilter_nla(ac, anim_data, ads, adt, filter_mode, id);
1500                         },
1501                         { /* Drivers */
1502                                 items += animfilter_fcurves(anim_data, ads, adt->drivers.first, NULL, filter_mode, id);
1503                         },
1504                         { /* NLA Control Keyframes */
1505                                 items += animfilter_nla_controls(anim_data, ads, adt, filter_mode, id);
1506                         },
1507                         { /* Keyframes */
1508                                 items += animfilter_action(ac, anim_data, ads, adt->action, filter_mode, id);
1509                         }
1510                 );
1511         }
1512         
1513         return items;
1514 }
1515
1516
1517
1518 /* Include ShapeKey Data for ShapeKey Editor */
1519 static size_t animdata_filter_shapekey(bAnimContext *ac, ListBase *anim_data, Key *key, int filter_mode)
1520 {
1521         size_t items = 0;
1522         
1523         /* check if channels or only F-Curves */
1524         if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
1525                 KeyBlock *kb;
1526                 
1527                 /* loop through the channels adding ShapeKeys as appropriate */
1528                 for (kb = key->block.first; kb; kb = kb->next) {
1529                         /* skip the first one, since that's the non-animatable basis */
1530                         if (kb == key->block.first) continue;
1531                         
1532                         /* only work with this channel and its subchannels if it is editable */
1533                         if (!(filter_mode & ANIMFILTER_FOREDIT) || EDITABLE_SHAPEKEY(kb)) {
1534                                 /* only include this track if selected in a way consistent with the filtering requirements */
1535                                 if (ANIMCHANNEL_SELOK(SEL_SHAPEKEY(kb)) ) {
1536                                         // TODO: consider 'active' too?
1537                                         
1538                                         /* owner-id here must be key so that the F-Curve can be resolved... */
1539                                         ANIMCHANNEL_NEW_CHANNEL(kb, ANIMTYPE_SHAPEKEY, key);
1540                                 }
1541                         }
1542                 }
1543         }
1544         else {
1545                 /* just use the action associated with the shapekey */
1546                 // TODO: somehow manage to pass dopesheet info down here too?
1547                 if (key->adt) {
1548                         if (filter_mode & ANIMFILTER_ANIMDATA) {
1549                                 if (ANIMCHANNEL_SELOK(SEL_ANIMDATA(key->adt)) ) {
1550                                         ANIMCHANNEL_NEW_CHANNEL(key->adt, ANIMTYPE_ANIMDATA, key);
1551                                 }
1552                         }
1553                         else if (key->adt->action) {
1554                                 items = animfilter_action(ac, anim_data, NULL, key->adt->action, filter_mode, (ID *)key);
1555                         }
1556                 }
1557         }
1558         
1559         /* return the number of items added to the list */
1560         return items;
1561 }
1562
1563 /* Helper for Grease Pencil - layers within a datablock */
1564 static size_t animdata_filter_gpencil_layers_data(ListBase *anim_data, bDopeSheet *ads, bGPdata *gpd, int filter_mode)
1565 {
1566         bGPDlayer *gpl;
1567         size_t items = 0;
1568         
1569         /* loop over layers as the conditions are acceptable */
1570         for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
1571                 /* only if selected */
1572                 if (ANIMCHANNEL_SELOK(SEL_GPL(gpl)) ) {
1573                         /* only if editable */
1574                         if (!(filter_mode & ANIMFILTER_FOREDIT) || EDITABLE_GPL(gpl)) {
1575                                 /* active... */
1576                                 if (!(filter_mode & ANIMFILTER_ACTIVE) || (gpl->flag & GP_LAYER_ACTIVE)) {
1577                                         /* skip layer if the name doesn't match the filter string */
1578                                         if ((ads) && (ads->filterflag & ADS_FILTER_BY_FCU_NAME)) {
1579                                                 if (name_matches_dopesheet_filter(ads, gpl->info) == false)
1580                                                         continue;
1581                                         }
1582                                         
1583                                         
1584                                         /* add to list */
1585                                         ANIMCHANNEL_NEW_CHANNEL(gpl, ANIMTYPE_GPLAYER, gpd);
1586                                 }
1587                         }
1588                 }
1589         }
1590         
1591         return items;
1592 }
1593
1594 /* Helper for Grease Pencil - Grease Pencil datablock - GP Frames */
1595 static size_t animdata_filter_gpencil_data(ListBase *anim_data, bDopeSheet *ads, bGPdata *gpd, int filter_mode)
1596 {
1597         size_t items = 0;
1598         
1599         /* When asked from "AnimData" blocks (i.e. the top-level containers for normal animation),
1600          * for convenience, this will return GP Datablocks instead. This may cause issues down
1601          * the track, but for now, this will do...
1602          */
1603         if (filter_mode & ANIMFILTER_ANIMDATA) {
1604                 /* just add GPD as a channel - this will add everything needed */
1605                 ANIMCHANNEL_NEW_CHANNEL(gpd, ANIMTYPE_GPDATABLOCK, NULL);
1606         }
1607         else {
1608                 ListBase tmp_data = {NULL, NULL};
1609                 size_t tmp_items = 0;
1610                 
1611                 /* add gpencil animation channels */
1612                 BEGIN_ANIMFILTER_SUBCHANNELS(EXPANDED_GPD(gpd))
1613                 {
1614                         tmp_items += animdata_filter_gpencil_layers_data(&tmp_data, ads, gpd, filter_mode);
1615                 }
1616                 END_ANIMFILTER_SUBCHANNELS;
1617                 
1618                 /* did we find anything? */
1619                 if (tmp_items) {
1620                         /* include data-expand widget first */
1621                         if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
1622                                 /* add gpd as channel too (if for drawing, and it has layers) */
1623                                 ANIMCHANNEL_NEW_CHANNEL(gpd, ANIMTYPE_GPDATABLOCK, NULL);
1624                         }
1625                         
1626                         /* now add the list of collected channels */
1627                         BLI_movelisttolist(anim_data, &tmp_data);
1628                         BLI_assert(BLI_listbase_is_empty(&tmp_data));
1629                         items += tmp_items;
1630                 }
1631         }
1632         
1633         return items;
1634 }
1635
1636 /* Grab all Grease Pencil datablocks in file */
1637 // TODO: should this be amalgamated with the dopesheet filtering code?
1638 static size_t animdata_filter_gpencil(bAnimContext *ac, ListBase *anim_data, void *UNUSED(data), int filter_mode)
1639 {
1640         bDopeSheet *ads = ac->ads;
1641         size_t items = 0;
1642         
1643         if (ads->filterflag & ADS_FILTER_GP_3DONLY) {
1644                 Scene *scene = (Scene *)ads->source;
1645                 Base *base;
1646                 
1647                 /* Active scene's GPencil block first - No parent item needed... */
1648                 if (scene->gpd) {
1649                         items += animdata_filter_gpencil_data(anim_data, ads, scene->gpd, filter_mode);
1650                 }
1651                 
1652                 /* Objects in the scene */
1653                 for (base = scene->base.first; base; base = base->next) {
1654                         /* Only consider this object if it has got some GP data (saving on all the other tests) */
1655                         if (base->object && base->object->gpd) {
1656                                 Object *ob = base->object;
1657                                 
1658                                 /* firstly, check if object can be included, by the following factors:
1659                                  *      - if only visible, must check for layer and also viewport visibility
1660                                  *              --> while tools may demand only visible, user setting takes priority
1661                                  *                      as user option controls whether sets of channels get included while
1662                                  *                      tool-flag takes into account collapsed/open channels too
1663                                  *      - if only selected, must check if object is selected 
1664                                  *      - there must be animation data to edit (this is done recursively as we 
1665                                  *        try to add the channels)
1666                                  */
1667                                 if ((filter_mode & ANIMFILTER_DATA_VISIBLE) && !(ads->filterflag & ADS_FILTER_INCL_HIDDEN)) {
1668                                         /* layer visibility - we check both object and base, since these may not be in sync yet */
1669                                         if ((scene->lay & (ob->lay | base->lay)) == 0) continue;
1670                                         
1671                                         /* outliner restrict-flag */
1672                                         if (ob->restrictflag & OB_RESTRICT_VIEW) continue;
1673                                 }
1674                                 
1675                                 /* check selection and object type filters */
1676                                 if ( (ads->filterflag & ADS_FILTER_ONLYSEL) && !((base->flag & SELECT) /*|| (base == scene->basact)*/) ) {
1677                                         /* only selected should be shown */
1678                                         continue;
1679                                 }
1680                                 
1681                                 /* check if object belongs to the filtering group if option to filter 
1682                                  * objects by the grouped status is on
1683                                  *      - used to ease the process of doing multiple-character choreographies
1684                                  */
1685                                 if (ads->filterflag & ADS_FILTER_ONLYOBGROUP) {
1686                                         if (BKE_group_object_exists(ads->filter_grp, ob) == 0)
1687                                                 continue;
1688                                 }
1689                                 
1690                                 /* finally, include this object's grease pencil datablock */
1691                                 /* XXX: Should we store these under expanders per item? */
1692                                 items += animdata_filter_gpencil_data(anim_data, ads, ob->gpd, filter_mode);
1693                         }
1694                 }
1695         }
1696         else {
1697                 bGPdata *gpd;
1698                 
1699                 /* Grab all Grease Pencil datablocks directly from main, but only those that seem to be useful somewhere */
1700                 for (gpd = G.main->gpencil.first; gpd; gpd = gpd->id.next) {
1701                         /* only show if gpd is used by something... */
1702                         if (ID_REAL_USERS(gpd) < 1)
1703                                 continue;
1704                         
1705                         /* add GP frames from this datablock */
1706                         items += animdata_filter_gpencil_data(anim_data, ads, gpd, filter_mode);
1707                 }
1708         }
1709         
1710         /* return the number of items added to the list */
1711         return items;
1712 }
1713
1714 /* Helper for Grease Pencil data integrated with main DopeSheet */
1715 static size_t animdata_filter_ds_gpencil(bAnimContext *ac, ListBase *anim_data, bDopeSheet *ads, bGPdata *gpd, int filter_mode)
1716 {
1717         ListBase tmp_data = {NULL, NULL};
1718         size_t tmp_items = 0;
1719         size_t items = 0;
1720         
1721         /* add relevant animation channels for Grease Pencil */
1722         BEGIN_ANIMFILTER_SUBCHANNELS(EXPANDED_GPD(gpd))
1723         {
1724                 /* add animation channels */
1725                 tmp_items += animfilter_block_data(ac, &tmp_data, ads, &gpd->id, filter_mode);
1726                 
1727                 /* add Grease Pencil layers */
1728                 // TODO: do these need a separate expander?
1729                 // XXX:  what order should these go in?
1730         }
1731         END_ANIMFILTER_SUBCHANNELS;
1732         
1733         /* did we find anything? */
1734         if (tmp_items) {
1735                 /* include data-expand widget first */
1736                 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
1737                         /* check if filtering by active status */
1738                         // XXX: active check here needs checking
1739                         if (ANIMCHANNEL_ACTIVEOK(gpd)) {
1740                                 ANIMCHANNEL_NEW_CHANNEL(gpd, ANIMTYPE_DSGPENCIL, gpd);
1741                         }
1742                 }
1743                 
1744                 /* now add the list of collected channels */
1745                 BLI_movelisttolist(anim_data, &tmp_data);
1746                 BLI_assert(BLI_listbase_is_empty(&tmp_data));
1747                 items += tmp_items;
1748         }
1749         
1750         /* return the number of items added to the list */
1751         return items;
1752 }
1753
1754 /* Helper for Mask Editing - mask layers */
1755 static size_t animdata_filter_mask_data(ListBase *anim_data, Mask *mask, const int filter_mode)
1756 {
1757         MaskLayer *masklay_act = BKE_mask_layer_active(mask);
1758         MaskLayer *masklay;
1759         size_t items = 0;
1760
1761         /* loop over layers as the conditions are acceptable */
1762         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1763                 /* only if selected */
1764                 if (ANIMCHANNEL_SELOK(SEL_MASKLAY(masklay)) ) {
1765                         /* only if editable */
1766                         if (!(filter_mode & ANIMFILTER_FOREDIT) || EDITABLE_MASK(masklay)) {
1767                                 /* active... */
1768                                 if (!(filter_mode & ANIMFILTER_ACTIVE) || (masklay_act == masklay)) {
1769                                         /* add to list */
1770                                         ANIMCHANNEL_NEW_CHANNEL(masklay, ANIMTYPE_MASKLAYER, mask);
1771                                 }
1772                         }
1773                 }
1774         }
1775
1776         return items;
1777 }
1778
1779 /* Grab all mask data */
1780 static size_t animdata_filter_mask(ListBase *anim_data, void *UNUSED(data), int filter_mode)
1781 {
1782         Mask *mask;
1783         size_t items = 0;
1784         
1785         /* for now, grab mask datablocks directly from main */
1786         // XXX: this is not good...
1787         for (mask = G.main->mask.first; mask; mask = mask->id.next) {
1788                 ListBase tmp_data = {NULL, NULL};
1789                 size_t tmp_items = 0;
1790                 
1791                 /* only show if mask is used by something... */
1792                 if (ID_REAL_USERS(mask) < 1)
1793                         continue;
1794                 
1795                 /* add mask animation channels */
1796                 BEGIN_ANIMFILTER_SUBCHANNELS(EXPANDED_MASK(mask))
1797                 {
1798                         tmp_items += animdata_filter_mask_data(&tmp_data, mask, filter_mode);
1799                 }
1800                 END_ANIMFILTER_SUBCHANNELS;
1801                 
1802                 /* did we find anything? */
1803                 if (tmp_items) {
1804                         /* include data-expand widget first */
1805                         if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
1806                                 /* add gpd as channel too (if for drawing, and it has layers) */
1807                                 ANIMCHANNEL_NEW_CHANNEL(mask, ANIMTYPE_MASKDATABLOCK, NULL);
1808                         }
1809                         
1810                         /* now add the list of collected channels */
1811                         BLI_movelisttolist(anim_data, &tmp_data);
1812                         BLI_assert(BLI_listbase_is_empty(&tmp_data));
1813                         items += tmp_items;
1814                 }
1815         }
1816         
1817         /* return the number of items added to the list */
1818         return items;
1819 }
1820
1821 /* NOTE: owner_id is scene, material, or texture block, which is the direct owner of the node tree in question */
1822 static size_t animdata_filter_ds_nodetree_group(bAnimContext *ac, ListBase *anim_data, bDopeSheet *ads, ID *owner_id, bNodeTree *ntree, int filter_mode)
1823 {
1824         ListBase tmp_data = {NULL, NULL};
1825         size_t tmp_items = 0;
1826         size_t items = 0;
1827         
1828         /* add nodetree animation channels */
1829         BEGIN_ANIMFILTER_SUBCHANNELS(FILTER_NTREE_DATA(ntree))
1830         {
1831                 /* animation data filtering */
1832                 tmp_items += animfilter_block_data(ac, &tmp_data, ads, (ID *)ntree, filter_mode);
1833         }
1834         END_ANIMFILTER_SUBCHANNELS;
1835         
1836         /* did we find anything? */
1837         if (tmp_items) {
1838                 /* include data-expand widget first */
1839                 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
1840                         /* check if filtering by active status */
1841                         if (ANIMCHANNEL_ACTIVEOK(ntree)) {
1842                                 ANIMCHANNEL_NEW_CHANNEL(ntree, ANIMTYPE_DSNTREE, owner_id);
1843                         }
1844                 }
1845                 
1846                 /* now add the list of collected channels */
1847                 BLI_movelisttolist(anim_data, &tmp_data);
1848                 BLI_assert(BLI_listbase_is_empty(&tmp_data));
1849                 items += tmp_items;
1850         }
1851         
1852         /* return the number of items added to the list */
1853         return items;
1854 }
1855
1856 static size_t animdata_filter_ds_nodetree(bAnimContext *ac, ListBase *anim_data, bDopeSheet *ads, ID *owner_id, bNodeTree *ntree, int filter_mode)
1857 {
1858         bNode *node;
1859         size_t items = 0;
1860         
1861         items += animdata_filter_ds_nodetree_group(ac, anim_data, ads, owner_id, ntree, filter_mode);
1862         
1863         for (node = ntree->nodes.first; node; node = node->next) {
1864                 if (node->type == NODE_GROUP) {
1865                         if (node->id) {
1866                                 if ((ads->filterflag & ADS_FILTER_ONLYSEL) && (node->flag & NODE_SELECT) == 0) {
1867                                         continue;
1868                                 }
1869                                 items += animdata_filter_ds_nodetree_group(ac, anim_data, ads, owner_id, (bNodeTree *) node->id,
1870                                                                            filter_mode | ANIMFILTER_TMP_IGNORE_ONLYSEL);
1871                         }
1872                 }
1873         }
1874
1875         return items;
1876 }
1877
1878 static size_t animdata_filter_ds_linestyle(bAnimContext *ac, ListBase *anim_data, bDopeSheet *ads, Scene *sce, int filter_mode)
1879 {
1880         SceneRenderLayer *srl;
1881         FreestyleLineSet *lineset;
1882         size_t items = 0;
1883         
1884         for (srl = sce->r.layers.first; srl; srl = srl->next) {
1885                 for (lineset = srl->freestyleConfig.linesets.first; lineset; lineset = lineset->next) {
1886                         if (lineset->linestyle) {
1887                                 lineset->linestyle->id.tag |= LIB_TAG_DOIT;
1888                         }
1889                 }
1890         }
1891         
1892         for (srl = sce->r.layers.first; srl; srl = srl->next) {
1893                 /* skip render layers without Freestyle enabled */
1894                 if (!(srl->layflag & SCE_LAY_FRS))
1895                         continue;
1896                 
1897                 /* loop over linesets defined in the render layer */
1898                 for (lineset = srl->freestyleConfig.linesets.first; lineset; lineset = lineset->next) {
1899                         FreestyleLineStyle *linestyle = lineset->linestyle;
1900                         ListBase tmp_data = {NULL, NULL};
1901                         size_t tmp_items = 0;
1902
1903                         if ((linestyle == NULL) ||
1904                             !(linestyle->id.tag & LIB_TAG_DOIT))
1905                         {
1906                                 continue;
1907                         }
1908                         linestyle->id.tag &= ~LIB_TAG_DOIT;
1909                         
1910                         /* add scene-level animation channels */
1911                         BEGIN_ANIMFILTER_SUBCHANNELS(FILTER_LS_SCED(linestyle))
1912                         {
1913                                 /* animation data filtering */
1914                                 tmp_items += animfilter_block_data(ac, &tmp_data, ads, (ID *)linestyle, filter_mode);
1915                         }
1916                         END_ANIMFILTER_SUBCHANNELS;
1917                         
1918                         /* did we find anything? */
1919                         if (tmp_items) {
1920                                 /* include anim-expand widget first */
1921                                 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
1922                                         /* check if filtering by active status */
1923                                         if (ANIMCHANNEL_ACTIVEOK(linestyle)) {
1924                                                 ANIMCHANNEL_NEW_CHANNEL(linestyle, ANIMTYPE_DSLINESTYLE, sce);
1925                                         }
1926                                 }
1927                                 
1928                                 /* now add the list of collected channels */
1929                                 BLI_movelisttolist(anim_data, &tmp_data);
1930                                 BLI_assert(BLI_listbase_is_empty(&tmp_data));
1931                                 items += tmp_items;
1932                         }
1933                 }
1934         }
1935         
1936         /* return the number of items added to the list */
1937         return items;
1938 }
1939
1940 static size_t animdata_filter_ds_texture(bAnimContext *ac, ListBase *anim_data, bDopeSheet *ads, 
1941                                          Tex *tex, ID *owner_id, int filter_mode)
1942 {
1943         ListBase tmp_data = {NULL, NULL};
1944         size_t tmp_items = 0;
1945         size_t items = 0;
1946         
1947         /* add texture's animation data to temp collection */
1948         BEGIN_ANIMFILTER_SUBCHANNELS(FILTER_TEX_DATA(tex)) 
1949         {
1950                 /* texture animdata */
1951                 tmp_items += animfilter_block_data(ac, &tmp_data, ads, (ID *)tex, filter_mode);
1952                 
1953                 /* nodes */
1954                 if ((tex->nodetree) && !(ads->filterflag & ADS_FILTER_NONTREE)) {
1955                         /* owner_id as id instead of texture, since it'll otherwise be impossible to track the depth */
1956                         // FIXME: perhaps as a result, textures should NOT be included under materials, but under their own section instead
1957                         // so that free-floating textures can also be animated
1958                         tmp_items += animdata_filter_ds_nodetree(ac, &tmp_data, ads, (ID *)tex, tex->nodetree, filter_mode);
1959                 }
1960         }
1961         END_ANIMFILTER_SUBCHANNELS;
1962         
1963         /* did we find anything? */
1964         if (tmp_items) {
1965                 /* include texture-expand widget? */
1966                 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
1967                         /* check if filtering by active status */
1968                         if (ANIMCHANNEL_ACTIVEOK(tex)) {
1969                                 ANIMCHANNEL_NEW_CHANNEL(tex, ANIMTYPE_DSTEX, owner_id);
1970                         }
1971                 }
1972                 
1973                 /* now add the list of collected channels */
1974                 BLI_movelisttolist(anim_data, &tmp_data);
1975                 BLI_assert(BLI_listbase_is_empty(&tmp_data));
1976                 items += tmp_items;
1977         }
1978         
1979         /* return the number of items added to the list */
1980         return items;
1981 }
1982
1983 /* NOTE: owner_id is either material, lamp, or world block, which is the direct owner of the texture stack in question */
1984 static size_t animdata_filter_ds_textures(bAnimContext *ac, ListBase *anim_data, bDopeSheet *ads, ID *owner_id, int filter_mode)
1985 {
1986         MTex **mtex = NULL;
1987         size_t items = 0;
1988         int a = 0;
1989         
1990         /* get datatype specific data first */
1991         if (owner_id == NULL)
1992                 return 0;
1993         
1994         switch (GS(owner_id->name)) {
1995                 case ID_MA:
1996                 {
1997                         Material *ma = (Material *)owner_id;
1998                         mtex = (MTex **)(&ma->mtex);
1999                         break;
2000                 }
2001                 case ID_LA:
2002                 {
2003                         Lamp *la = (Lamp *)owner_id;
2004                         mtex = (MTex **)(&la->mtex);
2005                         break;
2006                 }
2007                 case ID_WO:
2008                 {
2009                         World *wo = (World *)owner_id;
2010                         mtex = (MTex **)(&wo->mtex);
2011                         break;
2012                 }
2013                 case ID_PA:
2014                 {
2015                         ParticleSettings *part = (ParticleSettings *)owner_id;
2016                         mtex = (MTex **)(&part->mtex);
2017                         break;
2018                 }
2019                 default: 
2020                 {
2021                         /* invalid/unsupported option */
2022                         if (G.debug & G_DEBUG)
2023                                 printf("ERROR: Unsupported owner_id (i.e. texture stack) for filter textures - %s\n", owner_id->name);
2024                         return 0;
2025                 }
2026         }
2027         
2028         /* firstly check that we actuallly have some textures, by gathering all textures in a temp list */
2029         for (a = 0; a < MAX_MTEX; a++) {
2030                 Tex *tex = (mtex[a]) ? mtex[a]->tex : NULL;
2031                 
2032                 /* for now, if no texture returned, skip (this shouldn't confuse the user I hope) */
2033                 if (tex == NULL) 
2034                         continue;
2035                 
2036                 /* add texture's anim channels */
2037                 items += animdata_filter_ds_texture(ac, anim_data, ads, tex, owner_id, filter_mode);
2038         }
2039         
2040         /* return the number of items added to the list */
2041         return items;
2042 }
2043
2044
2045 static size_t animdata_filter_ds_material(bAnimContext *ac, ListBase *anim_data, bDopeSheet *ads, Material *ma, int filter_mode)
2046 {
2047         ListBase tmp_data = {NULL, NULL};
2048         size_t tmp_items = 0;
2049         size_t items = 0;
2050         
2051         /* add material's animation data to temp collection */
2052         BEGIN_ANIMFILTER_SUBCHANNELS(FILTER_MAT_OBJD(ma))
2053         {
2054                 /* material's animation data */
2055                 tmp_items += animfilter_block_data(ac, &tmp_data, ads, (ID *)ma, filter_mode);
2056                         
2057                 /* textures */
2058                 if (!(ads->filterflag & ADS_FILTER_NOTEX))
2059                         tmp_items += animdata_filter_ds_textures(ac, &tmp_data, ads, (ID *)ma, filter_mode);
2060                         
2061                 /* nodes */
2062                 if ((ma->nodetree) && !(ads->filterflag & ADS_FILTER_NONTREE)) 
2063                         tmp_items += animdata_filter_ds_nodetree(ac, &tmp_data, ads, (ID *)ma, ma->nodetree, filter_mode);
2064         }
2065         END_ANIMFILTER_SUBCHANNELS;
2066         
2067         /* did we find anything? */
2068         if (tmp_items) {
2069                 /* include material-expand widget first */
2070                 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
2071                         /* check if filtering by active status */
2072                         if (ANIMCHANNEL_ACTIVEOK(ma)) {
2073                                 ANIMCHANNEL_NEW_CHANNEL(ma, ANIMTYPE_DSMAT, ma);
2074                         }
2075                 }
2076                 
2077                 /* now add the list of collected channels */
2078                 BLI_movelisttolist(anim_data, &tmp_data);
2079                 BLI_assert(BLI_listbase_is_empty(&tmp_data));
2080                 items += tmp_items;
2081         }
2082         
2083         return items;
2084 }
2085
2086 static size_t animdata_filter_ds_materials(bAnimContext *ac, ListBase *anim_data, bDopeSheet *ads, Object *ob, int filter_mode)
2087 {
2088         bool has_nested = false;
2089         size_t items = 0;
2090         int a = 0;
2091         
2092         /* first pass: take the materials referenced via the Material slots of the object */
2093         for (a = 1; a <= ob->totcol; a++) {
2094                 Material *ma = give_current_material(ob, a);
2095                 
2096                 /* if material is valid, try to add relevant contents from here */
2097                 if (ma) {
2098                         /* add channels */
2099                         items += animdata_filter_ds_material(ac, anim_data, ads, ma, filter_mode);
2100                         
2101                         /* for optimising second pass - check if there's a nested material here to come back for */
2102                         if (has_nested == false) {
2103                                 has_nested = (give_node_material(ma) != NULL);
2104                         }
2105                 }
2106         }
2107         
2108         /* second pass: go through a second time looking for "nested" materials (material.material references)
2109          *
2110          * NOTE: here we ignore the expanded status of the parent, as it could be too confusing as to why these are
2111          *       disappearing/not available, since the relationships between these is not that clear
2112          */
2113         if (has_nested) {
2114                 for (a = 1; a <= ob->totcol; a++) {
2115                         Material *base = give_current_material(ob, a);
2116                         Material *ma   = give_node_material(base);
2117                         
2118                         /* add channels from the nested material if it exists
2119                          *   - skip if the same material is referenced in its node tree
2120                          *     (which is common for BI materials) as that results in
2121                          *     confusing duplicates
2122                          */
2123                         if ((ma) && (ma != base)) {
2124                                 items += animdata_filter_ds_material(ac, anim_data, ads, ma, filter_mode);
2125                         }
2126                 }
2127         }
2128         
2129         /* return the number of items added to the list */
2130         return items;
2131 }
2132
2133
2134 /* ............ */
2135
2136 /* Temporary context for modifier linked-data channel extraction */
2137 typedef struct tAnimFilterModifiersContext {
2138         bAnimContext *ac;       /* anim editor context */
2139         bDopeSheet *ads;    /* dopesheet filtering settings */
2140         
2141         ListBase tmp_data;  /* list of channels created (but not yet added to the main list) */
2142         size_t items;       /* number of channels created */
2143         
2144         int filter_mode;    /* flags for stuff we want to filter */
2145 } tAnimFilterModifiersContext;
2146
2147
2148 /* dependency walker callback for modifier dependencies */
2149 static void animfilter_modifier_idpoin_cb(void *afm_ptr, Object *ob, ID **idpoin, int UNUSED(cd_flag))
2150 {
2151         tAnimFilterModifiersContext *afm = (tAnimFilterModifiersContext *)afm_ptr;
2152         ID *owner_id = &ob->id;
2153         ID *id = *idpoin;
2154         
2155         /* NOTE: the walker only guarantees to give us all the ID-ptr *slots*, 
2156          * not just the ones which are actually used, so be careful!
2157          */
2158         if (id == NULL)
2159                 return;
2160                 
2161         /* check if this is something we're interested in... */
2162         switch (GS(id->name)) {
2163                 case ID_TE: /* Textures */
2164                 {
2165                         Tex *tex = (Tex *)id;
2166                         if (!(afm->ads->filterflag & ADS_FILTER_NOTEX)) {       
2167                                 afm->items += animdata_filter_ds_texture(afm->ac, &afm->tmp_data, afm->ads, tex, owner_id, afm->filter_mode);
2168                         }
2169                         break;
2170                 }
2171                 
2172                 /* TODO: images? */
2173         }
2174 }
2175
2176 /* animation linked to data used by modifiers 
2177  * NOTE: strictly speaking, modifier animation is already included under Object level
2178  *       but for some modifiers (e.g. Displace), there can be linked data that has settings
2179  *       which would be nice to animate (i.e. texture parameters) but which are not actually
2180  *       attached to any other objects/materials/etc. in the scene
2181  */
2182 // TODO: do we want an expander for this?
2183 static size_t animdata_filter_ds_modifiers(bAnimContext *ac, ListBase *anim_data, bDopeSheet *ads, Object *ob, int filter_mode)
2184 {
2185         tAnimFilterModifiersContext afm = {NULL};
2186         size_t items = 0;
2187         
2188         /* 1) create a temporary "context" containing all the info we have here to pass to the callback 
2189          *    use to walk through the dependencies of the modifiers
2190          *
2191          * ! Assumes that all other unspecified values (i.e. accumulation buffers) are zero'd out properly
2192          */
2193         afm.ac          = ac;
2194         afm.ads         = ads;
2195         afm.filter_mode = filter_mode;
2196         
2197         /* 2) walk over dependencies */
2198         modifiers_foreachIDLink(ob, animfilter_modifier_idpoin_cb, &afm);
2199         
2200         /* 3) extract data from the context, merging it back into the standard list */
2201         if (afm.items) {
2202                 /* now add the list of collected channels */
2203                 BLI_movelisttolist(anim_data, &afm.tmp_data);
2204                 BLI_assert(BLI_listbase_is_empty(&afm.tmp_data));
2205                 items += afm.items;
2206         }
2207         
2208         return items;
2209 }
2210
2211 /* ............ */
2212
2213
2214 static size_t animdata_filter_ds_particles(bAnimContext *ac, ListBase *anim_data, bDopeSheet *ads, Object *ob, int filter_mode)
2215 {
2216         ParticleSystem *psys;
2217         size_t items = 0;
2218
2219         for (psys = ob->particlesystem.first; psys; psys = psys->next) {
2220                 ListBase tmp_data = {NULL, NULL};
2221                 size_t tmp_items = 0;
2222                 
2223                 /* if no material returned, skip - so that we don't get weird blank entries... */
2224                 if (ELEM(NULL, psys->part, psys->part->adt))
2225                         continue;
2226                 
2227                 /* add particle-system's animation data to temp collection */
2228                 BEGIN_ANIMFILTER_SUBCHANNELS(FILTER_PART_OBJD(psys->part))
2229                 {
2230                         /* particle system's animation data */
2231                         tmp_items += animfilter_block_data(ac, &tmp_data, ads, (ID *)psys->part, filter_mode);
2232                         
2233                         /* textures */
2234                         if (!(ads->filterflag & ADS_FILTER_NOTEX))
2235                                 tmp_items += animdata_filter_ds_textures(ac, &tmp_data, ads, (ID *)psys->part, filter_mode);
2236                 }
2237                 END_ANIMFILTER_SUBCHANNELS;
2238                 
2239                 /* did we find anything? */
2240                 if (tmp_items) {
2241                         /* include particle-expand widget first */
2242                         if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
2243                                 /* check if filtering by active status */
2244                                 if (ANIMCHANNEL_ACTIVEOK(psys->part)) {
2245                                         ANIMCHANNEL_NEW_CHANNEL(psys->part, ANIMTYPE_DSPART, psys->part);
2246                                 }
2247                         }
2248                         
2249                         /* now add the list of collected channels */
2250                         BLI_movelisttolist(anim_data, &tmp_data);
2251                         BLI_assert(BLI_listbase_is_empty(&tmp_data));
2252                         items += tmp_items;
2253                 }
2254         }
2255         
2256         /* return the number of items added to the list */
2257         return items;
2258 }
2259
2260
2261 static size_t animdata_filter_ds_obdata(bAnimContext *ac, ListBase *anim_data, bDopeSheet *ads, Object *ob, int filter_mode)
2262 {
2263         ListBase tmp_data = {NULL, NULL};
2264         size_t tmp_items = 0;
2265         size_t items = 0;
2266         
2267         IdAdtTemplate *iat = ob->data;
2268         short type = 0, expanded = 0;
2269         
2270         /* get settings based on data type */
2271         switch (ob->type) {
2272                 case OB_CAMERA: /* ------- Camera ------------ */
2273                 {
2274                         Camera *ca = (Camera *)ob->data;
2275                         
2276                         if (ads->filterflag & ADS_FILTER_NOCAM)
2277                                 return 0;
2278                         
2279                         type = ANIMTYPE_DSCAM;
2280                         expanded = FILTER_CAM_OBJD(ca);
2281                         break;
2282                 }
2283                 case OB_LAMP: /* ---------- Lamp ----------- */
2284                 {
2285                         Lamp *la = (Lamp *)ob->data;
2286                         
2287                         if (ads->filterflag & ADS_FILTER_NOLAM)
2288                                 return 0;
2289                         
2290                         type = ANIMTYPE_DSLAM;
2291                         expanded = FILTER_LAM_OBJD(la);
2292                         break;
2293                 }
2294                 case OB_CURVE: /* ------- Curve ---------- */
2295                 case OB_SURF: /* ------- Nurbs Surface ---------- */
2296                 case OB_FONT: /* ------- Text Curve ---------- */
2297                 {
2298                         Curve *cu = (Curve *)ob->data;
2299                         
2300                         if (ads->filterflag & ADS_FILTER_NOCUR)
2301                                 return 0;
2302                         
2303                         type = ANIMTYPE_DSCUR;
2304                         expanded = FILTER_CUR_OBJD(cu);
2305                         break;
2306                 }
2307                 case OB_MBALL: /* ------- MetaBall ---------- */
2308                 {
2309                         MetaBall *mb = (MetaBall *)ob->data;
2310                         
2311                         if (ads->filterflag & ADS_FILTER_NOMBA)
2312                                 return 0;
2313                         
2314                         type = ANIMTYPE_DSMBALL;
2315                         expanded = FILTER_MBALL_OBJD(mb);
2316                         break;
2317                 }
2318                 case OB_ARMATURE: /* ------- Armature ---------- */
2319                 {
2320                         bArmature *arm = (bArmature *)ob->data;
2321                         
2322                         if (ads->filterflag & ADS_FILTER_NOARM)
2323                                 return 0;
2324                         
2325                         type = ANIMTYPE_DSARM;
2326                         expanded = FILTER_ARM_OBJD(arm);
2327                         break;
2328                 }
2329                 case OB_MESH: /* ------- Mesh ---------- */
2330                 {
2331                         Mesh *me = (Mesh *)ob->data;
2332                         
2333                         if (ads->filterflag & ADS_FILTER_NOMESH)
2334                                 return 0;
2335                         
2336                         type = ANIMTYPE_DSMESH;
2337                         expanded = FILTER_MESH_OBJD(me);
2338                         break;
2339                 }
2340                 case OB_LATTICE: /* ---- Lattice ---- */
2341                 {
2342                         Lattice *lt = (Lattice *)ob->data;
2343                         
2344                         if (ads->filterflag & ADS_FILTER_NOLAT)
2345                                 return 0;
2346                         
2347                         type = ANIMTYPE_DSLAT;
2348                         expanded = FILTER_LATTICE_OBJD(lt);
2349                         break;
2350                 }
2351                 case OB_SPEAKER: /* ---------- Speaker ----------- */
2352                 {
2353                         Speaker *spk = (Speaker *)ob->data;
2354                         
2355                         type = ANIMTYPE_DSSPK;
2356                         expanded = FILTER_SPK_OBJD(spk);
2357                         break;
2358                 }
2359         }
2360         
2361         /* add object data animation channels */
2362         BEGIN_ANIMFILTER_SUBCHANNELS(expanded)
2363         {
2364                 /* animation data filtering */
2365                 tmp_items += animfilter_block_data(ac, &tmp_data, ads, (ID *)iat, filter_mode);
2366                 
2367                 /* sub-data filtering... */
2368                 switch (ob->type) {
2369                         case OB_LAMP:  /* lamp - textures + nodetree */
2370                         {
2371                                 Lamp *la = ob->data;
2372                                 bNodeTree *ntree = la->nodetree;
2373                                 
2374                                 /* nodetree */
2375                                 if ((ntree) && !(ads->filterflag & ADS_FILTER_NONTREE))
2376                                         tmp_items += animdata_filter_ds_nodetree(ac, &tmp_data, ads, &la->id, ntree, filter_mode);
2377                                 
2378                                 /* textures */
2379                                 if (!(ads->filterflag & ADS_FILTER_NOTEX))
2380                                         tmp_items += animdata_filter_ds_textures(ac, &tmp_data, ads, &la->id, filter_mode);
2381                                 break;
2382                         }
2383                 }
2384         }
2385         END_ANIMFILTER_SUBCHANNELS;
2386         
2387         /* did we find anything? */
2388         if (tmp_items) {
2389                 /* include data-expand widget first */
2390                 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
2391                         /* check if filtering by active status */
2392                         if (ANIMCHANNEL_ACTIVEOK(iat)) {
2393                                 ANIMCHANNEL_NEW_CHANNEL(iat, type, iat);
2394                         }
2395                 }
2396                 
2397                 /* now add the list of collected channels */
2398                 BLI_movelisttolist(anim_data, &tmp_data);
2399                 BLI_assert(BLI_listbase_is_empty(&tmp_data));
2400                 items += tmp_items;
2401         }
2402         
2403         /* return the number of items added to the list */
2404         return items;
2405 }
2406
2407 /* shapekey-level animation */
2408 static size_t animdata_filter_ds_keyanim(bAnimContext *ac, ListBase *anim_data, bDopeSheet *ads, Object *ob, Key *key, int filter_mode)
2409 {
2410         ListBase tmp_data = {NULL, NULL};
2411         size_t tmp_items = 0;
2412         size_t items = 0;
2413         
2414         /* add shapekey-level animation channels */
2415         BEGIN_ANIMFILTER_SUBCHANNELS(FILTER_SKE_OBJD(key))
2416         {
2417                 /* animation data filtering */
2418                 tmp_items += animfilter_block_data(ac, &tmp_data, ads, (ID *)key, filter_mode);
2419         }
2420         END_ANIMFILTER_SUBCHANNELS;
2421         
2422         /* did we find anything? */
2423         if (tmp_items) {
2424                 /* include key-expand widget first */
2425                 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
2426                         if (ANIMCHANNEL_ACTIVEOK(key)) {
2427                                 ANIMCHANNEL_NEW_CHANNEL(key, ANIMTYPE_DSSKEY, ob);
2428                         }
2429                 }
2430                 
2431                 /* now add the list of collected channels */
2432                 BLI_movelisttolist(anim_data, &tmp_data);
2433                 BLI_assert(BLI_listbase_is_empty(&tmp_data));
2434                 items += tmp_items;
2435         }
2436         
2437         /* return the number of items added to the list */
2438         return items;
2439 }
2440
2441
2442 /* object-level animation */
2443 static size_t animdata_filter_ds_obanim(bAnimContext *ac, ListBase *anim_data, bDopeSheet *ads, Object *ob, int filter_mode)
2444 {
2445         ListBase tmp_data = {NULL, NULL};
2446         size_t tmp_items = 0;
2447         size_t items = 0;
2448         
2449         AnimData *adt = ob->adt;
2450         short type = 0, expanded = 1;
2451         void *cdata = NULL;
2452
2453         /* determine the type of expander channels to use */
2454         /* this is the best way to do this for now... */
2455         ANIMDATA_FILTER_CASES(ob,
2456                 { /* AnimData - no channel, but consider data */ },
2457                 { /* NLA - no channel, but consider data */ },
2458                 { /* Drivers */
2459                         type = ANIMTYPE_FILLDRIVERS;
2460                         cdata = adt;
2461                         expanded = EXPANDED_DRVD(adt);
2462                 },
2463                 { /* NLA Strip Controls - no dedicated channel for now (XXX) */ },
2464                 { /* Keyframes */
2465                         type = ANIMTYPE_FILLACTD;
2466                         cdata = adt->action;
2467                         expanded = EXPANDED_ACTC(adt->action);
2468                 });
2469                 
2470         /* add object-level animation channels */
2471         BEGIN_ANIMFILTER_SUBCHANNELS(expanded)
2472         {
2473                 /* animation data filtering */
2474                 tmp_items += animfilter_block_data(ac, &tmp_data, ads, (ID *)ob, filter_mode);
2475         }
2476         END_ANIMFILTER_SUBCHANNELS;
2477         
2478         /* did we find anything? */
2479         if (tmp_items) {
2480                 /* include anim-expand widget first */
2481                 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
2482                         if (type != ANIMTYPE_NONE) {
2483                                 /* NOTE: active-status (and the associated checks) don't apply here... */
2484                                 ANIMCHANNEL_NEW_CHANNEL(cdata, type, ob);
2485                         }
2486                 }
2487                 
2488                 /* now add the list of collected channels */
2489                 BLI_movelisttolist(anim_data, &tmp_data);
2490                 BLI_assert(BLI_listbase_is_empty(&tmp_data));
2491                 items += tmp_items;
2492         }
2493         
2494         /* return the number of items added to the list */
2495         return items;
2496 }
2497
2498 /* get animation channels from object2 */
2499 static size_t animdata_filter_dopesheet_ob(bAnimContext *ac, ListBase *anim_data, bDopeSheet *ads, Base *base, int filter_mode)
2500 {
2501         ListBase tmp_data = {NULL, NULL};
2502         Object *ob = base->object;
2503         size_t tmp_items = 0;
2504         size_t items = 0;
2505         
2506         /* filter data contained under object first */
2507         BEGIN_ANIMFILTER_SUBCHANNELS(EXPANDED_OBJC(ob))
2508         {
2509                 Key *key = BKE_key_from_object(ob);
2510                 
2511                 /* object-level animation */
2512                 if ((ob->adt) && !(ads->filterflag & ADS_FILTER_NOOBJ)) {
2513                         tmp_items += animdata_filter_ds_obanim(ac, &tmp_data, ads, ob, filter_mode);
2514                 }
2515                 
2516                 /* shape-key */
2517                 if ((key && key->adt) && !(ads->filterflag & ADS_FILTER_NOSHAPEKEYS)) {
2518                         tmp_items += animdata_filter_ds_keyanim(ac, &tmp_data, ads, ob, key, filter_mode);
2519                 }
2520                 
2521                 /* modifiers */
2522                 if ((ob->modifiers.first) && !(ads->filterflag & ADS_FILTER_NOMODIFIERS)) {
2523                         tmp_items += animdata_filter_ds_modifiers(ac, &tmp_data, ads, ob, filter_mode);
2524                 }
2525                 
2526                 /* materials */
2527                 if ((ob->totcol) && !(ads->filterflag & ADS_FILTER_NOMAT)) {
2528                         tmp_items += animdata_filter_ds_materials(ac, &tmp_data, ads, ob, filter_mode);
2529                 }
2530                 
2531                 /* object data */
2532                 if (ob->data) {
2533                         tmp_items += animdata_filter_ds_obdata(ac, &tmp_data, ads, ob, filter_mode);
2534                 }
2535                 
2536                 /* particles */
2537                 if ((ob->particlesystem.first) && !(ads->filterflag & ADS_FILTER_NOPART)) {
2538                         tmp_items += animdata_filter_ds_particles(ac, &tmp_data, ads, ob, filter_mode);
2539                 }
2540                 
2541                 /* grease pencil */
2542                 if ((ob->gpd) && !(ads->filterflag & ADS_FILTER_NOGPENCIL)) {
2543                         tmp_items += animdata_filter_ds_gpencil(ac, &tmp_data, ads, ob->gpd, filter_mode);
2544                 }
2545         }
2546         END_ANIMFILTER_SUBCHANNELS;
2547         
2548         
2549         /* if we collected some channels, add these to the new list... */
2550         if (tmp_items) {
2551                 /* firstly add object expander if required */
2552                 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
2553                         /* check if filtering by selection */
2554                         // XXX: double-check on this - most of the time, a lot of tools need to filter out these channels!
2555                         if (ANIMCHANNEL_SELOK((base->flag & SELECT))) {
2556                                 /* check if filtering by active status */
2557                                 if (ANIMCHANNEL_ACTIVEOK(ob)) {
2558                                         ANIMCHANNEL_NEW_CHANNEL(base, ANIMTYPE_OBJECT, ob);
2559                                 }
2560                         }
2561                 }
2562                 
2563                 /* now add the list of collected channels */
2564                 BLI_movelisttolist(anim_data, &tmp_data);
2565                 BLI_assert(BLI_listbase_is_empty(&tmp_data));
2566                 items += tmp_items;
2567         }
2568         
2569         /* return the number of items added */
2570         return items;
2571 }
2572
2573 static size_t animdata_filter_ds_world(bAnimContext *ac, ListBase *anim_data, bDopeSheet *ads, Scene *sce, World *wo, int filter_mode)
2574 {
2575         ListBase tmp_data = {NULL, NULL};
2576         size_t tmp_items = 0;
2577         size_t items = 0;
2578         
2579         /* add world animation channels */
2580         BEGIN_ANIMFILTER_SUBCHANNELS(FILTER_WOR_SCED(wo))
2581         {
2582                 /* animation data filtering */
2583                 tmp_items += animfilter_block_data(ac, &tmp_data, ads, (ID *)wo, filter_mode);
2584                 
2585                 /* textures for world */
2586                 if (!(ads->filterflag & ADS_FILTER_NOTEX))
2587                         tmp_items += animdata_filter_ds_textures(ac, &tmp_data, ads, (ID *)wo, filter_mode);
2588                         
2589                 /* nodes */
2590                 if ((wo->nodetree) && !(ads->filterflag & ADS_FILTER_NONTREE)) 
2591                         tmp_items += animdata_filter_ds_nodetree(ac, &tmp_data, ads, (ID *)wo, wo->nodetree, filter_mode);
2592         }
2593         END_ANIMFILTER_SUBCHANNELS;
2594         
2595         /* did we find anything? */
2596         if (tmp_items) {
2597                 /* include data-expand widget first */
2598                 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
2599                         /* check if filtering by active status */
2600                         if (ANIMCHANNEL_ACTIVEOK(wo)) {
2601                                 ANIMCHANNEL_NEW_CHANNEL(wo, ANIMTYPE_DSWOR, sce);
2602                         }
2603                 }
2604                 
2605                 /* now add the list of collected channels */
2606                 BLI_movelisttolist(anim_data, &tmp_data);
2607                 BLI_assert(BLI_listbase_is_empty(&tmp_data));
2608                 items += tmp_items;
2609         }
2610         
2611         /* return the number of items added to the list */
2612         return items;
2613 }
2614
2615 static size_t animdata_filter_ds_scene(bAnimContext *ac, ListBase *anim_data, bDopeSheet *ads, Scene *sce, int filter_mode)
2616 {
2617         ListBase tmp_data = {NULL, NULL};
2618         size_t tmp_items = 0;
2619         size_t items = 0;
2620         
2621         AnimData *adt = sce->adt;
2622         short type = 0, expanded = 1;
2623         void *cdata = NULL;
2624         
2625         /* determine the type of expander channels to use */
2626         // this is the best way to do this for now...
2627         ANIMDATA_FILTER_CASES(sce,
2628                 { /* AnimData - no channel, but consider data */},
2629                 { /* NLA - no channel, but consider data */},
2630                 { /* Drivers */
2631                         type = ANIMTYPE_FILLDRIVERS;
2632                         cdata = adt;
2633                         expanded = EXPANDED_DRVD(adt);
2634                 },
2635                 { /* NLA Strip Controls - no dedicated channel for now (XXX) */ },
2636                 { /* Keyframes */
2637                         type = ANIMTYPE_FILLACTD;
2638                         cdata = adt->action;
2639                         expanded = EXPANDED_ACTC(adt->action);
2640                 });
2641                 
2642         /* add scene-level animation channels */
2643         BEGIN_ANIMFILTER_SUBCHANNELS(expanded)
2644         {
2645                 /* animation data filtering */
2646                 tmp_items += animfilter_block_data(ac, &tmp_data, ads, (ID *)sce, filter_mode);
2647         }
2648         END_ANIMFILTER_SUBCHANNELS;
2649         
2650         /* did we find anything? */
2651         if (tmp_items) {
2652                 /* include anim-expand widget first */
2653                 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
2654                         if (type != ANIMTYPE_NONE) {
2655                                 /* NOTE: active-status (and the associated checks) don't apply here... */
2656                                 ANIMCHANNEL_NEW_CHANNEL(cdata, type, sce);
2657                         }
2658                 }
2659                 
2660                 /* now add the list of collected channels */
2661                 BLI_movelisttolist(anim_data, &tmp_data);
2662                 BLI_assert(BLI_listbase_is_empty(&tmp_data));
2663                 items += tmp_items;
2664         }
2665         
2666         /* return the number of items added to the list */
2667         return items;
2668 }
2669
2670 static size_t animdata_filter_dopesheet_scene(bAnimContext *ac, ListBase *anim_data, bDopeSheet *ads, Scene *sce, int filter_mode)
2671 {
2672         ListBase tmp_data = {NULL, NULL};
2673         size_t tmp_items = 0;
2674         size_t items = 0;
2675         
2676         /* filter data contained under object first */
2677         BEGIN_ANIMFILTER_SUBCHANNELS(EXPANDED_SCEC(sce))
2678         {
2679                 bNodeTree *ntree = sce->nodetree;
2680                 bGPdata *gpd = sce->gpd;
2681                 World *wo = sce->world;
2682                 
2683                 /* Action, Drivers, or NLA for Scene */
2684                 if ((ads->filterflag & ADS_FILTER_NOSCE) == 0) {
2685                         tmp_items += animdata_filter_ds_scene(ac, &tmp_data, ads, sce, filter_mode);
2686                 }
2687                 
2688                 /* world */
2689                 if ((wo) && !(ads->filterflag & ADS_FILTER_NOWOR)) {
2690                         tmp_items += animdata_filter_ds_world(ac, &tmp_data, ads, sce, wo, filter_mode);
2691                 }
2692                 
2693                 /* nodetree */
2694                 if ((ntree) && !(ads->filterflag & ADS_FILTER_NONTREE)) {
2695                         tmp_items += animdata_filter_ds_nodetree(ac, &tmp_data, ads, (ID *)sce, ntree, filter_mode);
2696                 }
2697                 
2698                 /* line styles */
2699                 if ((ads->filterflag & ADS_FILTER_NOLINESTYLE) == 0) {
2700                         tmp_items += animdata_filter_ds_linestyle(ac, &tmp_data, ads, sce, filter_mode);
2701                 }
2702                 
2703                 /* grease pencil */
2704                 if ((gpd) && !(ads->filterflag & ADS_FILTER_NOGPENCIL)) {
2705                         tmp_items += animdata_filter_ds_gpencil(ac, &tmp_data, ads, gpd, filter_mode);
2706                 }
2707                 
2708                 /* TODO: one day, when sequencer becomes its own datatype, perhaps it should be included here */
2709         }
2710         END_ANIMFILTER_SUBCHANNELS;
2711
2712         /* if we collected some channels, add these to the new list... */
2713         if (tmp_items) {
2714                 /* firstly add object expander if required */
2715                 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
2716                         /* check if filtering by selection */
2717                         if (ANIMCHANNEL_SELOK((sce->flag & SCE_DS_SELECTED))) {
2718                                 /* NOTE: active-status doesn't matter for this! */
2719                                 ANIMCHANNEL_NEW_CHANNEL(sce, ANIMTYPE_SCENE, sce);
2720                         }
2721                 }
2722                 
2723                 /* now add the list of collected channels */
2724                 BLI_movelisttolist(anim_data, &tmp_data);
2725                 BLI_assert(BLI_listbase_is_empty(&tmp_data));
2726                 items += tmp_items;
2727         }
2728         
2729         /* return the number of items added */
2730         return items;
2731 }
2732
2733 // TODO: implement pinning... (if and when pinning is done, what we need to do is to provide freeing mechanisms - to protect against data that was deleted)
2734 static size_t animdata_filter_dopesheet(bAnimContext *ac, ListBase *anim_data, bDopeSheet *ads, int filter_mode)
2735 {
2736         Scene *sce = (Scene *)ads->source;
2737         Base *base;
2738         size_t items = 0;
2739         
2740         /* check that we do indeed have a scene */
2741         if ((ads->source == NULL) || (GS(ads->source->name) != ID_SCE)) {
2742                 printf("Dope Sheet Error: No scene!\n");
2743                 if (G.debug & G_DEBUG)
2744                         printf("\tPointer = %p, Name = '%s'\n", (void *)ads->source, (ads->source) ? ads->source->name : NULL);
2745                 return 0;
2746         }
2747         
2748         /* augment the filter-flags with settings based on the dopesheet filterflags 
2749          * so that some temp settings can get added automagically...
2750          */
2751         if (ads->filterflag & ADS_FILTER_SELEDIT) {
2752                 /* only selected F-Curves should get their keyframes considered for editability */
2753                 filter_mode |= ANIMFILTER_SELEDIT;
2754         }
2755         
2756         /* scene-linked animation - e.g. world, compositing nodes, scene anim (including sequencer currently) */
2757         items += animdata_filter_dopesheet_scene(ac, anim_data, ads, sce, filter_mode);
2758         
2759         /* loop over all bases (i.e.objects) in the scene */
2760         for (base = sce->base.first; base; base = base->next) {
2761                 /* check if there's an object (all the relevant checks are done in the ob-function) */
2762                 if (base->object) {
2763                         Object *ob = base->object;
2764                         
2765                         /* firstly, check if object can be included, by the following factors:
2766                          *      - if only visible, must check for layer and also viewport visibility
2767                          *              --> while tools may demand only visible, user setting takes priority
2768                          *                      as user option controls whether sets of channels get included while
2769                          *                      tool-flag takes into account collapsed/open channels too
2770                          *      - if only selected, must check if object is selected 
2771                          *      - there must be animation data to edit (this is done recursively as we 
2772                          *        try to add the channels)
2773                          */
2774                         if ((filter_mode & ANIMFILTER_DATA_VISIBLE) && !(ads->filterflag & ADS_FILTER_INCL_HIDDEN)) {
2775                                 /* layer visibility - we check both object and base, since these may not be in sync yet */
2776                                 if ((sce->lay & (ob->lay | base->lay)) == 0) continue;
2777                                 
2778                                 /* outliner restrict-flag */
2779                                 if (ob->restrictflag & OB_RESTRICT_VIEW) continue;
2780                         }
2781                         
2782                         /* if only F-Curves with visible flags set can be shown, check that 
2783                          * datablock hasn't been set to invisible 
2784                          */
2785                         if (filter_mode & ANIMFILTER_CURVE_VISIBLE) {
2786                                 if ((ob->adt) && (ob->adt->flag & ADT_CURVES_NOT_VISIBLE))
2787                                         continue;
2788                         }
2789                         
2790                         /* check selection and object type filters */
2791                         if ( (ads->filterflag & ADS_FILTER_ONLYSEL) && !((base->flag & SELECT) /*|| (base == sce->basact)*/) ) {
2792                                 /* only selected should be shown */
2793                                 continue;
2794                         }
2795                         
2796