Animato - Restoring most of Action Editor
[blender.git] / source / blender / editors / animation / anim_filter.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. 
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2008 Blender Foundation.
21  * All rights reserved.
22  *
23  * 
24  * Contributor(s): Joshua Leung
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 /* This file contains a system used to provide a layer of abstraction between sources
30  * of animation data and tools in Animation Editors. The method used here involves 
31  * generating a list of edit structures which enable tools to naively perform the actions 
32  * they require without all the boiler-plate associated with loops within loops and checking 
33  * for cases to ignore. 
34  *
35  * While this is primarily used for the Action/Dopesheet Editor (and its accessory modes),
36  * the IPO Editor also uses this for it's channel list and for determining which curves
37  * are being edited.
38  *
39  * Note: much of the original system this was based on was built before the creation of the RNA
40  * system. In future, it would be interesting to replace some parts of this code with RNA queries,
41  * however, RNA does not eliminate some of the boiler-plate reduction benefits presented by this 
42  * system, so if any such work does occur, it should only be used for the internals used here...
43  *
44  * -- Joshua Leung, Dec 2008
45  */
46
47 #include <string.h>
48 #include <stdio.h>
49
50 #include "DNA_listBase.h"
51 #include "DNA_ID.h"
52 #include "DNA_anim_types.h"
53 #include "DNA_action_types.h"
54 #include "DNA_constraint_types.h"
55 #include "DNA_camera_types.h"
56 #include "DNA_curve_types.h"
57 #include "DNA_gpencil_types.h"
58 #include "DNA_ipo_types.h"
59 #include "DNA_lamp_types.h"
60 #include "DNA_lattice_types.h"
61 #include "DNA_key_types.h"
62 #include "DNA_material_types.h"
63 #include "DNA_mesh_types.h"
64 #include "DNA_object_types.h"
65 #include "DNA_space_types.h"
66 #include "DNA_scene_types.h"
67 #include "DNA_screen_types.h"
68 #include "DNA_windowmanager_types.h"
69
70 #include "MEM_guardedalloc.h"
71
72 #include "BLI_blenlib.h"
73
74 #include "BKE_context.h"
75 #include "BKE_global.h"
76 #include "BKE_key.h"
77 #include "BKE_object.h"
78 #include "BKE_material.h"
79 #include "BKE_screen.h"
80 #include "BKE_utildefines.h"
81
82 #include "ED_anim_api.h"
83 #include "ED_types.h"
84 #include "ED_util.h"
85
86 #include "WM_api.h"
87 #include "WM_types.h"
88
89 #include "BIF_gl.h"
90 #include "BIF_glutil.h"
91
92 #include "UI_interface.h"
93 #include "UI_resources.h"
94 #include "UI_view2d.h"
95
96 /* ************************************************************ */
97 /* Blender Context <-> Animation Context mapping */
98
99 /* ----------- Private Stuff - Action Editor ------------- */
100
101 /* Get shapekey data being edited (for Action Editor -> ShapeKey mode) */
102 /* Note: there's a similar function in key.c (ob_get_key) */
103 Key *actedit_get_shapekeys (bAnimContext *ac, SpaceAction *saction) 
104 {
105     Scene *scene= ac->scene;
106     Object *ob;
107     Key *key;
108         
109     ob = OBACT;
110     if (ob == NULL) 
111                 return NULL;
112         
113         /* XXX pinning is not available in 'ShapeKey' mode... */
114         //if (saction->pin) return NULL;
115         
116         /* shapekey data is stored with geometry data */
117         switch (ob->type) {
118                 case OB_MESH:
119                         key= ((Mesh *)ob->data)->key;
120                         break;
121                         
122                 case OB_LATTICE:
123                         key= ((Lattice *)ob->data)->key;
124                         break;
125                         
126                 case OB_CURVE:
127                 case OB_SURF:
128                         key= ((Curve *)ob->data)->key;
129                         break;
130                         
131                 default:
132                         return NULL;
133         }
134         
135         if (key) {
136                 if (key->type == KEY_RELATIVE)
137                         return key;
138         }
139         
140     return NULL;
141 }
142
143 /* Get data being edited in Action Editor (depending on current 'mode') */
144 static short actedit_get_context (bAnimContext *ac, SpaceAction *saction)
145 {
146         /* sync settings with current view status, then return appropriate data */
147         switch (saction->mode) {
148                 case SACTCONT_ACTION: /* 'Action Editor' */
149                         /* if not pinned, sync with active object */
150                         if (saction->pin == 0) {
151                                 if (ac->obact && ac->obact->adt)
152                                         saction->action = ac->obact->adt->action;
153                                 else
154                                         saction->action= NULL;
155                         }
156                         
157                         ac->datatype= ANIMCONT_ACTION;
158                         ac->data= saction->action;
159                         
160                         ac->mode= saction->mode;
161                         return 1;
162                         
163                 case SACTCONT_SHAPEKEY: /* 'ShapeKey Editor' */
164                         ac->datatype= ANIMCONT_SHAPEKEY;
165                         ac->data= actedit_get_shapekeys(ac, saction);
166                         
167                         ac->mode= saction->mode;
168                         return 1;
169                         
170                 case SACTCONT_GPENCIL: /* Grease Pencil */ // XXX review how this mode is handled...
171                         ac->datatype=ANIMCONT_GPENCIL;
172                         //ac->data= CTX_wm_screen(C); // FIXME: add that dopesheet type thing here!
173                         ac->data= NULL; // !!!
174                         
175                         ac->mode= saction->mode;
176                         return 1;
177                         
178                 case SACTCONT_DOPESHEET: /* DopeSheet */
179                         /* update scene-pointer (no need to check for pinning yet, as not implemented) */
180                         saction->ads.source= (ID *)ac->scene;
181                         
182                         ac->datatype= ANIMCONT_DOPESHEET;
183                         ac->data= &saction->ads;
184                         
185                         ac->mode= saction->mode;
186                         return 1;
187                 
188                 default: /* unhandled yet */
189                         ac->datatype= ANIMCONT_NONE;
190                         ac->data= NULL;
191                         
192                         ac->mode= -1;
193                         return 0;
194         }
195 }
196
197 /* ----------- Private Stuff - IPO Editor ------------- */
198
199 /* Get data being edited in IPO Editor (depending on current 'mode') */
200 static short ipoedit_get_context (bAnimContext *ac, SpaceIpo *sipo)
201 {
202         // XXX FIXME...
203         return 0;
204 }
205
206 /* ----------- Public API --------------- */
207
208 /* Obtain current anim-data context, given that context info from Blender context has already been set 
209  *      - AnimContext to write to is provided as pointer to var on stack so that we don't have
210  *        allocation/freeing costs (which are not that avoidable with channels).
211  */
212 short ANIM_animdata_context_getdata (bAnimContext *ac)
213 {
214         ScrArea *sa= ac->sa;
215         short ok= 0;
216         
217         /* context depends on editor we are currently in */
218         if (sa) {
219                 switch (sa->spacetype) {
220                         case SPACE_ACTION:
221                         {
222                                 SpaceAction *saction= (SpaceAction *)sa->spacedata.first;
223                                 ok= actedit_get_context(ac, saction);
224                         }
225                                 break;
226                                 
227                         case SPACE_IPO:
228                         {
229                                 SpaceIpo *sipo= (SpaceIpo *)sa->spacedata.first;
230                                 ok= ipoedit_get_context(ac, sipo);
231                         }
232                                 break;
233                 }
234         }
235         
236         /* check if there's any valid data */
237         if (ok && ac->data)
238                 return 1;
239         else
240                 return 0;
241 }
242
243 /* Obtain current anim-data context from Blender Context info 
244  *      - AnimContext to write to is provided as pointer to var on stack so that we don't have
245  *        allocation/freeing costs (which are not that avoidable with channels).
246  *      - Clears data and sets the information from Blender Context which is useful
247  */
248 short ANIM_animdata_get_context (const bContext *C, bAnimContext *ac)
249 {
250         ScrArea *sa= CTX_wm_area(C);
251         ARegion *ar= CTX_wm_region(C);
252         Scene *scene= CTX_data_scene(C);
253         
254         /* clear old context info */
255         if (ac == NULL) return 0;
256         memset(ac, 0, sizeof(bAnimContext));
257         
258         /* get useful default context settings from context */
259         ac->scene= scene;
260         ac->obact= (scene && scene->basact)?  scene->basact->object : NULL;
261         ac->sa= sa;
262         ac->ar= ar;
263         ac->spacetype= (sa) ? sa->spacetype : 0;
264         ac->regiontype= (ar) ? ar->regiontype : 0;
265         
266         /* get data context info */
267         return ANIM_animdata_context_getdata(ac);
268 }
269
270 /* ************************************************************ */
271 /* Blender Data <-- Filter --> Channels to be operated on */
272
273 /* quick macro to test if AnimData is usable */
274 #define ANIMDATA_HAS_KEYS(id) ((id)->adt && (id)->adt->action)
275
276 /* ----------- 'Private' Stuff --------------- */
277
278 /* this function allocates memory for a new bAnimListElem struct for the 
279  * provided animation channel-data. 
280  */
281 bAnimListElem *make_new_animlistelem (void *data, short datatype, void *owner, short ownertype)
282 {
283         bAnimListElem *ale= NULL;
284         
285         /* only allocate memory if there is data to convert */
286         if (data) {
287                 /* allocate and set generic data */
288                 ale= MEM_callocN(sizeof(bAnimListElem), "bAnimListElem");
289                 
290                 ale->data= data;
291                 ale->type= datatype;
292                 ale->owner= owner;
293                 ale->ownertype= ownertype;
294                 
295                 if ((owner) && (ownertype == ANIMTYPE_FCURVE)) {
296                         FCurve *ofcu= (FCurve *)owner;
297                         ale->grp= ofcu->grp;
298                 }
299                 else 
300                         ale->grp= NULL;
301                 
302                 /* do specifics */
303                 switch (datatype) {
304                         case ANIMTYPE_OBJECT:
305                         {
306                                 Base *base= (Base *)data;
307                                 Object *ob= base->object;
308                                 
309                                 ale->flag= ob->flag;
310                                 
311                                 ale->key_data= ob;
312                                 ale->datatype= ALE_OB;
313                         }
314                                 break;
315                         case ANIMTYPE_FILLACTD:
316                         {
317                                 bAction *act= (bAction *)data;
318                                 
319                                 ale->flag= act->flag;
320                                 
321                                 ale->key_data= act;
322                                 ale->datatype= ALE_ACT;
323                         }
324                                 break;
325                         case ANIMTYPE_FILLMATD:
326                         {
327                                 Object *ob= (Object *)data;
328                                 
329                                 ale->flag= FILTER_MAT_OBJC(ob);
330                                 
331                                 ale->key_data= NULL;
332                                 ale->datatype= ALE_NONE;
333                         }
334                                 break;
335                         
336                         case ANIMTYPE_DSMAT:
337                         {
338                                 Material *ma= (Material *)data;
339                                 AnimData *adt= ma->adt;
340                                 
341                                 ale->flag= FILTER_MAT_OBJD(ma);
342                                 
343                                 ale->key_data= adt->action;
344                                 ale->datatype= ALE_ACT;
345                         }
346                                 break;
347                         case ANIMTYPE_DSLAM:
348                         {
349                                 Lamp *la= (Lamp *)data;
350                                 AnimData *adt= la->adt;
351                                 
352                                 ale->flag= FILTER_LAM_OBJD(la);
353                                 
354                                 ale->key_data= adt->action;
355                                 ale->datatype= ALE_ACT;
356                         }
357                                 break;
358                         case ANIMTYPE_DSCAM:
359                         {
360                                 Camera *ca= (Camera *)data;
361                                 AnimData *adt= ca->adt;
362                                 
363                                 ale->flag= FILTER_CAM_OBJD(ca);
364                                 
365                                 ale->key_data= adt->action;
366                                 ale->datatype= ALE_ACT;
367                         }
368                                 break;
369                         case ANIMTYPE_DSCUR:
370                         {
371                                 Curve *cu= (Curve *)data;
372                                 AnimData *adt= cu->adt;
373                                 
374                                 ale->flag= FILTER_CUR_OBJD(cu);
375                                 
376                                 ale->key_data= adt->action;
377                                 ale->datatype= ALE_ACT;
378                         }
379                                 break;
380                         case ANIMTYPE_DSSKEY:
381                         {
382                                 Key *key= (Key *)data;
383                                 AnimData *adt= key->adt;
384                                 
385                                 ale->flag= FILTER_SKE_OBJD(key); 
386                                 
387                                 ale->key_data= adt->action;
388                                 ale->datatype= ALE_ACT;
389                         }
390                                 break;
391                                 
392                         case ANIMTYPE_GROUP:
393                         {
394                                 bActionGroup *agrp= (bActionGroup *)data;
395                                 
396                                 ale->flag= agrp->flag;
397                                 
398                                 ale->key_data= NULL;
399                                 ale->datatype= ALE_GROUP;
400                         }
401                                 break;
402                         case ANIMTYPE_FCURVE:
403                         {
404                                 FCurve *fcu= (FCurve *)data;
405                                 
406                                 ale->flag= fcu->flag;
407                                 
408                                 ale->key_data= fcu;
409                                 ale->datatype= ALE_FCURVE;
410                         }
411                                 break;
412                         
413                         case ANIMTYPE_GPLAYER:
414                         {
415                                 bGPDlayer *gpl= (bGPDlayer *)data;
416                                 
417                                 ale->flag= gpl->flag;
418                                 
419                                 ale->key_data= NULL;
420                                 ale->datatype= ALE_GPFRAME;
421                         }
422                                 break;
423                 }
424         }
425         
426         /* return created datatype */
427         return ale;
428 }
429  
430 /* ----------------------------------------- */
431
432
433 static int animdata_filter_fcurves (ListBase *anim_data, FCurve *first, bActionGroup *grp, int filter_mode, ID *owner_id)
434 {
435         bAnimListElem *ale = NULL;
436         FCurve *fcu;
437         int items = 0;
438         
439         /* loop over F-Curves - assume that the caller of this has already checked that these should be included 
440          * NOTE: we need to check if the F-Curves belong to the same group, as this gets called for groups too...
441          */
442         for (fcu= first; ((fcu) && (fcu->grp==grp)); fcu= fcu->next) {
443                 /* only work with this channel and its subchannels if it is editable */
444                 if (!(filter_mode & ANIMFILTER_FOREDIT) || EDITABLE_FCU(fcu)) {
445                         /* only include this curve if selected */
446                         if (!(filter_mode & ANIMFILTER_SEL) || (SEL_FCU(fcu))) {
447                                 /* owner/ownertype will be either object or action-channel, depending if it was dopesheet or part of an action */
448                                 ale= make_new_animlistelem(fcu, ANIMTYPE_FCURVE, fcu, ANIMTYPE_FCURVE);
449                                 
450                                 if (ale) {
451                                         /* ID will only be Object if data to write to directly belongs there, otherwise, another pointer will be used */
452                                         ale->id= owner_id;
453                                         BLI_addtail(anim_data, ale);
454                                         items++;
455                                 }
456                         }
457                 }
458         }
459         
460         /* return the number of items added to the list */
461         return items;
462 }
463
464 static int animdata_filter_action (ListBase *anim_data, bAction *act, int filter_mode, void *owner, short ownertype)
465 {
466         bAnimListElem *ale=NULL;
467         bActionGroup *agrp;
468         FCurve *lastchan=NULL;
469         short owned= (owner && ownertype) ? 1 : 0;
470         int items = 0;
471         
472         /* loop over groups */
473         //       XXX in future, we need to be prepared for nestled groups...
474         for (agrp= act->groups.first; agrp; agrp= agrp->next) {
475                 /* add this group as a channel first */
476                 if ((filter_mode & ANIMFILTER_CHANNELS) || !(filter_mode & ANIMFILTER_CURVESONLY)) {
477                         /* check if filtering by selection */
478                         if ( !(filter_mode & ANIMFILTER_SEL) || SEL_AGRP(agrp) ) {
479                                 ale= make_new_animlistelem(agrp, ANIMTYPE_GROUP, NULL, ANIMTYPE_NONE);
480                                 if (ale) {
481                                         if (owned) ale->id= owner;
482                                         BLI_addtail(anim_data, ale);
483                                         items++;
484                                 }
485                         }
486                 }
487                 
488                 /* store reference to last channel of group */
489                 if (agrp->channels.last) 
490                         lastchan= agrp->channels.last;
491                 
492                 
493                 /* there are some situations, where only the channels of the animive group should get considered */
494                 if (!(filter_mode & ANIMFILTER_ACTGROUPED) || (agrp->flag & AGRP_ACTIVE)) {
495                         /* filters here are a bit convoulted...
496                          *      - groups show a "summary" of keyframes beside their name which must accessable for tools which handle keyframes
497                          *      - groups can be collapsed (and those tools which are only interested in channels rely on knowing that group is closed)
498                          *
499                          * cases when we should include F-Curves inside group:
500                          *      - we don't care about visibility
501                          *      - group is expanded
502                          *      - we're interested in keyframes, but not if they appear in selected channels
503                          */
504                         if ( (!(filter_mode & ANIMFILTER_VISIBLE) || EXPANDED_AGRP(agrp)) || 
505                                  ( (!(filter_mode & ANIMFILTER_SEL) || (SEL_AGRP(agrp))) && 
506                                    (filter_mode & ANIMFILTER_CURVESONLY) ) ) 
507                         {
508                                 if (!(filter_mode & ANIMFILTER_FOREDIT) || EDITABLE_AGRP(agrp)) {
509                                         // XXX the 'owner' info here needs review...
510                                         items += animdata_filter_fcurves(anim_data, agrp->channels.first, agrp, filter_mode, ((owned)?owner:NULL));
511                                         
512                                         /* remove group from filtered list if last element is group 
513                                          * (i.e. only if group had channels, which were all hidden)
514                                          */
515                                         // XXX this is really hacky... it should be fixed in a much more elegant way!
516                                         if ( (ale) && (anim_data->last == ale) && 
517                                                  (ale->data == agrp) && (agrp->channels.first) ) 
518                                         {
519                                                 BLI_freelinkN(anim_data, ale);
520                                                 items--;
521                                         }
522                                 }
523                         }
524                 }
525         }
526         
527         /* loop over un-grouped F-Curves (only if we're not only considering those channels in the animive group) */
528         if (!(filter_mode & ANIMFILTER_ACTGROUPED))  {
529                 // XXX the 'owner' info here needs review...
530                 items += animdata_filter_fcurves(anim_data, (lastchan)?(lastchan->next):(act->curves.first), NULL, filter_mode, ((owned)?owner:NULL));
531         }
532         
533         /* return the number of items added to the list */
534         return items;
535 }
536
537 static int animdata_filter_shapekey (ListBase *anim_data, Key *key, int filter_mode, void *owner, short ownertype)
538 {
539         bAnimListElem *ale;
540         KeyBlock *kb;
541         //FCurve *fcu;
542         short owned= (owner && ownertype)? 1 : 0;
543         int i, items=0;
544         
545         /* are we filtering for display or editing */
546         if (filter_mode & ANIMFILTER_CHANNELS) {
547                 /* for display - loop over shapekeys, adding ipo-curve references where needed */
548                 kb= key->block.first;
549                 
550                 /* loop through possible shapekeys, manually creating entries */
551                 for (i= 1; i < key->totkey; i++) {
552                         ale= MEM_callocN(sizeof(bAnimListElem), "bAnimListElem");
553                         kb = kb->next; /* do this even on the first try, as the first is 'Basis' (which doesn't get included) */
554                         
555                         ale->data= kb;
556                         ale->type= ANIMTYPE_SHAPEKEY; /* 'abused' usage of this type */
557                         ale->owner= key;
558                         ale->ownertype= ANIMTYPE_SHAPEKEY;
559                         ale->datatype= ALE_NONE;
560                         ale->index = i;
561                         
562 #if 0 // XXX fixme... old system
563                         if (key->ipo) {
564                                 for (icu= key->ipo->curve.first; icu; icu=icu->next) {
565                                         if (icu->adrcode == i) {
566                                                 ale->key_data= icu;
567                                                 ale->datatype= ALE_ICU;
568                                                 break;
569                                         }
570                                 }
571                         }
572 #endif // XXX fixme... old system
573                         
574                         if (owned) ale->id= owner;
575                         
576                         BLI_addtail(anim_data, ale);
577                         items++;
578                 }
579         }
580         else {
581 #if 0 // XXX fixme... old system
582                 /* loop over ipo curves if present - for editing */
583                 if (key->ipo) {
584                         if (filter_mode & ANIMFILTER_IPOKEYS) {
585                                 ale= make_new_animlistelem(key->ipo, ANIMTYPE_IPO, key, ANIMTYPE_SHAPEKEY);
586                                 if (ale) {
587                                         if (owned) ale->id= owner;
588                                         BLI_addtail(anim_data, ale);
589                                         items++;
590                                 }
591                         }
592                         else {
593                                 items += animdata_filter_ipocurves(anim_data, key->ipo, filter_mode, key, ANIMTYPE_SHAPEKEY, (owned)?(owner):(NULL));
594                         }
595                 }
596 #endif // XXX fixme... old system
597         }
598         
599         /* return the number of items added to the list */
600         return items;
601 }
602  
603 #if 0
604 // FIXME: switch this to use the bDopeSheet...
605 static int animdata_filter_gpencil (ListBase *anim_data, bScreen *sc, int filter_mode)
606 {
607         bAnimListElem *ale;
608         ScrArea *sa, *curarea;
609         bGPdata *gpd;
610         bGPDlayer *gpl;
611         int items = 0;
612         
613         /* check if filtering types are appropriate */
614         {
615                 /* special hack for fullscreen area (which must be this one then):
616                  *      - we use the curarea->full as screen to get spaces from, since the
617                  *        old (pre-fullscreen) screen was stored there...
618                  *      - this is needed as all data would otherwise disappear
619                  */
620                 // XXX need to get new alternative for curarea
621                 if ((curarea->full) && (curarea->spacetype==SPACE_ACTION))
622                         sc= curarea->full;
623                 
624                 /* loop over spaces in current screen, finding gpd blocks (could be slow!) */
625                 for (sa= sc->areabase.first; sa; sa= sa->next) {
626                         /* try to get gp data */
627                         // XXX need to put back grease pencil api...
628                         gpd= gpencil_data_getactive(sa);
629                         if (gpd == NULL) continue;
630                         
631                         /* add gpd as channel too (if for drawing, and it has layers) */
632                         if ((filter_mode & ANIMFILTER_CHANNELS) && (gpd->layers.first)) {
633                                 /* add to list */
634                                 ale= make_new_animlistelem(gpd, ANIMTYPE_GPDATABLOCK, sa, ANIMTYPE_SPECIALDATA);
635                                 if (ale) {
636                                         BLI_addtail(anim_data, ale);
637                                         items++;
638                                 }
639                         }
640                         
641                         /* only add layers if they will be visible (if drawing channels) */
642                         if ( !(filter_mode & ANIMFILTER_VISIBLE) || (EXPANDED_GPD(gpd)) ) {
643                                 /* loop over layers as the conditions are acceptable */
644                                 for (gpl= gpd->layers.first; gpl; gpl= gpl->next) {
645                                         /* only if selected */
646                                         if (!(filter_mode & ANIMFILTER_SEL) || SEL_GPL(gpl)) {
647                                                 /* only if editable */
648                                                 if (!(filter_mode & ANIMFILTER_FOREDIT) || EDITABLE_GPL(gpl)) {
649                                                         /* add to list */
650                                                         ale= make_new_animlistelem(gpl, ANIMTYPE_GPLAYER, gpd, ANIMTYPE_GPDATABLOCK);
651                                                         if (ale) {
652                                                                 BLI_addtail(anim_data, ale);
653                                                                 items++;
654                                                         }
655                                                 }
656                                         }
657                                 }
658                         }
659                 }
660         }
661         
662         /* return the number of items added to the list */
663         return items;
664 }
665 #endif 
666
667 #if 0 // XXX old anim sys
668 static int animdata_filter_dopesheet_mats (ListBase *anim_data, bDopeSheet *ads, Base *base, int filter_mode)
669 {
670         bAnimListElem *ale=NULL;
671         Object *ob= base->object;
672         int items = 0;
673         
674         /* include materials-expand widget? */
675         if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & (ANIMFILTER_IPOKEYS|ANIMFILTER_ONLYFCU))) {
676                 ale= make_new_animlistelem(ob, ANIMTYPE_FILLMATD, base, ANIMTYPE_OBJECT);
677                 if (ale) {
678                         BLI_addtail(anim_data, ale);
679                         items++;
680                 }
681         }
682         
683         /* add materials? */
684         if (FILTER_MAT_OBJC(ob) || (filter_mode & ANIMFILTER_IPOKEYS) || (filter_mode & ANIMFILTER_ONLYFCU)) {
685                 short a;
686                 
687                 /* for each material, either add channels separately, or as ipo-block */
688                 for (a=0; a<ob->totcol; a++) {
689                         Material *ma= give_current_material(ob, a);
690                         
691                         /* for now, if no material returned, skip (this shouldn't confuse the user I hope) */
692                         if (ELEM(NULL, ma, ma->ipo)) continue;
693                         
694                         /* include material-expand widget? */
695                         // hmm... do we need to store the index of this material in the array anywhere?
696                         if (filter_mode & (ANIMFILTER_CHANNELS|ANIMFILTER_IPOKEYS)) {
697                                 ale= make_new_animlistelem(ma, ANIMTYPE_DSMAT, base, ANIMTYPE_OBJECT);
698                                 if (ale) {
699                                         BLI_addtail(anim_data, ale);
700                                         items++;
701                                 }
702                         }
703                         
704                         /* add material's ipo-curve channels? */
705                         if ( (FILTER_MAT_OBJD(ma) || (filter_mode & ANIMFILTER_ONLYFCU)) && 
706                                   !(filter_mode & ANIMFILTER_IPOKEYS) ) 
707                         {
708                                 items += animdata_filter_ipocurves(anim_data, ma->ipo, filter_mode, base, ANIMTYPE_OBJECT, (ID *)ma);
709                         }
710                 }
711
712         }
713         
714         /* return the number of items added to the list */
715         return items;
716 }
717
718 static int animdata_filter_dopesheet_cam (ListBase *anim_data, bDopeSheet *ads, Base *base, int filter_mode)
719 {
720         bAnimListElem *ale=NULL;
721         Object *ob= base->object;
722         Camera *ca= (Camera *)ob->data;
723         int items = 0;
724         
725         /* include camera-expand widget? */
726         if (filter_mode & (ANIMFILTER_CHANNELS|ANIMFILTER_IPOKEYS)) {
727                 ale= make_new_animlistelem(ca, ANIMTYPE_DSCAM, base, ANIMTYPE_OBJECT);
728                 if (ale) {
729                         BLI_addtail(anim_data, ale);
730                         items++;
731                 }
732         }
733         
734         /* add camera ipo-curve channels? */
735         if ( (FILTER_CAM_OBJD(ca) || (filter_mode & ANIMFILTER_ONLYFCU)) && 
736                   !(filter_mode & ANIMFILTER_IPOKEYS) ) 
737         {
738                 items += animdata_filter_ipocurves(anim_data, ca->ipo, filter_mode, base, ANIMTYPE_OBJECT, (ID *)ca);
739         }
740         
741         /* return the number of items added to the list */
742         return items;
743 }
744
745 static int animdata_filter_dopesheet_lamp (ListBase *anim_data, bDopeSheet *ads, Base *base, int filter_mode)
746 {
747         bAnimListElem *ale=NULL;
748         Object *ob= base->object;
749         Lamp *la= (Lamp *)ob->data;
750         int items = 0;
751         
752         /* include lamp-expand widget? */
753         if (filter_mode & (ANIMFILTER_CHANNELS|ANIMFILTER_IPOKEYS)) {
754                 ale= make_new_animlistelem(la, ANIMTYPE_DSLAM, base, ANIMTYPE_OBJECT);
755                 if (ale) {
756                         BLI_addtail(anim_data, ale);
757                         items++;
758                 }
759         }
760         
761         /* add lamp ipo-curve channels? */
762         if ( (FILTER_LAM_OBJD(la) || (filter_mode & ANIMFILTER_ONLYFCU)) && 
763                   !(filter_mode & ANIMFILTER_IPOKEYS) ) 
764         {
765                 items += animdata_filter_ipocurves(anim_data, la->ipo, filter_mode, base, ANIMTYPE_OBJECT, (ID *)la);
766         }
767         
768         /* return the number of items added to the list */
769         return items;
770 }
771
772 static int animdata_filter_dopesheet_curve (ListBase *anim_data, bDopeSheet *ads, Base *base, int filter_mode)
773 {
774         bAnimListElem *ale=NULL;
775         Object *ob= base->object;
776         Curve *cu= (Curve *)ob->data;
777         int items = 0;
778         
779         /* include curve-expand widget? */
780         if (filter_mode & (ANIMFILTER_CHANNELS|ANIMFILTER_IPOKEYS)) {
781                 ale= make_new_animlistelem(cu, ANIMTYPE_DSCUR, base, ANIMTYPE_OBJECT);
782                 if (ale) {
783                         BLI_addtail(anim_data, ale);
784                         items++;
785                 }
786         }
787         
788         /* add curve ipo-curve channels? */
789         if ( (FILTER_CUR_OBJD(cu) || (filter_mode & ANIMFILTER_ONLYFCU)) && 
790                   !(filter_mode & ANIMFILTER_IPOKEYS) ) 
791         {
792                 items += animdata_filter_ipocurves(anim_data, cu->ipo, filter_mode, base, ANIMTYPE_OBJECT, (ID *)cu);
793         }
794         
795         /* return the number of items added to the list */
796         return items;
797 }
798 #endif // XXX old anim sys
799
800 static int animdata_filter_dopesheet_ob (ListBase *anim_data, bDopeSheet *ads, Base *base, int filter_mode)
801 {
802         bAnimListElem *ale=NULL;
803         Scene *sce= (Scene *)ads->source;
804         Object *ob= base->object;
805 //      Key *key= ob_get_key(ob);
806         int items = 0;
807         
808         /* add this object as a channel first */
809         if ((filter_mode & ANIMFILTER_CURVESONLY) == 0) {
810                 /* check if filtering by selection */
811                 if ( !(filter_mode & ANIMFILTER_SEL) || ((base->flag & SELECT) || (base == sce->basact)) ) {
812                         ale= make_new_animlistelem(base, ANIMTYPE_OBJECT, NULL, ANIMTYPE_NONE);
813                         if (ale) {
814                                 BLI_addtail(anim_data, ale);
815                                 items++;
816                         }
817                 }
818         }
819         
820         /* if collapsed, don't go any further (unless adding keyframes only) */
821         if ( (EXPANDED_OBJC(ob) == 0) && !(filter_mode & ANIMFILTER_CURVESONLY) )
822                 return items;
823         
824         /* Action? */
825         if (ANIMDATA_HAS_KEYS(ob) /*&& !(ads->filterflag & ADS_FILTER_NOACTS)*/) {
826                 AnimData *adt= ob->adt;
827                 
828                 /* include action-expand widget? */
829                 if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & (ANIMFILTER_CURVESONLY))) {
830                         ale= make_new_animlistelem(adt->action, ANIMTYPE_FILLACTD, base, ANIMTYPE_OBJECT);
831                         if (ale) {
832                                 ale->id= (ID *)ob; // err.... is this a good idea?
833                                 BLI_addtail(anim_data, ale);
834                                 items++;
835                         }
836                 }
837                 
838                 /* add F-Curve channels? */
839                 if (EXPANDED_ACTC(adt->action) || !(filter_mode & ANIMFILTER_CHANNELS)) {
840                         // need to make the ownertype normal object here... (maybe type should be a separate one for clarity?)
841                         items += animdata_filter_action(anim_data, adt->action, filter_mode, ob, ANIMTYPE_OBJECT); 
842                 }
843         }
844         
845 #if 0 // XXX fixme... 
846         /* ShapeKeys? */
847         if ((key) && !(ads->filterflag & ADS_FILTER_NOSHAPEKEYS)) {
848                 /* include shapekey-expand widget? */
849                 if ((filter_mode & ANIMFILTER_CHANNELS) && !(filter_mode & (ANIMFILTER_IPOKEYS|ANIMFILTER_ONLYFCU))) {
850                         ale= make_new_animlistelem(key, ANIMTYPE_DSSKEY, base, ANIMTYPE_OBJECT);
851                         if (ale) {
852                                 BLI_addtail(anim_data, ale);
853                                 items++;
854                         }
855                 }
856                 
857                 /* add channels */
858                 if (FILTER_SKE_OBJD(key) || (filter_mode & ANIMFILTER_IPOKEYS) || (filter_mode & ANIMFILTER_ONLYFCU)) {
859                         items += animdata_filter_shapekey(anim_data, key, filter_mode, ob, ANIMTYPE_OBJECT);
860                 }
861         }
862         
863         /* Materials? */
864         if ((ob->totcol) && !(ads->filterflag & ADS_FILTER_NOMAT))
865                 items += animdata_filter_dopesheet_mats(anim_data, ads, base, filter_mode);
866         
867         /* Object Data */
868         switch (ob->type) {
869                 case OB_CAMERA: /* ------- Camera ------------ */
870                 {
871                         Camera *ca= (Camera *)ob->data;
872                         if ((ca->ipo) && !(ads->filterflag & ADS_FILTER_NOCAM))
873                                 items += animdata_filter_dopesheet_cam(anim_data, ads, base, filter_mode);
874                 }
875                         break;
876                 case OB_LAMP: /* ---------- Lamp ----------- */
877                 {
878                         Lamp *la= (Lamp *)ob->data;
879                         if ((la->ipo) && !(ads->filterflag & ADS_FILTER_NOLAM))
880                                 items += animdata_filter_dopesheet_lamp(anim_data, ads, base, filter_mode);
881                 }
882                         break;
883                 case OB_CURVE: /* ------- Curve ---------- */
884                 {
885                         Curve *cu= (Curve *)ob->data;
886                         if ((cu->ipo) && !(ads->filterflag & ADS_FILTER_NOCUR))
887                                 items += animdata_filter_dopesheet_curve(anim_data, ads, base, filter_mode);
888                 }
889                         break;
890         }
891 #endif // XXX fixme...
892         
893         /* return the number of items added to the list */
894         return items;
895 }       
896
897 // 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)
898 static int animdata_filter_dopesheet (ListBase *anim_data, bDopeSheet *ads, int filter_mode)
899 {
900         Scene *sce= (Scene *)ads->source;
901         Base *base;
902         int items = 0;
903         
904         /* check that we do indeed have a scene */
905         if ((ads->source == NULL) || (GS(ads->source->name)!=ID_SCE)) {
906                 printf("DopeSheet Error: Not scene! \n");
907                 return 0;
908         }
909         
910         /* loop over all bases in the scene */
911         for (base= sce->base.first; base; base= base->next) {
912                 /* check if there's an object (all the relevant checks are done in the ob-function) */
913                 if (base->object) {
914                         Object *ob= base->object;
915                         Key *key= ob_get_key(ob);
916                         short actOk, keyOk, dataOk;
917                         
918                         /* firstly, check if object can be included, by the following fanimors:
919                          *      - if only visible, must check for layer and also viewport visibility
920                          *      - if only selected, must check if object is selected 
921                          *      - there must be animation data to edit
922                          */
923                         // TODO: if cache is implemented, just check name here, and then 
924                         if (filter_mode & ANIMFILTER_VISIBLE) {
925                                 /* layer visibility */
926                                 if ((ob->lay & sce->lay)==0) continue;
927                                 
928                                 /* outliner restrict-flag */
929                                 if (ob->restrictflag & OB_RESTRICT_VIEW) continue;
930                         }
931                         
932                         /* additionally, dopesheet filtering also affects what objects to consider */
933                         if (ads->filterflag) {
934                                 /* check selection and object type filters */
935                                 if ( (ads->filterflag & ADS_FILTER_ONLYSEL) && !((base->flag & SELECT) || (base == sce->basact)) )  {
936                                         /* only selected should be shown */
937                                         continue;
938                                 }
939                                 if ((ads->filterflag & ADS_FILTER_NOARM) && (ob->type == OB_ARMATURE)) {
940                                         /* not showing armatures  */
941                                         continue;
942                                 }
943                                 if ((ads->filterflag & ADS_FILTER_NOOBJ) && (ob->type != OB_ARMATURE)) {
944                                         /* not showing objects that aren't armatures */
945                                         continue;
946                                 }
947                                 
948                                 /* check filters for datatypes */
949                                 actOk= (ANIMDATA_HAS_KEYS(ob) /*&& !(ads->filterflag & ADS_FILTER_NOACTS)*/);
950                                 keyOk= ((key) && !(ads->filterflag & ADS_FILTER_NOSHAPEKEYS));
951                                 
952                                 switch (ob->type) {
953                                         case OB_CAMERA: /* ------- Camera ------------ */
954                                         {
955                                                 Camera *ca= (Camera *)ob->data;
956                                                 dataOk= (ANIMDATA_HAS_KEYS(ca) && !(ads->filterflag & ADS_FILTER_NOCAM));                                               
957                                         }
958                                                 break;
959                                         case OB_LAMP: /* ---------- Lamp ----------- */
960                                         {
961                                                 Lamp *la= (Lamp *)ob->data;
962                                                 dataOk= (ANIMDATA_HAS_KEYS(la) && !(ads->filterflag & ADS_FILTER_NOLAM));
963                                         }
964                                                 break;
965                                         case OB_CURVE: /* -------- Curve ---------- */
966                                         {
967                                                 Curve *cu= (Curve *)ob->data;
968                                                 dataOk= (ANIMDATA_HAS_KEYS(cu) && !(ads->filterflag & ADS_FILTER_NOCUR));
969                                         }
970                                                 break;
971                                         default: /* --- other --- */
972                                                 dataOk= 0;
973                                                 break;
974                                 }
975                                 
976                                 /* check if all bad (i.e. nothing to show) */
977                                 if (!actOk && !keyOk && !dataOk)
978                                         continue;
979                         }
980                         else {
981                                 /* check data-types */
982                                 actOk= ANIMDATA_HAS_KEYS(ob);
983                                 keyOk= (key != NULL);
984                                 
985                                 switch (ob->type) {
986                                         case OB_CAMERA: /* ------- Camera ------------ */
987                                         {
988                                                 Camera *ca= (Camera *)ob->data;
989                                                 dataOk= ANIMDATA_HAS_KEYS(ca);                                          
990                                         }
991                                                 break;
992                                         case OB_LAMP: /* ---------- Lamp ----------- */
993                                         {
994                                                 Lamp *la= (Lamp *)ob->data;
995                                                 dataOk= ANIMDATA_HAS_KEYS(la);
996                                         }
997                                                 break;
998                                         case OB_CURVE: /* -------- Curve ---------- */
999                                         {
1000                                                 Curve *cu= (Curve *)ob->data;
1001                                                 dataOk= ANIMDATA_HAS_KEYS(cu);
1002                                         }
1003                                                 break;
1004                                         default: /* --- other --- */
1005                                                 dataOk= 0;
1006                                                 break;
1007                                 }
1008                                 
1009                                 /* check if all bad (i.e. nothing to show) */
1010                                 if (!actOk && !keyOk && !dataOk)
1011                                         continue;
1012                         }
1013                         
1014                         /* since we're still here, this object should be usable */
1015                         items += animdata_filter_dopesheet_ob(anim_data, ads, base, filter_mode);
1016                 }
1017         }
1018         
1019         /* return the number of items in the list */
1020         return items;
1021 }
1022
1023 /* ----------- Public API --------------- */
1024
1025 /* This function filters the active data source to leave only animation channels suitable for
1026  * usage by the caller. It will return the length of the list 
1027  * 
1028  *      *act_data: is a pointer to a ListBase, to which the filtered animation channels
1029  *              will be placed for use.
1030  *      filter_mode: how should the data be filtered - bitmapping accessed flags
1031  */
1032 int ANIM_animdata_filter (ListBase *anim_data, int filter_mode, void *data, short datatype)
1033 {
1034         int items = 0;
1035         
1036         /* only filter data if there's somewhere to put it */
1037         if (data && anim_data) {
1038                 bAnimListElem *ale, *next;
1039                 
1040                 /* firstly filter the data */
1041                 switch (datatype) {
1042                         case ANIMCONT_ACTION:
1043                                 items= animdata_filter_action(anim_data, data, filter_mode, NULL, ANIMTYPE_NONE);
1044                                 break;
1045                         case ANIMCONT_SHAPEKEY:
1046                                 items= animdata_filter_shapekey(anim_data, data, filter_mode, NULL, ANIMTYPE_NONE);
1047                                 break;
1048                         case ANIMCONT_GPENCIL:
1049                                 //items= animdata_filter_gpencil(anim_data, data, filter_mode);
1050                                 break;
1051                         case ANIMCONT_DOPESHEET:
1052                                 items= animdata_filter_dopesheet(anim_data, data, filter_mode);
1053                                 break;
1054                                 
1055                         case ANIMCONT_IPO:
1056                                 // FIXME: this will be used for showing a single IPO-block (not too useful from animator perspective though!)
1057                                 //items= 0;
1058                                 break;
1059                 }
1060                         
1061                 /* remove any weedy entries */
1062                 // XXX this is weedy code!
1063                 for (ale= anim_data->first; ale; ale= next) {
1064                         next= ale->next;
1065                         
1066                         if (ale->type == ANIMTYPE_NONE) {
1067                                 items--;
1068                                 BLI_freelinkN(anim_data, ale);
1069                         }
1070                 }
1071         }
1072         
1073         /* return the number of items in the list */
1074         return items;
1075 }
1076
1077 /* ************************************************************ */