NLA/AnimEditors: Added operator to remove all "empty" AnimData blocks
[blender.git] / source / blender / editors / animation / anim_channels_edit.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) 2009 Blender Foundation, Joshua Leung
19  * All rights reserved.
20  *
21  * Contributor(s): Joshua Leung
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/editors/animation/anim_channels_edit.c
27  *  \ingroup edanimation
28  */
29
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h> 
34
35 #include "MEM_guardedalloc.h"
36
37 #include "BLI_blenlib.h"
38 #include "BLI_utildefines.h"
39 #include "BLI_listbase.h"
40 #include "BKE_library.h"
41
42 #include "DNA_anim_types.h"
43 #include "DNA_object_types.h"
44 #include "DNA_scene_types.h"
45 #include "DNA_key_types.h"
46 #include "DNA_gpencil_types.h"
47 #include "DNA_mask_types.h"
48
49 #include "RNA_access.h"
50 #include "RNA_define.h"
51
52 #include "BKE_animsys.h"
53 #include "BKE_action.h"
54 #include "BKE_fcurve.h"
55 #include "BKE_gpencil.h"
56 #include "BKE_context.h"
57 #include "BKE_mask.h"
58 #include "BKE_global.h"
59
60 #include "UI_view2d.h"
61
62 #include "ED_anim_api.h"
63 #include "ED_armature.h"
64 #include "ED_keyframes_edit.h" // XXX move the select modes out of there!
65 #include "ED_object.h"
66 #include "ED_screen.h"
67
68 #include "WM_api.h"
69 #include "WM_types.h"
70
71 /* ************************************************************************** */
72 /* CHANNELS API - Exposed API */
73
74 /* -------------------------- Selection ------------------------------------- */
75
76 /* Set the given animation-channel as the active one for the active context */
77 // TODO: extend for animdata types...
78 void ANIM_set_active_channel(bAnimContext *ac, void *data, eAnimCont_Types datatype, eAnimFilter_Flags filter, void *channel_data, eAnim_ChannelType channel_type)
79 {
80         ListBase anim_data = {NULL, NULL};
81         bAnimListElem *ale;
82         
83         /* try to build list of filtered items */
84         ANIM_animdata_filter(ac, &anim_data, filter, data, datatype);
85         if (BLI_listbase_is_empty(&anim_data))
86                 return;
87                 
88         /* only clear the 'active' flag for the channels of the same type */
89         for (ale = anim_data.first; ale; ale = ale->next) {
90                 /* skip if types don't match */
91                 if (channel_type != ale->type)
92                         continue;
93                 
94                 /* flag to set depends on type */
95                 switch (ale->type) {
96                         case ANIMTYPE_GROUP:
97                         {
98                                 bActionGroup *agrp = (bActionGroup *)ale->data;
99                                 
100                                 ACHANNEL_SET_FLAG(agrp, ACHANNEL_SETFLAG_CLEAR, AGRP_ACTIVE);
101                                 break;
102                         }
103                         case ANIMTYPE_FCURVE:
104                         {
105                                 FCurve *fcu = (FCurve *)ale->data;
106                                 
107                                 ACHANNEL_SET_FLAG(fcu, ACHANNEL_SETFLAG_CLEAR, FCURVE_ACTIVE);
108                                 break;
109                         }
110                         case ANIMTYPE_NLATRACK:
111                         {
112                                 NlaTrack *nlt = (NlaTrack *)ale->data;
113                                 
114                                 ACHANNEL_SET_FLAG(nlt, ACHANNEL_SETFLAG_CLEAR, NLATRACK_ACTIVE);
115                                 break;
116                         }
117                         case ANIMTYPE_FILLACTD: /* Action Expander */
118                         case ANIMTYPE_DSMAT:    /* Datablock AnimData Expanders */
119                         case ANIMTYPE_DSLAM:
120                         case ANIMTYPE_DSCAM:
121                         case ANIMTYPE_DSCUR:
122                         case ANIMTYPE_DSSKEY:
123                         case ANIMTYPE_DSWOR:
124                         case ANIMTYPE_DSPART:
125                         case ANIMTYPE_DSMBALL:
126                         case ANIMTYPE_DSARM:
127                         case ANIMTYPE_DSMESH:
128                         case ANIMTYPE_DSTEX:
129                         case ANIMTYPE_DSLAT:
130                         case ANIMTYPE_DSLINESTYLE:
131                         case ANIMTYPE_DSSPK:
132                         {
133                                 /* need to verify that this data is valid for now */
134                                 if (ale->adt) {
135                                         ACHANNEL_SET_FLAG(ale->adt, ACHANNEL_SETFLAG_CLEAR, ADT_UI_ACTIVE);
136                                 }
137                                 break;
138                         }
139                 }
140         }
141         
142         /* set active flag */
143         if (channel_data) {
144                 switch (channel_type) {
145                         case ANIMTYPE_GROUP:
146                         {
147                                 bActionGroup *agrp = (bActionGroup *)channel_data;
148                                 agrp->flag |= AGRP_ACTIVE;
149                                 break;
150                         }
151                         case ANIMTYPE_FCURVE:
152                         {
153                                 FCurve *fcu = (FCurve *)channel_data;
154                                 fcu->flag |= FCURVE_ACTIVE;
155                                 break;
156                         }
157                         case ANIMTYPE_NLATRACK:
158                         {
159                                 NlaTrack *nlt = (NlaTrack *)channel_data;
160                                 nlt->flag |= NLATRACK_ACTIVE;
161                                 break;
162                         }
163                         case ANIMTYPE_FILLACTD: /* Action Expander */
164                         case ANIMTYPE_DSMAT:    /* Datablock AnimData Expanders */
165                         case ANIMTYPE_DSLAM:
166                         case ANIMTYPE_DSCAM:
167                         case ANIMTYPE_DSCUR:
168                         case ANIMTYPE_DSSKEY:
169                         case ANIMTYPE_DSWOR:
170                         case ANIMTYPE_DSPART:
171                         case ANIMTYPE_DSMBALL:
172                         case ANIMTYPE_DSARM:
173                         case ANIMTYPE_DSMESH:
174                         case ANIMTYPE_DSLAT:
175                         case ANIMTYPE_DSLINESTYLE:
176                         case ANIMTYPE_DSSPK:
177                         case ANIMTYPE_DSNTREE:
178                         case ANIMTYPE_DSTEX:
179                         {
180                                 /* need to verify that this data is valid for now */
181                                 if (ale && ale->adt) {
182                                         ale->adt->flag |= ADT_UI_ACTIVE;
183                                 }
184                                 break;
185                         }
186                         
187                         /* unhandled currently, but may be interesting */
188                         case ANIMTYPE_GPLAYER:
189                         case ANIMTYPE_MASKLAYER:
190                         case ANIMTYPE_SHAPEKEY:
191                         case ANIMTYPE_NLAACTION:
192                                 break;
193                         
194                         /* other types */
195                         default:
196                                 break;
197                 }
198         }
199         
200         /* clean up */
201         ANIM_animdata_freelist(&anim_data);
202 }
203
204 /* Deselect all animation channels 
205  *      - data: pointer to datatype, as contained in bAnimContext
206  *      - datatype: the type of data that 'data' represents (eAnimCont_Types)
207  *      - test: check if deselecting instead of selecting
208  *      - sel: eAnimChannels_SetFlag;
209  */
210 void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, eAnimCont_Types datatype, bool test, eAnimChannels_SetFlag sel)
211 {
212         ListBase anim_data = {NULL, NULL};
213         bAnimListElem *ale;
214         int filter;
215         
216         /* filter data */
217         /* NOTE: no list visible, otherwise, we get dangling */
218         filter = ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS;
219         ANIM_animdata_filter(ac, &anim_data, filter, data, datatype);
220         
221         /* See if we should be selecting or deselecting */
222         if (test) {
223                 for (ale = anim_data.first; ale; ale = ale->next) {
224                         if (sel == 0) 
225                                 break;
226                         
227                         switch (ale->type) {
228                                 case ANIMTYPE_SCENE:
229                                         if (ale->flag & SCE_DS_SELECTED)
230                                                 sel = ACHANNEL_SETFLAG_CLEAR;
231                                         break;
232                                 case ANIMTYPE_OBJECT:
233 #if 0   /* for now, do not take object selection into account, since it gets too annoying */
234                                         if (ale->flag & SELECT)
235                                                 sel = ACHANNEL_SETFLAG_CLEAR;
236 #endif
237                                         break;
238                                 case ANIMTYPE_GROUP:
239                                         if (ale->flag & AGRP_SELECTED)
240                                                 sel = ACHANNEL_SETFLAG_CLEAR;
241                                         break;
242                                 case ANIMTYPE_FCURVE:
243                                         if (ale->flag & FCURVE_SELECTED)
244                                                 sel = ACHANNEL_SETFLAG_CLEAR;
245                                         break;
246                                 case ANIMTYPE_SHAPEKEY:
247                                         if (ale->flag & KEYBLOCK_SEL)
248                                                 sel = ACHANNEL_SETFLAG_CLEAR;
249                                         break;
250                                 case ANIMTYPE_NLATRACK:
251                                         if (ale->flag & NLATRACK_SELECTED)
252                                                 sel = ACHANNEL_SETFLAG_CLEAR;
253                                         break;
254                                         
255                                 case ANIMTYPE_FILLACTD: /* Action Expander */
256                                 case ANIMTYPE_DSMAT:    /* Datablock AnimData Expanders */
257                                 case ANIMTYPE_DSLAM:
258                                 case ANIMTYPE_DSCAM:
259                                 case ANIMTYPE_DSCUR:
260                                 case ANIMTYPE_DSSKEY:
261                                 case ANIMTYPE_DSWOR:
262                                 case ANIMTYPE_DSPART:
263                                 case ANIMTYPE_DSMBALL:
264                                 case ANIMTYPE_DSARM:
265                                 case ANIMTYPE_DSMESH:
266                                 case ANIMTYPE_DSNTREE:
267                                 case ANIMTYPE_DSTEX:
268                                 case ANIMTYPE_DSLAT:
269                                 case ANIMTYPE_DSLINESTYLE:
270                                 case ANIMTYPE_DSSPK:
271                                 {
272                                         if ((ale->adt) && (ale->adt->flag & ADT_UI_SELECTED))
273                                                 sel = ACHANNEL_SETFLAG_CLEAR;
274                                         break;
275                                 }
276                                 case ANIMTYPE_GPLAYER:
277                                         if (ale->flag & GP_LAYER_SELECT)
278                                                 sel = ACHANNEL_SETFLAG_CLEAR;
279                                         break;
280                                 case ANIMTYPE_MASKLAYER:
281                                         if (ale->flag & MASK_LAYERFLAG_SELECT)
282                                                 sel = ACHANNEL_SETFLAG_CLEAR;
283                                         break;
284                         }
285                 }
286         }
287                 
288         /* Now set the flags */
289         for (ale = anim_data.first; ale; ale = ale->next) {
290                 switch (ale->type) {
291                         case ANIMTYPE_SCENE:
292                         {
293                                 Scene *scene = (Scene *)ale->data;
294                                 
295                                 ACHANNEL_SET_FLAG(scene, sel, SCE_DS_SELECTED);
296                                 
297                                 if (scene->adt) {
298                                         ACHANNEL_SET_FLAG(scene, sel, ADT_UI_SELECTED);
299                                 }
300                                 break;
301                         }
302                         case ANIMTYPE_OBJECT:
303                         {
304 #if 0   /* for now, do not take object selection into account, since it gets too annoying */
305                                 Base *base = (Base *)ale->data;
306                                 Object *ob = base->object;
307
308                                 ACHANNEL_SET_FLAG(base, sel, SELECT);
309                                 ACHANNEL_SET_FLAG(ob, sel, SELECT);
310
311                                 if (ob->adt) {
312                                         ACHANNEL_SET_FLAG(ob, sel, ADT_UI_SELECTED);
313                                 }
314 #endif
315                                 break;
316                         }
317                         case ANIMTYPE_GROUP:
318                         {
319                                 bActionGroup *agrp = (bActionGroup *)ale->data;
320                                 
321                                 ACHANNEL_SET_FLAG(agrp, sel, AGRP_SELECTED);
322                                 agrp->flag &= ~AGRP_ACTIVE;
323                                 break;
324                         }
325                         case ANIMTYPE_FCURVE:
326                         {
327                                 FCurve *fcu = (FCurve *)ale->data;
328                                 
329                                 ACHANNEL_SET_FLAG(fcu, sel, FCURVE_SELECTED);
330                                 fcu->flag &= ~FCURVE_ACTIVE;
331                                 break;
332                         }
333                         case ANIMTYPE_SHAPEKEY:
334                         {
335                                 KeyBlock *kb = (KeyBlock *)ale->data;
336                                 
337                                 ACHANNEL_SET_FLAG(kb, sel, KEYBLOCK_SEL);
338                                 break;
339                         }
340                         case ANIMTYPE_NLATRACK:
341                         {
342                                 NlaTrack *nlt = (NlaTrack *)ale->data;
343                                 
344                                 ACHANNEL_SET_FLAG(nlt, sel, NLATRACK_SELECTED);
345                                 nlt->flag &= ~NLATRACK_ACTIVE;
346                                 break;
347                         }
348                         case ANIMTYPE_FILLACTD: /* Action Expander */
349                         case ANIMTYPE_DSMAT:    /* Datablock AnimData Expanders */
350                         case ANIMTYPE_DSLAM:
351                         case ANIMTYPE_DSCAM:
352                         case ANIMTYPE_DSCUR:
353                         case ANIMTYPE_DSSKEY:
354                         case ANIMTYPE_DSWOR:
355                         case ANIMTYPE_DSPART:
356                         case ANIMTYPE_DSMBALL:
357                         case ANIMTYPE_DSARM:
358                         case ANIMTYPE_DSMESH:
359                         case ANIMTYPE_DSNTREE:
360                         case ANIMTYPE_DSTEX:
361                         case ANIMTYPE_DSLAT:
362                         case ANIMTYPE_DSLINESTYLE:
363                         case ANIMTYPE_DSSPK:
364                         {
365                                 /* need to verify that this data is valid for now */
366                                 if (ale->adt) {
367                                         ACHANNEL_SET_FLAG(ale->adt, sel, ADT_UI_SELECTED);
368                                         ale->adt->flag &= ~ADT_UI_ACTIVE;
369                                 }
370                                 break;
371                         }
372                         case ANIMTYPE_GPLAYER:
373                         {
374                                 bGPDlayer *gpl = (bGPDlayer *)ale->data;
375                                 
376                                 ACHANNEL_SET_FLAG(gpl, sel, GP_LAYER_SELECT);
377                                 break;
378                         }
379                         case ANIMTYPE_MASKLAYER:
380                         {
381                                 MaskLayer *masklay = (MaskLayer *)ale->data;
382                                 
383                                 ACHANNEL_SET_FLAG(masklay, sel, MASK_LAYERFLAG_SELECT);
384                                 break;
385                         }
386                 }
387         }
388         
389         /* Cleanup */
390         ANIM_animdata_freelist(&anim_data);
391 }
392
393 /* ---------------------------- Graph Editor ------------------------------------- */
394
395 /* Flush visibility (for Graph Editor) changes up/down hierarchy for changes in the given setting 
396  *      - anim_data: list of the all the anim channels that can be chosen
397  *              -> filtered using ANIMFILTER_CHANNELS only, since if we took VISIBLE too,
398  *                then the channels under closed expanders get ignored...
399  *      - ale_setting: the anim channel (not in the anim_data list directly, though occurring there)
400  *              with the new state of the setting that we want flushed up/down the hierarchy 
401  *      - setting: type of setting to set
402  *      - on: whether the visibility setting has been enabled or disabled 
403  */
404 void ANIM_flush_setting_anim_channels(bAnimContext *ac, ListBase *anim_data, bAnimListElem *ale_setting, eAnimChannel_Settings setting, eAnimChannels_SetFlag mode)
405 {
406         bAnimListElem *ale, *match = NULL;
407         int prevLevel = 0, matchLevel = 0;
408         
409         /* sanity check */
410         if (ELEM(NULL, anim_data, anim_data->first))
411                 return;
412         
413         /* find the channel that got changed */
414         for (ale = anim_data->first; ale; ale = ale->next) {
415                 /* compare data, and type as main way of identifying the channel */
416                 if ((ale->data == ale_setting->data) && (ale->type == ale_setting->type)) {
417                         /* we also have to check the ID, this is assigned to, since a block may have multiple users */
418                         /* TODO: is the owner-data more revealing? */
419                         if (ale->id == ale_setting->id) {
420                                 match = ale;
421                                 break;
422                         }
423                 }
424         }
425         if (match == NULL) {
426                 printf("ERROR: no channel matching the one changed was found\n");
427                 return;
428         }
429         else {
430                 bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale_setting);
431                 
432                 if (acf == NULL) {
433                         printf("ERROR: no channel info for the changed channel\n");
434                         return;
435                 }
436                 
437                 /* get the level of the channel that was affected
438                  *   - we define the level as simply being the offset for the start of the channel
439                  */
440                 matchLevel = (acf->get_offset) ? acf->get_offset(ac, ale_setting) : 0;
441                 prevLevel = matchLevel;
442         }
443         
444         /* flush up? 
445          *
446          * For Visibility:
447          *      - only flush up if the current state is now enabled (positive 'on' state is default) 
448          *        (otherwise, it's too much work to force the parents to be inactive too)
449          *
450          * For everything else:
451          *      - only flush up if the current state is now disabled (negative 'off' state is default)
452          *        (otherwise, it's too much work to force the parents to be active too)
453          */
454         if ( ((setting == ACHANNEL_SETTING_VISIBLE) && (mode != ACHANNEL_SETFLAG_CLEAR)) ||
455              ((setting != ACHANNEL_SETTING_VISIBLE) && (mode == ACHANNEL_SETFLAG_CLEAR)))
456         {
457                 /* go backwards in the list, until the highest-ranking element (by indention has been covered) */
458                 for (ale = match->prev; ale; ale = ale->prev) {
459                         bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale);
460                         int level;
461                         
462                         /* if no channel info was found, skip, since this type might not have any useful info */
463                         if (acf == NULL)
464                                 continue;
465                         
466                         /* get the level of the current channel traversed 
467                          *   - we define the level as simply being the offset for the start of the channel
468                          */
469                         level = (acf->get_offset) ? acf->get_offset(ac, ale) : 0;
470                         
471                         /* if the level is 'less than' (i.e. more important) the level we're matching
472                          * but also 'less than' the level just tried (i.e. only the 1st group above grouped F-Curves, 
473                          * when toggling visibility of F-Curves, gets flushed, which should happen if we don't let prevLevel
474                          * get updated below once the first 1st group is found)...
475                          */
476                         if (level < prevLevel) {
477                                 /* flush the new status... */
478                                 ANIM_channel_setting_set(ac, ale, setting, mode);
479                                 
480                                 /* store this level as the 'old' level now */
481                                 prevLevel = level;
482                         }
483                         /* if the level is 'greater than' (i.e. less important) than the previous level... */
484                         else if (level > prevLevel) {
485                                 /* if previous level was a base-level (i.e. 0 offset / root of one hierarchy),
486                                  * stop here
487                                  */
488                                 if (prevLevel == 0)
489                                         break;
490                                 /* otherwise, this level weaves into another sibling hierarchy to the previous one just
491                                  * finished, so skip until we get to the parent of this level 
492                                  */
493                                 else
494                                         continue;
495                         }
496                 }
497         }
498         
499         /* flush down (always) */
500         {
501                 /* go forwards in the list, until the lowest-ranking element (by indention has been covered) */
502                 for (ale = match->next; ale; ale = ale->next) {
503                         bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale);
504                         int level;
505                         
506                         /* if no channel info was found, skip, since this type might not have any useful info */
507                         if (acf == NULL)
508                                 continue;
509                         
510                         /* get the level of the current channel traversed 
511                          *   - we define the level as simply being the offset for the start of the channel
512                          */
513                         level = (acf->get_offset) ? acf->get_offset(ac, ale) : 0;
514                         
515                         /* if the level is 'greater than' (i.e. less important) the channel that was changed, 
516                          * flush the new status...
517                          */
518                         if (level > matchLevel)
519                                 ANIM_channel_setting_set(ac, ale, setting, mode);
520                         /* however, if the level is 'less than or equal to' the channel that was changed,
521                          * (i.e. the current channel is as important if not more important than the changed channel)
522                          * then we should stop, since we've found the last one of the children we should flush
523                          */
524                         else
525                                 break;
526                         
527                         /* store this level as the 'old' level now */
528                         // prevLevel = level; // XXX: prevLevel is unused
529                 }
530         }
531 }
532
533 /* -------------------------- F-Curves ------------------------------------- */
534
535 /* Delete the given F-Curve from its AnimData block */
536 void ANIM_fcurve_delete_from_animdata(bAnimContext *ac, AnimData *adt, FCurve *fcu)
537 {
538         /* - if no AnimData, we've got nowhere to remove the F-Curve from 
539          *      (this doesn't guarantee that the F-Curve is in there, but at least we tried
540          * - if no F-Curve, there is nothing to remove
541          */
542         if (ELEM(NULL, adt, fcu))
543                 return;
544                 
545         /* remove from whatever list it came from
546          *      - Action Group
547          *      - Action
548          *      - Drivers
549          *      - TODO... some others?
550          */
551         if ((ac) && (ac->datatype == ANIMCONT_DRIVERS)) {
552                 /* driver F-Curve */
553                 BLI_remlink(&adt->drivers, fcu);
554         }
555         else if (adt->action) {
556                 bAction *act = adt->action;
557                 
558                 /* remove from group or action, whichever one "owns" the F-Curve */
559                 if (fcu->grp) {
560                         bActionGroup *agrp = fcu->grp;
561                         
562                         /* remove F-Curve from group+action */
563                         action_groups_remove_channel(act, fcu);
564                         
565                         /* if group has no more channels, remove it too, 
566                          * otherwise can have many dangling groups [#33541]
567                          */
568                         if (BLI_listbase_is_empty(&agrp->channels)) {
569                                 BLI_freelinkN(&act->groups, agrp);
570                         }
571                 }
572                 else {
573                         BLI_remlink(&act->curves, fcu);
574                 }
575                 
576                 /* if action has no more F-Curves as a result of this, unlink it from
577                  * AnimData if it did not come from a NLA Strip being tweaked.
578                  *
579                  * This is done so that we don't have dangling Object+Action entries in
580                  * channel list that are empty, and linger around long after the data they
581                  * are for has disappeared (and probably won't come back).
582                  */
583                 if (BLI_listbase_is_empty(&act->curves) && (adt->flag & ADT_NLA_EDIT_ON) == 0) {
584                         id_us_min(&act->id);
585                         adt->action = NULL;
586                 }
587         }
588                 
589         /* free the F-Curve itself */
590         free_fcurve(fcu);
591 }
592
593 /* ************************************************************************** */
594 /* OPERATORS */
595
596 /* ****************** Operator Utilities ********************************** */
597
598 /* poll callback for being in an Animation Editor channels list region */
599 static int animedit_poll_channels_active(bContext *C)
600 {
601         ScrArea *sa = CTX_wm_area(C);
602         
603         /* channels region test */
604         /* TODO: could enhance with actually testing if channels region? */
605         if (ELEM(NULL, sa, CTX_wm_region(C)))
606                 return 0;
607         /* animation editor test */
608         if (ELEM3(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA) == 0)
609                 return 0;
610
611         return 1;
612 }
613
614 /* poll callback for Animation Editor channels list region + not in NLA-tweakmode for NLA */
615 static int animedit_poll_channels_nla_tweakmode_off(bContext *C)
616 {
617         ScrArea *sa = CTX_wm_area(C);
618         Scene *scene = CTX_data_scene(C);
619
620         /* channels region test */
621         /* TODO: could enhance with actually testing if channels region? */
622         if (ELEM(NULL, sa, CTX_wm_region(C)))
623                 return 0;
624         /* animation editor test */
625         if (ELEM3(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA) == 0)
626                 return 0;
627         
628         /* NLA TweakMode test */
629         if (sa->spacetype == SPACE_NLA) {
630                 if ((scene == NULL) || (scene->flag & SCE_NLA_EDIT_ON))
631                         return 0;
632         }
633                 
634         return 1;
635 }
636
637 /* ****************** Rearrange Channels Operator ******************* */
638
639 /* constants for channel rearranging */
640 /* WARNING: don't change existing ones without modifying rearrange func accordingly */
641 typedef enum eRearrangeAnimChan_Mode {
642         REARRANGE_ANIMCHAN_TOP = -2,
643         REARRANGE_ANIMCHAN_UP = -1,
644         REARRANGE_ANIMCHAN_DOWN = 1,
645         REARRANGE_ANIMCHAN_BOTTOM = 2
646 } eRearrangeAnimChan_Mode;
647
648 /* defines for rearranging channels */
649 static EnumPropertyItem prop_animchannel_rearrange_types[] = {
650         {REARRANGE_ANIMCHAN_TOP, "TOP", 0, "To Top", ""},
651         {REARRANGE_ANIMCHAN_UP, "UP", 0, "Up", ""},
652         {REARRANGE_ANIMCHAN_DOWN, "DOWN", 0, "Down", ""},
653         {REARRANGE_ANIMCHAN_BOTTOM, "BOTTOM", 0, "To Bottom", ""},
654         {0, NULL, 0, NULL, NULL}
655 };
656
657 /* Reordering "Islands" Defines ----------------------------------- */
658
659 /* Island definition - just a listbase container */
660 typedef struct tReorderChannelIsland {
661         struct tReorderChannelIsland *next, *prev;
662         
663         ListBase channels;  /* channels within this region with the same state */
664         int flag;           /* eReorderIslandFlag */
665 } tReorderChannelIsland;
666
667 /* flags for channel reordering islands */
668 typedef enum eReorderIslandFlag {
669         REORDER_ISLAND_SELECTED         = (1 << 0),   /* island is selected */
670         REORDER_ISLAND_UNTOUCHABLE      = (1 << 1),   /* island should be ignored */
671         REORDER_ISLAND_MOVED            = (1 << 2),   /* island has already been moved */
672         REORDER_ISLAND_HIDDEN           = (1 << 3),   /* island is not visible */
673 } eReorderIslandFlag;
674
675
676 /* Rearrange Methods --------------------------------------------- */
677
678 static bool rearrange_island_ok(tReorderChannelIsland *island)
679 {
680         /* island must not be untouchable */
681         if (island->flag & REORDER_ISLAND_UNTOUCHABLE)
682                 return 0;
683         
684         /* island should be selected to be moved */
685         return (island->flag & REORDER_ISLAND_SELECTED) && !(island->flag & REORDER_ISLAND_MOVED);
686 }
687
688 /* ............................. */
689
690 static bool rearrange_island_top(ListBase *list, tReorderChannelIsland *island)
691 {
692         if (rearrange_island_ok(island)) {
693                 /* remove from current position */
694                 BLI_remlink(list, island);
695                 
696                 /* make it first element */
697                 BLI_insertlinkbefore(list, list->first, island);
698                 
699                 return 1;
700         }
701         
702         return 0;
703 }
704
705 static bool rearrange_island_up(ListBase *list, tReorderChannelIsland *island)
706 {
707         if (rearrange_island_ok(island)) {
708                 /* moving up = moving before the previous island, otherwise we're in the same place */
709                 tReorderChannelIsland *prev = island->prev;
710
711                 /* Skip hidden islands! */
712                 while (prev && prev->flag & REORDER_ISLAND_HIDDEN) {
713                         prev = prev->prev;
714                 }
715
716                 if (prev) {
717                         /* remove from current position */
718                         BLI_remlink(list, island);
719                         
720                         /* push it up */
721                         BLI_insertlinkbefore(list, prev, island);
722                         
723                         return 1;
724                 }
725         }
726         
727         return 0;
728 }
729
730 static bool rearrange_island_down(ListBase *list, tReorderChannelIsland *island)
731 {
732         if (rearrange_island_ok(island)) {
733                 /* moving down = moving after the next island, otherwise we're in the same place */
734                 tReorderChannelIsland *next = island->next;
735
736                 /* Skip hidden islands! */
737                 while (next && next->flag & REORDER_ISLAND_HIDDEN) {
738                         next = next->next;
739                 }
740
741                 if (next) {
742                         /* can only move past if next is not untouchable (i.e. nothing can go after it) */
743                         if ((next->flag & REORDER_ISLAND_UNTOUCHABLE) == 0) {
744                                 /* remove from current position */
745                                 BLI_remlink(list, island);
746                                 
747                                 /* push it down */
748                                 BLI_insertlinkafter(list, next, island);
749                                 
750                                 return true;
751                         }
752                 }
753                 /* else: no next channel, so we're at the bottom already, so can't move */
754         }
755         
756         return false;
757 }
758
759 static bool rearrange_island_bottom(ListBase *list, tReorderChannelIsland *island)
760 {
761         if (rearrange_island_ok(island)) {
762                 tReorderChannelIsland *last = list->last;
763                 
764                 /* remove island from current position */
765                 BLI_remlink(list, island);
766                 
767                 /* add before or after the last channel? */
768                 if ((last->flag & REORDER_ISLAND_UNTOUCHABLE) == 0) {
769                         /* can add after it */
770                         BLI_addtail(list, island);
771                 }
772                 else {
773                         /* can at most go just before it, since last cannot be moved */
774                         BLI_insertlinkbefore(list, last, island);
775                         
776                 }
777                 
778                 return true;
779         }
780         
781         return false;
782 }
783
784 /* ............................. */
785
786 /**
787  * typedef for channel rearranging function
788  *
789  * \param list List of tReorderChannelIsland's that channels belong to
790  * \param island Island to be moved
791  * \return Whether operation was a success
792  */
793 typedef bool (*AnimChanRearrangeFp)(ListBase *list, tReorderChannelIsland *island);
794
795 /* get rearranging function, given 'rearrange' mode */
796 static AnimChanRearrangeFp rearrange_get_mode_func(eRearrangeAnimChan_Mode mode)
797 {
798         switch (mode) {
799                 case REARRANGE_ANIMCHAN_TOP:
800                         return rearrange_island_top;
801                 case REARRANGE_ANIMCHAN_UP:
802                         return rearrange_island_up;
803                 case REARRANGE_ANIMCHAN_DOWN:
804                         return rearrange_island_down;
805                 case REARRANGE_ANIMCHAN_BOTTOM:
806                         return rearrange_island_bottom;
807                 default:
808                         return NULL;
809         }
810 }
811
812 /* Rearrange Islands Generics ------------------------------------- */
813
814 /* add channel into list of islands */
815 static void rearrange_animchannel_add_to_islands(ListBase *islands, ListBase *srcList,
816                                                  Link *channel, eAnim_ChannelType type,
817                                                  const bool is_hidden)
818 {
819         tReorderChannelIsland *island = islands->last;  /* always try to add to last island if possible */
820         bool is_sel = false, is_untouchable = false;
821         
822         /* get flags - selected and untouchable from the channel */
823         switch (type) {
824                 case ANIMTYPE_GROUP:
825                 {
826                         bActionGroup *agrp = (bActionGroup *)channel;
827                         
828                         is_sel = SEL_AGRP(agrp);
829                         is_untouchable = (agrp->flag & AGRP_TEMP) != 0;
830                         break;
831                 }
832                 case ANIMTYPE_FCURVE:
833                 {
834                         FCurve *fcu = (FCurve *)channel;
835                         
836                         is_sel = SEL_FCU(fcu);
837                         break;
838                 }
839                 case ANIMTYPE_NLATRACK:
840                 {
841                         NlaTrack *nlt = (NlaTrack *)channel;
842                         
843                         is_sel = SEL_NLT(nlt);
844                         break;
845                 }
846                 default:
847                         printf("rearrange_animchannel_add_to_islands(): don't know how to handle channels of type %d\n", type);
848                         return;
849         }
850         
851         /* do we need to add to a new island? */
852         if (/* 1) no islands yet */
853             (island == NULL) ||
854             /* 2) unselected islands have single channels only - to allow up/down movement */
855             ((island->flag & REORDER_ISLAND_SELECTED) == 0) ||
856             /* 3) if channel is unselected, stop existing island (it was either wrong sel status, or full already) */
857             (is_sel == 0) ||
858             /* 4) hidden status changes */
859             ((island->flag & REORDER_ISLAND_HIDDEN) != is_hidden)
860             )
861         {
862                 /* create a new island now */
863                 island = MEM_callocN(sizeof(tReorderChannelIsland), "tReorderChannelIsland");
864                 BLI_addtail(islands, island);
865                 
866                 if (is_sel)
867                         island->flag |= REORDER_ISLAND_SELECTED;
868                 if (is_untouchable)
869                         island->flag |= REORDER_ISLAND_UNTOUCHABLE;
870                 if (is_hidden)
871                         island->flag |= REORDER_ISLAND_HIDDEN;
872         }
873
874         /* add channel to island - need to remove it from its existing list first though */
875         BLI_remlink(srcList, channel);
876         BLI_addtail(&island->channels, channel);
877 }
878
879 /* flatten islands out into a single list again */
880 static void rearrange_animchannel_flatten_islands(ListBase *islands, ListBase *srcList)
881 {
882         tReorderChannelIsland *island, *isn = NULL;
883         
884         /* make sure srcList is empty now */
885         BLI_assert(BLI_listbase_is_empty(srcList));
886         
887         /* go through merging islands */
888         for (island = islands->first; island; island = isn) {
889                 isn = island->next;
890                 
891                 /* merge island channels back to main list, then delete the island */
892                 BLI_movelisttolist(srcList, &island->channels);
893                 BLI_freelinkN(islands, island);
894         }
895 }
896
897 /* ............................. */
898
899 /* get a list of all bAnimListElem's of a certain type which are currently visible */
900 static void rearrange_animchannels_filter_visible(ListBase *anim_data_visible, bAnimContext *ac, eAnim_ChannelType type)
901 {
902         ListBase anim_data = {NULL, NULL};
903         bAnimListElem *ale, *ale_next;
904     int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
905         
906         /* get all visible channels */
907     ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
908         
909         /* now, only keep the ones that are of the types we are interested in */
910         for (ale = anim_data.first; ale; ale = ale_next) {
911                 ale_next = ale->next;
912                 
913                 if (ale->type != type) {
914                         BLI_freelinkN(&anim_data, ale);
915                 }
916         }
917         
918         /* return cleaned up list */
919         *anim_data_visible = anim_data;
920 }
921
922 /* performing rearranging of channels using islands */
923 static bool rearrange_animchannel_islands(ListBase *list, AnimChanRearrangeFp rearrange_func,
924                                           eRearrangeAnimChan_Mode mode, eAnim_ChannelType type,
925                                           ListBase *anim_data_visible)
926 {
927         ListBase islands = {NULL, NULL};
928         Link *channel, *chanNext = NULL;
929         bool done = false;
930         
931         /* don't waste effort on an empty list */
932         if (BLI_listbase_is_empty(list))
933                 return 0;
934         
935         /* group channels into islands */
936         for (channel = list->first; channel; channel = chanNext) {
937                 /* find out whether this channel is present in anim_data_visible or not! */
938                 const bool is_hidden = (BLI_findptr(anim_data_visible, channel, offsetof(bAnimListElem, data)) == NULL);
939                 chanNext = channel->next;
940                 rearrange_animchannel_add_to_islands(&islands, list, channel, type, is_hidden);
941         }
942         
943         /* perform moving of selected islands now, but only if there is more than one of 'em so that something will happen 
944          *      - scanning of the list is performed in the opposite direction to the direction we're moving things, so that we 
945          *        shouldn't need to encounter items we've moved already
946          */
947         if (islands.first != islands.last) {
948                 tReorderChannelIsland *first = (mode > 0) ? islands.last : islands.first;
949                 tReorderChannelIsland *island, *isn = NULL;
950                 
951                 for (island = first; island; island = isn) {
952                         isn = (mode > 0) ? island->prev : island->next;
953                         
954                         /* perform rearranging */
955                         if (rearrange_func(&islands, island)) {
956                                 island->flag |= REORDER_ISLAND_MOVED;
957                                 done = true;
958                         }
959                 }
960         }
961         
962         /* ungroup islands */
963         rearrange_animchannel_flatten_islands(&islands, list);
964         
965         /* did we do anything? */
966         return done;
967 }
968
969 /* NLA Specific Stuff ----------------------------------------------------- */
970
971 /* Change the order NLA Tracks within NLA Stack
972  * ! NLA tracks are displayed in opposite order, so directions need care
973  *      mode: REARRANGE_ANIMCHAN_*  
974  */
975 static void rearrange_nla_channels(bAnimContext *ac, AnimData *adt, eRearrangeAnimChan_Mode mode)
976 {
977         AnimChanRearrangeFp rearrange_func;
978         ListBase anim_data_visible = {NULL, NULL};
979         
980         /* hack: invert mode so that functions will work in right order */
981         mode *= -1;
982         
983         /* get rearranging function */
984         rearrange_func = rearrange_get_mode_func(mode);
985         if (rearrange_func == NULL)
986                 return;
987         
988         /* Filter visible data. */
989         rearrange_animchannels_filter_visible(&anim_data_visible, ac, ANIMTYPE_NLATRACK);
990         
991         /* perform rearranging on tracks list */
992         rearrange_animchannel_islands(&adt->nla_tracks, rearrange_func, mode, ANIMTYPE_NLATRACK, &anim_data_visible);
993         
994         /* free temp data */
995         BLI_freelistN(&anim_data_visible);
996 }
997
998 /* Drivers Specific Stuff ------------------------------------------------- */
999
1000 /* Change the order drivers within AnimData block
1001  *      mode: REARRANGE_ANIMCHAN_*  
1002  */
1003 static void rearrange_driver_channels(bAnimContext *ac, AnimData *adt, eRearrangeAnimChan_Mode mode)
1004 {
1005         /* get rearranging function */
1006         AnimChanRearrangeFp rearrange_func = rearrange_get_mode_func(mode);
1007         ListBase anim_data_visible = {NULL, NULL};
1008         
1009         if (rearrange_func == NULL)
1010                 return;
1011         
1012         /* only consider drivers if they're accessible */
1013         if (EXPANDED_DRVD(adt) == 0)
1014                 return;
1015         
1016         /* Filter visible data. */
1017         rearrange_animchannels_filter_visible(&anim_data_visible, ac, ANIMTYPE_FCURVE);
1018         
1019         /* perform rearranging on drivers list (drivers are really just F-Curves) */
1020         rearrange_animchannel_islands(&adt->drivers, rearrange_func, mode, ANIMTYPE_FCURVE, &anim_data_visible);
1021         
1022         /* free temp data */
1023         BLI_freelistN(&anim_data_visible);
1024 }
1025
1026 /* Action Specific Stuff ------------------------------------------------- */
1027
1028 /* make sure all action-channels belong to a group (and clear action's list) */
1029 static void split_groups_action_temp(bAction *act, bActionGroup *tgrp)
1030 {
1031         bActionGroup *agrp;
1032         FCurve *fcu;
1033         
1034         if (act == NULL)
1035                 return;
1036         
1037         /* Separate F-Curves into lists per group */
1038         for (agrp = act->groups.first; agrp; agrp = agrp->next) {
1039                 if (agrp->channels.first) {
1040                         fcu = agrp->channels.last;
1041                         act->curves.first = fcu->next;
1042                         
1043                         fcu = agrp->channels.first;
1044                         fcu->prev = NULL;
1045                         
1046                         fcu = agrp->channels.last;
1047                         fcu->next = NULL;
1048                 }
1049         }
1050         
1051         /* Initialize memory for temp-group */
1052         memset(tgrp, 0, sizeof(bActionGroup));
1053         tgrp->flag |= (AGRP_EXPANDED | AGRP_TEMP);
1054         BLI_strncpy(tgrp->name, "#TempGroup", sizeof(tgrp->name));
1055         
1056         /* Move any action-channels not already moved, to the temp group */
1057         if (act->curves.first) {
1058                 /* start of list */
1059                 fcu = act->curves.first;
1060                 fcu->prev = NULL;
1061                 tgrp->channels.first = fcu;
1062                 act->curves.first = NULL;
1063                 
1064                 /* end of list */
1065                 fcu = act->curves.last;
1066                 fcu->next = NULL;
1067                 tgrp->channels.last = fcu;
1068                 act->curves.last = NULL;
1069                 
1070                 /* ensure that all of these get their group set to this temp group 
1071                  * (so that visibility filtering works)
1072                  */
1073                 for (fcu = tgrp->channels.first; fcu; fcu = fcu->next) {
1074                         fcu->grp = tgrp;
1075                 }
1076         }
1077         
1078         /* Add temp-group to list */
1079         BLI_addtail(&act->groups, tgrp);
1080 }
1081
1082 /* link lists of channels that groups have */
1083 static void join_groups_action_temp(bAction *act)
1084 {
1085         bActionGroup *agrp;
1086         
1087         for (agrp = act->groups.first; agrp; agrp = agrp->next) {
1088                 ListBase tempGroup;
1089                 
1090                 /* add list of channels to action's channels */
1091                 tempGroup = agrp->channels;
1092                 BLI_movelisttolist(&act->curves, &agrp->channels);
1093                 agrp->channels = tempGroup;
1094                 
1095                 /* clear moved flag */
1096                 agrp->flag &= ~AGRP_MOVED;
1097                 
1098                 /* if group was temporary one:
1099                  * - unassign all FCurves which were temporarily added to it
1100                  * - remove from list (but don't free as it's on the stack!)
1101                  */
1102                 if (agrp->flag & AGRP_TEMP) {
1103                         FCurve *fcu;
1104                         
1105                         for (fcu = agrp->channels.first; fcu; fcu = fcu->next) {
1106                                 fcu->grp = NULL;
1107                         }
1108                         
1109                         BLI_remlink(&act->groups, agrp);
1110                         break;
1111                 }
1112         }
1113 }
1114
1115 /* Change the order of anim-channels within action 
1116  *      mode: REARRANGE_ANIMCHAN_*  
1117  */
1118 static void rearrange_action_channels(bAnimContext *ac, bAction *act, eRearrangeAnimChan_Mode mode)
1119 {
1120         bActionGroup tgrp;
1121         ListBase anim_data_visible = {NULL, NULL};
1122         bool do_channels;
1123         
1124         /* get rearranging function */
1125         AnimChanRearrangeFp rearrange_func = rearrange_get_mode_func(mode);
1126         
1127         if (rearrange_func == NULL)
1128                 return;
1129         
1130         /* make sure we're only operating with groups (vs a mixture of groups+curves) */
1131         split_groups_action_temp(act, &tgrp);
1132         
1133         /* Filter visible data. */
1134         rearrange_animchannels_filter_visible(&anim_data_visible, ac, ANIMTYPE_GROUP);
1135         
1136         /* rearrange groups first 
1137          *      - the group's channels will only get considered if nothing happened when rearranging the groups
1138          *        i.e. the rearrange function returned 0
1139          */
1140         do_channels = (rearrange_animchannel_islands(&act->groups, rearrange_func, mode, ANIMTYPE_GROUP,
1141                                                      &anim_data_visible) == 0);
1142         
1143         /* free temp data */
1144         BLI_freelistN(&anim_data_visible);
1145         
1146         if (do_channels) {
1147                 bActionGroup *agrp;
1148                 
1149                 /* Filter visible data. */
1150                 rearrange_animchannels_filter_visible(&anim_data_visible, ac, ANIMTYPE_FCURVE);
1151                 
1152                 for (agrp = act->groups.first; agrp; agrp = agrp->next) {
1153                         /* only consider F-Curves if they're visible (group expanded) */
1154                         if (EXPANDED_AGRP(ac, agrp)) {
1155                                 rearrange_animchannel_islands(&agrp->channels, rearrange_func, mode, ANIMTYPE_FCURVE,
1156                                                               &anim_data_visible);
1157                         }
1158                 }
1159                 
1160                 /* free temp data */
1161                 BLI_freelistN(&anim_data_visible);
1162         }
1163         
1164         /* assemble lists into one list (and clear moved tags) */
1165         join_groups_action_temp(act);
1166 }
1167
1168 /* ------------------- */
1169
1170 static int animchannels_rearrange_exec(bContext *C, wmOperator *op)
1171 {
1172         bAnimContext ac;
1173         short mode;
1174         
1175         /* get editor data */
1176         if (ANIM_animdata_get_context(C, &ac) == 0)
1177                 return OPERATOR_CANCELLED;
1178                 
1179         /* get mode */
1180         mode = RNA_enum_get(op->ptr, "direction");
1181         
1182         /* method to move channels depends on the editor */
1183         if (ac.datatype == ANIMCONT_GPENCIL) {
1184                 /* Grease Pencil channels */
1185                 printf("Grease Pencil not supported for moving yet\n");
1186         }
1187         else if (ac.datatype == ANIMCONT_MASK) {
1188                 /* Grease Pencil channels */
1189                 printf("Mask does not supported for moving yet\n");
1190         }
1191         else if (ac.datatype == ANIMCONT_ACTION) {
1192                 /* Directly rearrange action's channels */
1193                 rearrange_action_channels(&ac, ac.data, mode);
1194         }
1195         else {
1196                 ListBase anim_data = {NULL, NULL};
1197                 bAnimListElem *ale;
1198                 int filter;
1199                 
1200                 /* get animdata blocks */
1201                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA);
1202                 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1203                 
1204                 for (ale = anim_data.first; ale; ale = ale->next) {
1205                         AnimData *adt = ale->data;
1206                         
1207                         switch (ac.datatype) {
1208                                 case ANIMCONT_NLA: /* NLA-tracks only */
1209                                         rearrange_nla_channels(&ac, adt, mode);
1210                                         break;
1211                                 
1212                                 case ANIMCONT_DRIVERS: /* Drivers list only */
1213                                         rearrange_driver_channels(&ac, adt, mode);
1214                                         break;
1215                                 
1216                                 case ANIMCONT_SHAPEKEY: // DOUBLE CHECK ME...
1217                                 default: /* some collection of actions */
1218                                         if (adt->action)
1219                                                 rearrange_action_channels(&ac, adt->action, mode);
1220                                         else if (G.debug & G_DEBUG)
1221                                                 printf("Animdata has no action\n");
1222                                         break;
1223                         }
1224                 }
1225                 
1226                 /* free temp data */
1227                 ANIM_animdata_freelist(&anim_data);
1228         }
1229         
1230         /* send notifier that things have changed */
1231         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1232         
1233         return OPERATOR_FINISHED;
1234 }
1235
1236 static void ANIM_OT_channels_move(wmOperatorType *ot)
1237 {
1238         /* identifiers */
1239         ot->name = "Move Channels";
1240         ot->idname = "ANIM_OT_channels_move";
1241         ot->description = "Rearrange selected animation channels";
1242         
1243         /* api callbacks */
1244         ot->exec = animchannels_rearrange_exec;
1245         ot->poll = animedit_poll_channels_nla_tweakmode_off;
1246         
1247         /* flags */
1248         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1249         
1250         /* props */
1251         ot->prop = RNA_def_enum(ot->srna, "direction", prop_animchannel_rearrange_types, REARRANGE_ANIMCHAN_DOWN, "Direction", "");
1252 }
1253
1254 /* ******************** Group Channel Operator ************************ */
1255
1256 static int animchannels_grouping_poll(bContext *C)
1257 {
1258         ScrArea *sa = CTX_wm_area(C);
1259         SpaceLink *sl;
1260
1261         /* channels region test */
1262         /* TODO: could enhance with actually testing if channels region? */
1263         if (ELEM(NULL, sa, CTX_wm_region(C)))
1264                 return 0;
1265                 
1266         /* animation editor test - must be suitable modes only */
1267         sl = CTX_wm_space_data(C);
1268         
1269         switch (sa->spacetype) {
1270                 /* supported... */
1271                 case SPACE_ACTION:
1272                 {
1273                         SpaceAction *saction = (SpaceAction *)sl;
1274                         
1275                         /* dopesheet and action only - all others are for other datatypes or have no groups */
1276                         if (ELEM(saction->mode, SACTCONT_ACTION, SACTCONT_DOPESHEET) == 0)
1277                                 return 0;
1278                 }
1279                         break;
1280                         
1281                 case SPACE_IPO:
1282                 {
1283                         SpaceIpo *sipo = (SpaceIpo *)sl;
1284                         
1285                         /* drivers can't have groups... */
1286                         if (sipo->mode != SIPO_MODE_ANIMATION)
1287                                 return 0;
1288                 }
1289                         break;
1290                         
1291                 /* unsupported... */
1292                 default:
1293                         return 0;
1294         }
1295         
1296         return 1;
1297 }
1298
1299 /* ----------------------------------------------------------- */
1300
1301 static void animchannels_group_channels(bAnimContext *ac, bAnimListElem *adt_ref, const char name[])
1302 {       
1303         AnimData *adt = adt_ref->adt;
1304         bAction *act = adt->action;
1305         
1306         if (act) {
1307                 ListBase anim_data = {NULL, NULL};
1308                 int filter;
1309                 
1310                 /* find selected F-Curves to re-group */
1311                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL);
1312                 ANIM_animdata_filter(ac, &anim_data, filter, adt_ref, ANIMCONT_CHANNEL);
1313                 
1314                 if (anim_data.first) {
1315                         bActionGroup *agrp;
1316                         bAnimListElem *ale;
1317                         
1318                         /* create new group, which should now be part of the action */
1319                         agrp = action_groups_add_new(act, name);
1320                         BLI_assert(agrp != NULL);
1321                         
1322                         /* transfer selected F-Curves across to new group  */
1323                         for (ale = anim_data.first; ale; ale = ale->next) {
1324                                 FCurve *fcu = (FCurve *)ale->data;
1325                                 bActionGroup *grp = fcu->grp;
1326                                 
1327                                 /* remove F-Curve from group, then group too if it is now empty */
1328                                 action_groups_remove_channel(act, fcu);
1329                                 
1330                                 if ((grp) && BLI_listbase_is_empty(&grp->channels)) {
1331                                         BLI_freelinkN(&act->groups, grp);
1332                                 }
1333                                 
1334                                 /* add F-Curve to group */
1335                                 action_groups_add_channel(act, agrp, fcu);
1336                         }
1337                 }
1338                 
1339                 /* cleanup */
1340                 ANIM_animdata_freelist(&anim_data);
1341         }
1342 }
1343
1344 static int animchannels_group_exec(bContext *C, wmOperator *op)
1345 {
1346         bAnimContext ac;
1347         char name[MAX_NAME];
1348         
1349         /* get editor data */
1350         if (ANIM_animdata_get_context(C, &ac) == 0)
1351                 return OPERATOR_CANCELLED;
1352         
1353         /* get name for new group */
1354         RNA_string_get(op->ptr, "name", name);
1355         
1356         /* XXX: name for group should never be empty... */
1357         if (name[0]) {
1358                 ListBase anim_data = {NULL, NULL};
1359                 bAnimListElem *ale;
1360                 int filter;
1361                 
1362                 /* handle each animdata block separately, so that the regrouping doesn't flow into blocks  */
1363                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA | ANIMFILTER_NODUPLIS);
1364                 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1365                 
1366                 for (ale = anim_data.first; ale; ale = ale->next) {
1367                         animchannels_group_channels(&ac, ale, name);
1368                 }
1369                 
1370                 /* free temp data */
1371                 ANIM_animdata_freelist(&anim_data);
1372                 
1373                 /* updatss */
1374                 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1375         }
1376         
1377         return OPERATOR_FINISHED;
1378 }
1379
1380 static void ANIM_OT_channels_group(wmOperatorType *ot)
1381 {
1382         /* identifiers */
1383         ot->name = "Group Channels";
1384         ot->idname = "ANIM_OT_channels_group";
1385         ot->description = "Add selected F-Curves to a new group";
1386         
1387         /* callbacks */
1388         ot->invoke = WM_operator_props_popup;
1389         ot->exec = animchannels_group_exec;
1390         ot->poll = animchannels_grouping_poll;
1391         
1392         /* flags */
1393         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1394         
1395         /* props */
1396         ot->prop = RNA_def_string(ot->srna, "name", "New Group", 
1397                                   sizeof(((bActionGroup *)NULL)->name), 
1398                                   "Name", "Name of newly created group");
1399         /* RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE); */ /* XXX: still not too sure about this - keeping same text is confusing... */
1400 }
1401
1402 /* ----------------------------------------------------------- */
1403
1404 static int animchannels_ungroup_exec(bContext *C, wmOperator *UNUSED(op))
1405 {
1406         bAnimContext ac;
1407         
1408         ListBase anim_data = {NULL, NULL};
1409         bAnimListElem *ale;
1410         int filter;
1411         
1412         /* get editor data */
1413         if (ANIM_animdata_get_context(C, &ac) == 0)
1414                 return OPERATOR_CANCELLED;
1415                 
1416         /* just selected F-Curves... */
1417         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_NODUPLIS);
1418         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1419         
1420         for (ale = anim_data.first; ale; ale = ale->next) {
1421                 /* find action for this F-Curve... */
1422                 if (ale->adt && ale->adt->action) {
1423                         FCurve  *fcu = (FCurve *)ale->data;
1424                         bAction *act = ale->adt->action;
1425                         
1426                         /* only proceed to remove if F-Curve is in a group... */
1427                         if (fcu->grp) { 
1428                                 bActionGroup *agrp = fcu->grp;
1429                                 
1430                                 /* remove F-Curve from group and add at tail (ungrouped) */
1431                                 action_groups_remove_channel(act, fcu);
1432                                 BLI_addtail(&act->curves, fcu);
1433                                 
1434                                 /* delete group if it is now empty */
1435                                 if (BLI_listbase_is_empty(&agrp->channels)) {
1436                                         BLI_freelinkN(&act->groups, agrp);
1437                                 }
1438                         }
1439                 }
1440         }
1441         
1442         /* cleanup */
1443         ANIM_animdata_freelist(&anim_data);
1444         
1445         /* updates */
1446         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1447         
1448         return OPERATOR_FINISHED;
1449 }
1450
1451 static void ANIM_OT_channels_ungroup(wmOperatorType *ot)
1452 {
1453         /* identifiers */
1454         ot->name = "Ungroup Channels";
1455         ot->idname = "ANIM_OT_channels_ungroup";
1456         ot->description = "Remove selected F-Curves from their current groups";
1457         
1458         /* callbacks */
1459         ot->exec = animchannels_ungroup_exec;
1460         ot->poll = animchannels_grouping_poll;
1461         
1462         /* flags */
1463         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1464 }
1465
1466 /* ******************** Delete Channel Operator *********************** */
1467
1468 static int animchannels_delete_exec(bContext *C, wmOperator *UNUSED(op))
1469 {
1470         bAnimContext ac;
1471         ListBase anim_data = {NULL, NULL};
1472         bAnimListElem *ale;
1473         int filter;
1474         
1475         /* get editor data */
1476         if (ANIM_animdata_get_context(C, &ac) == 0)
1477                 return OPERATOR_CANCELLED;
1478         
1479         /* cannot delete in shapekey */
1480         if (ac.datatype == ANIMCONT_SHAPEKEY) 
1481                 return OPERATOR_CANCELLED;
1482                 
1483                 
1484         /* do groups only first (unless in Drivers mode, where there are none) */
1485         if (ac.datatype != ANIMCONT_DRIVERS) {
1486                 /* filter data */
1487                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
1488                 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1489                 
1490                 /* delete selected groups and their associated channels */
1491                 for (ale = anim_data.first; ale; ale = ale->next) {
1492                         /* only groups - don't check other types yet, since they may no-longer exist */
1493                         if (ale->type == ANIMTYPE_GROUP) {
1494                                 bActionGroup *agrp = (bActionGroup *)ale->data;
1495                                 AnimData *adt = ale->adt;
1496                                 FCurve *fcu, *fcn;
1497                                 
1498                                 /* skip this group if no AnimData available, as we can't safely remove the F-Curves */
1499                                 if (adt == NULL)
1500                                         continue;
1501                                 
1502                                 /* delete all of the Group's F-Curves, but no others */
1503                                 for (fcu = agrp->channels.first; fcu && fcu->grp == agrp; fcu = fcn) {
1504                                         fcn = fcu->next;
1505                                         
1506                                         /* remove from group and action, then free */
1507                                         action_groups_remove_channel(adt->action, fcu);
1508                                         free_fcurve(fcu);
1509                                 }
1510                                 
1511                                 /* free the group itself */
1512                                 if (adt->action)
1513                                         BLI_freelinkN(&adt->action->groups, agrp);
1514                                 else
1515                                         MEM_freeN(agrp);
1516                         }
1517                 }
1518                 
1519                 /* cleanup */
1520                 ANIM_animdata_freelist(&anim_data);
1521         }
1522         
1523         /* filter data */
1524         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
1525         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1526         
1527         /* delete selected data channels */
1528         for (ale = anim_data.first; ale; ale = ale->next) {
1529                 switch (ale->type) {
1530                         case ANIMTYPE_FCURVE: 
1531                         {
1532                                 /* F-Curves if we can identify its parent */
1533                                 AnimData *adt = ale->adt;
1534                                 FCurve *fcu = (FCurve *)ale->data;
1535                                 
1536                                 /* try to free F-Curve */
1537                                 ANIM_fcurve_delete_from_animdata(&ac, adt, fcu);
1538                                 break;
1539                         }
1540                         case ANIMTYPE_GPLAYER:
1541                         {
1542                                 /* Grease Pencil layer */
1543                                 bGPdata *gpd = (bGPdata *)ale->id;
1544                                 bGPDlayer *gpl = (bGPDlayer *)ale->data;
1545                                 
1546                                 /* try to delete the layer's data and the layer itself */
1547                                 free_gpencil_frames(gpl);
1548                                 BLI_freelinkN(&gpd->layers, gpl);
1549                                 break;
1550                         }
1551                         case ANIMTYPE_MASKLAYER:
1552                         {
1553                                 /* Mask layer */
1554                                 Mask *mask = (Mask *)ale->id;
1555                                 MaskLayer *masklay = (MaskLayer *)ale->data;
1556                                 
1557                                 /* try to delete the layer's data and the layer itself */
1558                                 BKE_mask_layer_remove(mask, masklay);
1559                                 break;
1560                         }
1561                 }
1562         }
1563         
1564         /* cleanup */
1565         ANIM_animdata_freelist(&anim_data);
1566         
1567         /* send notifier that things have changed */
1568         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1569         
1570         return OPERATOR_FINISHED;
1571 }
1572  
1573 static void ANIM_OT_channels_delete(wmOperatorType *ot)
1574 {
1575         /* identifiers */
1576         ot->name = "Delete Channels";
1577         ot->idname = "ANIM_OT_channels_delete";
1578         ot->description = "Delete all selected animation channels";
1579         
1580         /* api callbacks */
1581         ot->exec = animchannels_delete_exec;
1582         ot->poll = animedit_poll_channels_active;
1583         
1584         /* flags */
1585         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1586 }
1587
1588 /* ******************** Set Channel Visibility Operator *********************** */
1589 /* NOTE: this operator is only valid in the Graph Editor channels region */
1590
1591 static int animchannels_visibility_set_exec(bContext *C, wmOperator *UNUSED(op))
1592 {
1593         bAnimContext ac;
1594         ListBase anim_data = {NULL, NULL};
1595         ListBase all_data = {NULL, NULL};
1596         bAnimListElem *ale;
1597         int filter;
1598         
1599         /* get editor data */
1600         if (ANIM_animdata_get_context(C, &ac) == 0)
1601                 return OPERATOR_CANCELLED;
1602         
1603         /* get list of all channels that selection may need to be flushed to 
1604          * - hierarchy mustn't affect what we have access to here...
1605          */
1606         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_NODUPLIS);
1607         ANIM_animdata_filter(&ac, &all_data, filter, ac.data, ac.datatype);
1608                 
1609         /* hide all channels not selected
1610          * - hierarchy matters if we're doing this from the channels region
1611          *   since we only want to apply this to channels we can "see", 
1612          *   and have these affect their relatives
1613          * - but for Graph Editor, this gets used also from main region
1614          *   where hierarchy doesn't apply, as for [#21276]
1615          */
1616         if ((ac.spacetype == SPACE_IPO) && (ac.regiontype != RGN_TYPE_CHANNELS)) {
1617                 /* graph editor (case 2) */
1618                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_UNSEL | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
1619         }
1620         else {
1621                 /* standard case */
1622                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_UNSEL | ANIMFILTER_NODUPLIS);
1623         }
1624         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1625         
1626         for (ale = anim_data.first; ale; ale = ale->next) {
1627                 /* clear setting first */
1628                 ANIM_channel_setting_set(&ac, ale, ACHANNEL_SETTING_VISIBLE, ACHANNEL_SETFLAG_CLEAR);
1629                 
1630                 /* now also flush selection status as appropriate 
1631                  * NOTE: in some cases, this may result in repeat flushing being performed
1632                  */
1633                 ANIM_flush_setting_anim_channels(&ac, &all_data, ale, ACHANNEL_SETTING_VISIBLE, 0);
1634         }
1635         
1636         ANIM_animdata_freelist(&anim_data);
1637         
1638         /* make all the selected channels visible */
1639         filter = (ANIMFILTER_SEL | ANIMFILTER_NODUPLIS);
1640         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1641
1642         for (ale = anim_data.first; ale; ale = ale->next) {
1643                 /* hack: skip object channels for now, since flushing those will always flush everything, but they are always included */
1644                 /* TODO: find out why this is the case, and fix that */
1645                 if (ale->type == ANIMTYPE_OBJECT)
1646                         continue;
1647                 
1648                 /* enable the setting */
1649                 ANIM_channel_setting_set(&ac, ale, ACHANNEL_SETTING_VISIBLE, ACHANNEL_SETFLAG_ADD);
1650                 
1651                 /* now, also flush selection status up/down as appropriate */
1652                 ANIM_flush_setting_anim_channels(&ac, &all_data, ale, ACHANNEL_SETTING_VISIBLE, 1);
1653         }
1654         
1655         ANIM_animdata_freelist(&anim_data);
1656         BLI_freelistN(&all_data);
1657         
1658         
1659         /* send notifier that things have changed */
1660         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1661         
1662         return OPERATOR_FINISHED;
1663 }
1664
1665 static void ANIM_OT_channels_visibility_set(wmOperatorType *ot)
1666 {
1667         /* identifiers */
1668         ot->name = "Set Visibility";
1669         ot->idname = "ANIM_OT_channels_visibility_set";
1670         ot->description = "Make only the selected animation channels visible in the Graph Editor";
1671         
1672         /* api callbacks */
1673         ot->exec = animchannels_visibility_set_exec;
1674         ot->poll = ED_operator_graphedit_active;
1675         
1676         /* flags */
1677         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1678 }
1679
1680
1681 /* ******************** Toggle Channel Visibility Operator *********************** */
1682 /* NOTE: this operator is only valid in the Graph Editor channels region */
1683
1684 static int animchannels_visibility_toggle_exec(bContext *C, wmOperator *UNUSED(op))
1685 {
1686         bAnimContext ac;
1687         ListBase anim_data = {NULL, NULL};
1688         ListBase all_data = {NULL, NULL};
1689         bAnimListElem *ale;
1690         int filter;
1691         short vis = ACHANNEL_SETFLAG_ADD;
1692         
1693         /* get editor data */
1694         if (ANIM_animdata_get_context(C, &ac) == 0)
1695                 return OPERATOR_CANCELLED;
1696                 
1697         /* get list of all channels that selection may need to be flushed to 
1698          * - hierarchy mustn't affect what we have access to here...
1699          */
1700         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_NODUPLIS);
1701         ANIM_animdata_filter(&ac, &all_data, filter, ac.data, ac.datatype);
1702                 
1703         /* filter data
1704          * - restrict this to only applying on settings we can get to in the list
1705          */
1706         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_NODUPLIS);
1707         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1708         
1709         /* See if we should be making showing all selected or hiding */
1710         for (ale = anim_data.first; ale; ale = ale->next) {
1711                 /* set the setting in the appropriate way (if available) */
1712                 if (ANIM_channel_setting_get(&ac, ale, ACHANNEL_SETTING_VISIBLE)) {
1713                         vis = ACHANNEL_SETFLAG_CLEAR;
1714                         break;
1715                 }
1716         }
1717
1718         /* Now set the flags */
1719         for (ale = anim_data.first; ale; ale = ale->next) {
1720                 /* hack: skip object channels for now, since flushing those will always flush everything, but they are always included */
1721                 /* TODO: find out why this is the case, and fix that */
1722                 if (ale->type == ANIMTYPE_OBJECT)
1723                         continue;
1724                 
1725                 /* change the setting */
1726                 ANIM_channel_setting_set(&ac, ale, ACHANNEL_SETTING_VISIBLE, vis);
1727                 
1728                 /* now, also flush selection status up/down as appropriate */
1729                 ANIM_flush_setting_anim_channels(&ac, &all_data, ale, ACHANNEL_SETTING_VISIBLE, (vis == ACHANNEL_SETFLAG_ADD));
1730         }
1731         
1732         /* cleanup */
1733         ANIM_animdata_freelist(&anim_data);
1734         BLI_freelistN(&all_data);
1735         
1736         /* send notifier that things have changed */
1737         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1738         
1739         return OPERATOR_FINISHED;
1740 }
1741
1742 static void ANIM_OT_channels_visibility_toggle(wmOperatorType *ot)
1743 {
1744         /* identifiers */
1745         ot->name = "Toggle Visibility";
1746         ot->idname = "ANIM_OT_channels_visibility_toggle";
1747         ot->description = "Toggle visibility in Graph Editor of all selected animation channels";
1748         
1749         /* api callbacks */
1750         ot->exec = animchannels_visibility_toggle_exec;
1751         ot->poll = ED_operator_graphedit_active;
1752         
1753         /* flags */
1754         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1755 }
1756
1757 /* ********************** Set Flags Operator *********************** */
1758
1759 /* defines for setting animation-channel flags */
1760 static EnumPropertyItem prop_animchannel_setflag_types[] = {
1761         {ACHANNEL_SETFLAG_TOGGLE, "TOGGLE", 0, "Toggle", ""},
1762         {ACHANNEL_SETFLAG_CLEAR, "DISABLE", 0, "Disable", ""},
1763         {ACHANNEL_SETFLAG_ADD, "ENABLE", 0, "Enable", ""},
1764         {ACHANNEL_SETFLAG_INVERT, "INVERT", 0, "Invert", ""},
1765         {0, NULL, 0, NULL, NULL}
1766 };
1767
1768 /* defines for set animation-channel settings */
1769 // TODO: could add some more types, but those are really quite dependent on the mode...
1770 static EnumPropertyItem prop_animchannel_settings_types[] = {
1771         {ACHANNEL_SETTING_PROTECT, "PROTECT", 0, "Protect", ""},
1772         {ACHANNEL_SETTING_MUTE, "MUTE", 0, "Mute", ""},
1773         {0, NULL, 0, NULL, NULL}
1774 };
1775
1776
1777 /* ------------------- */
1778
1779 /* Set/clear a particular flag (setting) for all selected + visible channels 
1780  *      setting: the setting to modify
1781  *      mode: eAnimChannels_SetFlag
1782  *      onlysel: only selected channels get the flag set
1783  */
1784 // TODO: enable a setting which turns flushing on/off?
1785 static void setflag_anim_channels(bAnimContext *ac, eAnimChannel_Settings setting, eAnimChannels_SetFlag mode, bool onlysel, bool flush)
1786 {
1787         ListBase anim_data = {NULL, NULL};
1788         ListBase all_data = {NULL, NULL};
1789         bAnimListElem *ale;
1790         int filter;
1791         
1792         /* filter data that we need if flush is on */
1793         if (flush) {
1794                 /* get list of all channels that selection may need to be flushed to 
1795                  * - hierarchy visibility needs to be ignored so that settings can get flushed
1796                  *   "down" inside closed containers
1797                  */
1798                 filter = ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS;
1799                 ANIM_animdata_filter(ac, &all_data, filter, ac->data, ac->datatype);
1800         }
1801         
1802         /* filter data that we're working on 
1803          * - hierarchy matters if we're doing this from the channels region
1804          *   since we only want to apply this to channels we can "see", 
1805          *   and have these affect their relatives
1806          * - but for Graph Editor, this gets used also from main region
1807          *   where hierarchy doesn't apply [#21276]
1808          */
1809         if ((ac->spacetype == SPACE_IPO) && (ac->regiontype != RGN_TYPE_CHANNELS)) {
1810                 /* graph editor (case 2) */
1811                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
1812         }
1813         else {
1814                 /* standard case */
1815                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_NODUPLIS);
1816         }
1817         if (onlysel) filter |= ANIMFILTER_SEL;
1818         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1819         
1820         /* if toggling, check if disable or enable */
1821         if (mode == ACHANNEL_SETFLAG_TOGGLE) {
1822                 /* default to turn all on, unless we encounter one that's on... */
1823                 mode = ACHANNEL_SETFLAG_ADD;
1824                 
1825                 /* see if we should turn off instead... */
1826                 for (ale = anim_data.first; ale; ale = ale->next) {
1827                         /* set the setting in the appropriate way (if available) */
1828                         if (ANIM_channel_setting_get(ac, ale, setting) > 0) {
1829                                 mode = ACHANNEL_SETFLAG_CLEAR;
1830                                 break;
1831                         }
1832                 }
1833         }
1834         
1835         /* apply the setting */
1836         for (ale = anim_data.first; ale; ale = ale->next) {
1837                 /* skip channel if setting is not available */
1838                 if (ANIM_channel_setting_get(ac, ale, setting) == -1)
1839                         continue;
1840                 
1841                 /* set the setting in the appropriate way */
1842                 ANIM_channel_setting_set(ac, ale, setting, mode);
1843                 
1844                 /* if flush status... */
1845                 if (flush)
1846                         ANIM_flush_setting_anim_channels(ac, &all_data, ale, setting, mode);
1847         }
1848         
1849         ANIM_animdata_freelist(&anim_data);
1850         BLI_freelistN(&all_data);
1851 }
1852
1853 /* ------------------- */
1854
1855 static int animchannels_setflag_exec(bContext *C, wmOperator *op)
1856 {
1857         bAnimContext ac;
1858         eAnimChannel_Settings setting;
1859         eAnimChannels_SetFlag mode;
1860         bool flush = true;
1861         
1862         /* get editor data */
1863         if (ANIM_animdata_get_context(C, &ac) == 0)
1864                 return OPERATOR_CANCELLED;
1865                 
1866         /* mode (eAnimChannels_SetFlag), setting (eAnimChannel_Settings) */
1867         mode = RNA_enum_get(op->ptr, "mode");
1868         setting = RNA_enum_get(op->ptr, "type");
1869         
1870         /* check if setting is flushable */
1871         if (setting == ACHANNEL_SETTING_EXPAND)
1872                 flush = false;
1873         
1874         /* modify setting 
1875          *      - only selected channels are affected
1876          */
1877         setflag_anim_channels(&ac, setting, mode, true, flush);
1878         
1879         /* send notifier that things have changed */
1880         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1881         
1882         return OPERATOR_FINISHED;
1883 }
1884
1885 /* duplicate of 'ANIM_OT_channels_setting_toggle' for menu title only, weak! */
1886 static void ANIM_OT_channels_setting_enable(wmOperatorType *ot)
1887 {
1888         PropertyRNA *prop;
1889         
1890         /* identifiers */
1891         ot->name = "Enable Channel Setting";
1892         ot->idname = "ANIM_OT_channels_setting_enable";
1893         ot->description = "Enable specified setting on all selected animation channels";
1894         
1895         /* api callbacks */
1896         ot->invoke = WM_menu_invoke;
1897         ot->exec = animchannels_setflag_exec;
1898         ot->poll = animedit_poll_channels_active;
1899         
1900         /* flags */
1901         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1902         
1903         /* props */
1904         /* flag-setting mode */
1905         prop = RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_ADD, "Mode", "");
1906         RNA_def_property_flag(prop, PROP_HIDDEN);
1907         /* setting to set */
1908         ot->prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
1909 }
1910 /* duplicate of 'ANIM_OT_channels_setting_toggle' for menu title only, weak! */
1911 static void ANIM_OT_channels_setting_disable(wmOperatorType *ot)
1912 {
1913         PropertyRNA *prop;
1914         
1915         /* identifiers */
1916         ot->name = "Disable Channel Setting";
1917         ot->idname = "ANIM_OT_channels_setting_disable";
1918         ot->description = "Disable specified setting on all selected animation channels";
1919         
1920         /* api callbacks */
1921         ot->invoke = WM_menu_invoke;
1922         ot->exec = animchannels_setflag_exec;
1923         ot->poll = animedit_poll_channels_active;
1924         
1925         /* flags */
1926         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1927         
1928         /* props */
1929         /* flag-setting mode */
1930         prop = RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_CLEAR, "Mode", "");
1931         RNA_def_property_flag(prop, PROP_HIDDEN); /* internal hack - don't expose */
1932         /* setting to set */
1933         ot->prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
1934 }
1935
1936 static void ANIM_OT_channels_setting_toggle(wmOperatorType *ot)
1937 {
1938         PropertyRNA *prop;
1939         
1940         /* identifiers */
1941         ot->name = "Toggle Channel Setting";
1942         ot->idname = "ANIM_OT_channels_setting_toggle";
1943         ot->description = "Toggle specified setting on all selected animation channels";
1944         
1945         /* api callbacks */
1946         ot->invoke = WM_menu_invoke;
1947         ot->exec = animchannels_setflag_exec;
1948         ot->poll = animedit_poll_channels_active;
1949         
1950         /* flags */
1951         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1952         
1953         /* props */
1954         /* flag-setting mode */
1955         prop = RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_TOGGLE, "Mode", "");
1956         RNA_def_property_flag(prop, PROP_HIDDEN); /* internal hack - don't expose */
1957         /* setting to set */
1958         ot->prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
1959 }
1960
1961 static void ANIM_OT_channels_editable_toggle(wmOperatorType *ot)
1962 {
1963         PropertyRNA *prop;
1964         
1965         /* identifiers */
1966         ot->name = "Toggle Channel Editability";
1967         ot->idname = "ANIM_OT_channels_editable_toggle";
1968         ot->description = "Toggle editability of selected channels";
1969         
1970         /* api callbacks */
1971         ot->exec = animchannels_setflag_exec;
1972         ot->poll = animedit_poll_channels_active;
1973         
1974         /* flags */
1975         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1976         
1977         /* props */
1978         /* flag-setting mode */
1979         RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_TOGGLE, "Mode", "");
1980         /* setting to set */
1981         prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, ACHANNEL_SETTING_PROTECT, "Type", "");
1982         RNA_def_property_flag(prop, PROP_HIDDEN); /* internal hack - don't expose */
1983 }
1984
1985 /* ********************** Expand Channels Operator *********************** */
1986
1987 static int animchannels_expand_exec(bContext *C, wmOperator *op)
1988 {
1989         bAnimContext ac;
1990         bool onlysel = true;
1991         
1992         /* get editor data */
1993         if (ANIM_animdata_get_context(C, &ac) == 0)
1994                 return OPERATOR_CANCELLED;
1995                 
1996         /* only affect selected channels? */
1997         if (RNA_boolean_get(op->ptr, "all"))
1998                 onlysel = false;
1999         
2000         /* modify setting */
2001         setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_ADD, onlysel, false);
2002         
2003         /* send notifier that things have changed */
2004         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
2005         
2006         return OPERATOR_FINISHED;
2007 }
2008
2009 static void ANIM_OT_channels_expand(wmOperatorType *ot)
2010 {
2011         /* identifiers */
2012         ot->name = "Expand Channels";
2013         ot->idname = "ANIM_OT_channels_expand";
2014         ot->description = "Expand (i.e. open) all selected expandable animation channels";
2015         
2016         /* api callbacks */
2017         ot->exec = animchannels_expand_exec;
2018         ot->poll = animedit_poll_channels_active;
2019         
2020         /* flags */
2021         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2022         
2023         /* props */
2024         ot->prop = RNA_def_boolean(ot->srna, "all", 1, "All", "Expand all channels (not just selected ones)");
2025 }
2026
2027 /* ********************** Collapse Channels Operator *********************** */
2028
2029 static int animchannels_collapse_exec(bContext *C, wmOperator *op)
2030 {
2031         bAnimContext ac;
2032         bool onlysel = true;
2033         
2034         /* get editor data */
2035         if (ANIM_animdata_get_context(C, &ac) == 0)
2036                 return OPERATOR_CANCELLED;
2037                 
2038         /* only affect selected channels? */
2039         if (RNA_boolean_get(op->ptr, "all"))
2040                 onlysel = false;
2041         
2042         /* modify setting */
2043         setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_CLEAR, onlysel, false);
2044         
2045         /* send notifier that things have changed */
2046         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
2047         
2048         return OPERATOR_FINISHED;
2049 }
2050
2051 static void ANIM_OT_channels_collapse(wmOperatorType *ot)
2052 {
2053         /* identifiers */
2054         ot->name = "Collapse Channels";
2055         ot->idname = "ANIM_OT_channels_collapse";
2056         ot->description = "Collapse (i.e. close) all selected expandable animation channels";
2057         
2058         /* api callbacks */
2059         ot->exec = animchannels_collapse_exec;
2060         ot->poll = animedit_poll_channels_active;
2061         
2062         /* flags */
2063         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2064         
2065         /* props */
2066         ot->prop = RNA_def_boolean(ot->srna, "all", true, "All", "Collapse all channels (not just selected ones)");
2067 }
2068
2069 /* ************ Remove All "Empty" AnimData Blocks Operator ********* */
2070 /* We define "empty" AnimData blocks here as those which have all 3 of criteria:
2071  *  1) No active action OR that active actions are empty
2072  *     Assuming that all legitimate entries will have an action,
2073  *     and that empty actions
2074  *  2) No NLA Tracks + NLA Strips
2075  *     Assuming that users haven't set up any of these as "placeholders"
2076  *     for convenience sake, and that most that exist were either unintentional
2077  *     or are no longer wanted
2078  *  3) No drivers
2079  */
2080  
2081 static int animchannels_clean_empty_exec(bContext *C, wmOperator *op)
2082 {
2083         bAnimContext ac;
2084         
2085         ListBase anim_data = {NULL, NULL};
2086         bAnimListElem *ale;
2087         int filter;
2088         
2089         /* get editor data */
2090         if (ANIM_animdata_get_context(C, &ac) == 0)
2091                 return OPERATOR_CANCELLED;
2092         
2093         /* get animdata blocks */
2094         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA);
2095         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
2096         
2097         for (ale = anim_data.first; ale; ale = ale->next) {
2098                 ID *id = ale->id;
2099                 AnimData *adt = ale->data;
2100                 
2101                 bool action_empty  = false;
2102                 bool nla_empty     = false;
2103                 bool drivers_empty = false;
2104                 
2105                 /* sanity checks */
2106                 BLI_assert((id != NULL) && (adt != NULL));
2107                 
2108                 /* check if this is "empty" and can be deleted */
2109                 /* (For now, there are only these 3 criteria) */
2110                 
2111                 /* 1) Active Action is missing or empty */
2112                 if (ELEM(NULL, adt->action, adt->action->curves.first)) {
2113                         action_empty = true;
2114                 }
2115                 else {
2116                         /* TODO: check for keyframe + fmodifier data on these too */
2117                 }
2118                 
2119                 /* 2) No NLA Tracks and/or NLA Strips */
2120                 if (adt->nla_tracks.first == NULL) {
2121                         nla_empty = true;
2122                 }
2123                 else {
2124                         NlaTrack *nlt;
2125                         
2126                         /* empty tracks? */
2127                         for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next) {
2128                                 if (nlt->strips.first) {
2129                                         /* stop searching, as we found one that actually had stuff we don't want lost 
2130                                          * NOTE: nla_empty gets reset to false, as a previous track may have been empty
2131                                          */
2132                                         nla_empty = false;
2133                                         break;
2134                                 }
2135                                 else if (nlt->strips.first == NULL) {
2136                                         /* this track is empty, but another one may still have stuff in it, so can't break yet */
2137                                         nla_empty = true;
2138                                 }
2139                         }
2140                 }
2141                 
2142                 /* 3) Drivers */
2143                 drivers_empty = (adt->drivers.first == NULL);
2144                 
2145                 
2146                 /* remove AnimData? */
2147                 if (action_empty && nla_empty && drivers_empty) {
2148                         BKE_free_animdata(id);
2149                 }
2150         }
2151         
2152         /* free temp data */
2153         ANIM_animdata_freelist(&anim_data);
2154         
2155         /* send notifier that things have changed */
2156         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
2157         
2158         return OPERATOR_FINISHED;
2159 }
2160
2161 static void ANIM_OT_channels_clean_empty(wmOperatorType *ot)
2162 {
2163         /* identifiers */
2164         ot->name = "Remove Empty Animation Data";
2165         ot->idname = "ANIM_OT_channels_clean_empty";
2166         ot->description = "Delete all empty animation data containers from visible datablocks";
2167         
2168         /* api callbacks */
2169         ot->exec = animchannels_clean_empty_exec;
2170         ot->poll = animedit_poll_channels_nla_tweakmode_off;
2171         
2172         /* flags */
2173         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2174 }
2175
2176 /* ******************* Reenable Disabled Operator ******************* */
2177
2178 static int animchannels_enable_poll(bContext *C)
2179 {
2180         ScrArea *sa = CTX_wm_area(C);
2181         
2182         /* channels region test */
2183         /* TODO: could enhance with actually testing if channels region? */
2184         if (ELEM(NULL, sa, CTX_wm_region(C)))
2185                 return 0;
2186                 
2187         /* animation editor test - Action/Dopesheet/etc. and Graph only */
2188         if (ELEM(sa->spacetype, SPACE_ACTION, SPACE_IPO) == 0)
2189                 return 0;
2190                 
2191         return 1;
2192 }
2193
2194 static int animchannels_enable_exec(bContext *C, wmOperator *UNUSED(op))
2195 {
2196         bAnimContext ac;
2197         
2198         ListBase anim_data = {NULL, NULL};
2199         bAnimListElem *ale;
2200         int filter;
2201         
2202         /* get editor data */
2203         if (ANIM_animdata_get_context(C, &ac) == 0)
2204                 return OPERATOR_CANCELLED;
2205         
2206         /* filter data */
2207         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_NODUPLIS);
2208         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
2209         
2210         /* loop through filtered data and clean curves */
2211         for (ale = anim_data.first; ale; ale = ale->next) {
2212                 FCurve *fcu = (FCurve *)ale->data;
2213                 
2214                 /* remove disabled flags from F-Curves */
2215                 fcu->flag &= ~FCURVE_DISABLED;
2216                 
2217                 /* for drivers, let's do the same too */
2218                 if (fcu->driver)
2219                         fcu->driver->flag &= ~DRIVER_FLAG_INVALID;
2220                         
2221                 /* tag everything for updates - in particular, this is needed to get drivers working again */
2222                 ale->update |= ANIM_UPDATE_DEPS;
2223         }
2224         
2225         ANIM_animdata_update(&ac, &anim_data);
2226         ANIM_animdata_freelist(&anim_data);
2227                 
2228         /* send notifier that things have changed */
2229         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
2230         
2231         return OPERATOR_FINISHED;
2232 }
2233
2234 static void ANIM_OT_channels_fcurves_enable(wmOperatorType *ot)
2235 {
2236         /* identifiers */
2237         ot->name = "Revive Disabled F-Curves";
2238         ot->idname = "ANIM_OT_channels_fcurves_enable";
2239         ot->description = "Clears 'disabled' tag from all F-Curves to get broken F-Curves working again";
2240         
2241         /* api callbacks */
2242         ot->exec = animchannels_enable_exec;
2243         ot->poll = animchannels_enable_poll;
2244         
2245         /* flags */
2246         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2247 }
2248
2249 /* ****************** Find / Set Filter Operator ******************** */
2250
2251 /* XXX: make this generic? */
2252 static int animchannels_find_poll(bContext *C)
2253 {
2254         ScrArea *sa = CTX_wm_area(C);
2255         
2256         if (sa == NULL)
2257                 return 0;
2258         
2259         /* animation editor with dopesheet */
2260         return ELEM3(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA);
2261 }
2262
2263 /* find_invoke() - Get initial channels */
2264 static int animchannels_find_invoke(bContext *C, wmOperator *op, const wmEvent *evt)
2265 {
2266         bAnimContext ac;
2267         
2268         /* get editor data */
2269         if (ANIM_animdata_get_context(C, &ac) == 0)
2270                 return OPERATOR_CANCELLED;
2271         
2272         /* set initial filter text, and enable filter */
2273         RNA_string_set(op->ptr, "query", ac.ads->searchstr);
2274         
2275         /* defer to popup */
2276         return WM_operator_props_popup(C, op, evt);
2277 }
2278
2279 /* find_exec() -  Called to set the value */
2280 static int animchannels_find_exec(bContext *C, wmOperator *op)
2281 {
2282         bAnimContext ac;
2283         
2284         /* get editor data */
2285         if (ANIM_animdata_get_context(C, &ac) == 0)
2286                 return OPERATOR_CANCELLED;
2287         
2288         /* update filter text, and ensure that filter is enabled if there's something there
2289          * NOTE: we turn the filter off if there's nothing (this is a quick shortcut for dismissing)
2290          */
2291         RNA_string_get(op->ptr, "query", ac.ads->searchstr);
2292         
2293         if (ac.ads->searchstr[0]) {
2294                 ac.ads->filterflag |= ADS_FILTER_BY_FCU_NAME;
2295         }
2296         else {
2297                 ac.ads->filterflag &= ~ADS_FILTER_BY_FCU_NAME;
2298         }
2299         
2300         /* redraw */
2301         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
2302         
2303         return OPERATOR_FINISHED;
2304 }
2305
2306 static void ANIM_OT_channels_find(wmOperatorType *ot)
2307 {
2308         /* identifiers */
2309         ot->name = "Find Channels";
2310         ot->idname = "ANIM_OT_channels_find";
2311         ot->description = "Filter the set of channels shown to only include those with matching names";
2312         
2313         /* callbacks */
2314         ot->invoke = animchannels_find_invoke;
2315         ot->exec = animchannels_find_exec;
2316         ot->poll = animchannels_find_poll;
2317         
2318         /* flags */
2319         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2320         
2321         /* properties */
2322         ot->prop = RNA_def_string(ot->srna, "query", "Query", sizeof(((bDopeSheet *)NULL)->searchstr), "", "Text to search for in channel names");
2323 }
2324
2325 /* ********************** Select All Operator *********************** */
2326
2327 static int animchannels_deselectall_exec(bContext *C, wmOperator *op)
2328 {
2329         bAnimContext ac;
2330         
2331         /* get editor data */
2332         if (ANIM_animdata_get_context(C, &ac) == 0)
2333                 return OPERATOR_CANCELLED;
2334                 
2335         /* 'standard' behavior - check if selected, then apply relevant selection */
2336         if (RNA_boolean_get(op->ptr, "invert"))
2337                 ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, false, ACHANNEL_SETFLAG_TOGGLE);
2338         else
2339                 ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, true, ACHANNEL_SETFLAG_ADD);
2340         
2341         /* send notifier that things have changed */
2342         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL);
2343         
2344         return OPERATOR_FINISHED;
2345 }
2346  
2347 static void ANIM_OT_channels_select_all_toggle(wmOperatorType *ot)
2348 {
2349         /* identifiers */
2350         ot->name = "Select All";
2351         ot->idname = "ANIM_OT_channels_select_all_toggle";
2352         ot->description = "Toggle selection of all animation channels";
2353         
2354         /* api callbacks */
2355         ot->exec = animchannels_deselectall_exec;
2356         ot->poll = animedit_poll_channels_nla_tweakmode_off;
2357         
2358         /* flags */
2359         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2360         
2361         /* props */
2362         ot->prop = RNA_def_boolean(ot->srna, "invert", false, "Invert", "");
2363 }
2364
2365 /* ******************** Borderselect Operator *********************** */
2366
2367 static void borderselect_anim_channels(bAnimContext *ac, rcti *rect, short selectmode)
2368 {
2369         ListBase anim_data = {NULL, NULL};
2370         bAnimListElem *ale;
2371         int filter;
2372         
2373         SpaceNla *snla = (SpaceNla *)ac->sl;
2374         View2D *v2d = &ac->ar->v2d;
2375         rctf rectf;
2376         float ymin, ymax;
2377         
2378         /* set initial y extents */
2379         if (ac->datatype == ANIMCONT_NLA) {
2380                 ymin = (float)(-NLACHANNEL_HEIGHT(snla));
2381                 ymax = 0.0f;
2382         }
2383         else {
2384                 ymin = 0.0f;
2385                 ymax = (float)(-ACHANNEL_HEIGHT);
2386         }
2387         
2388         /* convert border-region to view coordinates */
2389         UI_view2d_region_to_view(v2d, rect->xmin, rect->ymin + 2, &rectf.xmin, &rectf.ymin);
2390         UI_view2d_region_to_view(v2d, rect->xmax, rect->ymax - 2, &rectf.xmax, &rectf.ymax);
2391         
2392         /* filter data */
2393         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
2394         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
2395         
2396         /* loop over data, doing border select */
2397         for (ale = anim_data.first; ale; ale = ale->next) {
2398                 if (ac->datatype == ANIMCONT_NLA)
2399                         ymin = ymax - NLACHANNEL_STEP(snla);
2400                 else
2401                         ymin = ymax - ACHANNEL_STEP;
2402                 
2403                 /* if channel is within border-select region, alter it */
2404                 if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))) {
2405                         /* set selection flags only */
2406                         ANIM_channel_setting_set(ac, ale, ACHANNEL_SETTING_SELECT, selectmode);
2407                         
2408                         /* type specific actions */
2409                         switch (ale->type) {
2410                                 case ANIMTYPE_GROUP:
2411                                 {
2412                                         bActionGroup *agrp = (bActionGroup *)ale->data;
2413                                         
2414                                         /* Armatures-Specific Feature:
2415                                          * See mouse_anim_channels() -> ANIMTYPE_GROUP case for more details (T38737)
2416                                          */
2417                                         if ((ac->ads->filterflag & ADS_FILTER_ONLYSEL) == 0) {
2418                                                 if ((ale->id) && (GS(ale->id->name) == ID_OB)) {
2419                                                         Object *ob = (Object *)ale->id;
2420                                                         
2421                                                         if (ob->type == OB_ARMATURE) {
2422                                                                 /* Assume for now that any group with corresponding name is what we want
2423                                                                  * (i.e. for an armature whose location is animated, things would break
2424                                                                  * if the user were to add a bone named "Location").
2425                                                                  *
2426                                                                  * TODO: check the first F-Curve or so to be sure...
2427                                                                  */
2428                                                                 bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, agrp->name);
2429                                                                 
2430                                                                 if (agrp->flag & AGRP_SELECTED) {
2431                                                                         ED_pose_bone_select(ob, pchan, true);
2432                                                                 }
2433                                                                 else {
2434                                                                         ED_pose_bone_select(ob, pchan, false);
2435                                                                 }
2436                                                         }
2437                                                 }
2438                                         }
2439                                         
2440                                         /* always clear active flag after doing this */
2441                                         agrp->flag &= ~AGRP_ACTIVE;
2442                                         break;
2443                                 }
2444                                 case ANIMTYPE_NLATRACK:
2445                                 {
2446                                         NlaTrack *nlt = (NlaTrack *)ale->data;
2447                                         
2448                                         /* for now, it's easier just to do this here manually, as defining a new type 
2449                                          * currently adds complications when doing other stuff 
2450                                          */
2451                                         ACHANNEL_SET_FLAG(nlt, selectmode, NLATRACK_SELECTED);
2452                                         break;
2453                                 }
2454                         }
2455                 }
2456                 
2457                 /* set minimum extent to be the maximum of the next channel */
2458                 ymax = ymin;
2459         }
2460         
2461         /* cleanup */
2462         ANIM_animdata_freelist(&anim_data);
2463 }
2464
2465 /* ------------------- */
2466
2467 static int animchannels_borderselect_exec(bContext *C, wmOperator *op)
2468 {
2469         bAnimContext ac;
2470         rcti rect;
2471         short selectmode = 0;
2472         int gesture_mode;
2473         bool extend;
2474         
2475         /* get editor data */
2476         if (ANIM_animdata_get_context(C, &ac) == 0)
2477                 return OPERATOR_CANCELLED;
2478         
2479         /* get settings from operator */
2480         WM_operator_properties_border_to_rcti(op, &rect);
2481         
2482         gesture_mode = RNA_int_get(op->ptr, "gesture_mode");
2483         extend = RNA_boolean_get(op->ptr, "extend");
2484
2485         if (!extend)
2486                 ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, true, ACHANNEL_SETFLAG_CLEAR);
2487
2488         if (gesture_mode == GESTURE_MODAL_SELECT)
2489                 selectmode = ACHANNEL_SETFLAG_ADD;
2490         else
2491                 selectmode = ACHANNEL_SETFLAG_CLEAR;
2492         
2493         /* apply borderselect animation channels */
2494         borderselect_anim_channels(&ac, &rect, selectmode);
2495         
2496         /* send notifier that things have changed */
2497         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL);
2498         
2499         return OPERATOR_FINISHED;
2500
2501
2502 static void ANIM_OT_channels_select_border(wmOperatorType *ot)
2503 {
2504         /* identifiers */
2505         ot->name = "Border Select";
2506         ot->idname = "ANIM_OT_channels_select_border";
2507         ot->description = "Select all animation channels within the specified region";
2508         
2509         /* api callbacks */
2510         ot->invoke = WM_border_select_invoke;
2511         ot->exec = animchannels_borderselect_exec;
2512         ot->modal = WM_border_select_modal;
2513         ot->cancel = WM_border_select_cancel;
2514         
2515         ot->poll = animedit_poll_channels_nla_tweakmode_off;
2516         
2517         /* flags */
2518         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2519         
2520         /* rna */
2521         WM_operator_properties_gesture_border(ot, true);
2522 }
2523
2524 /* ******************* Rename Operator ***************************** */
2525 /* Allow renaming some channels by clicking on them */
2526
2527 static void rename_anim_channels(bAnimContext *ac, int channel_index)
2528 {
2529         ListBase anim_data = {NULL, NULL};
2530         bAnimChannelType *acf;
2531         bAnimListElem *ale;
2532         int filter;
2533         
2534         /* get the channel that was clicked on */
2535         /* filter channels */
2536         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
2537         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
2538         
2539         /* get channel from index */
2540         ale = BLI_findlink(&anim_data, channel_index);
2541         if (ale == NULL) {
2542                 /* channel not found */
2543                 if (G.debug & G_DEBUG)
2544                         printf("Error: animation channel (index = %d) not found in rename_anim_channels()\n", channel_index);
2545                 
2546                 ANIM_animdata_freelist(&anim_data);
2547                 return;
2548         }
2549         
2550         /* check that channel can be renamed */
2551         acf = ANIM_channel_get_typeinfo(ale);
2552         if (acf && acf->name_prop) {
2553                 PointerRNA ptr;
2554                 PropertyRNA *prop;
2555                 
2556                 /* ok if we can get name property to edit from this channel */
2557                 if (acf->name_prop(ale, &ptr, &prop)) {
2558                         /* actually showing the rename textfield is done on redraw,
2559                          * so here we just store the index of this channel in the 
2560                          * dopesheet data, which will get utilized when drawing the
2561                          * channel...
2562                          *
2563                          * +1 factor is for backwards compat issues
2564                          */
2565                         if (ac->ads) {
2566                                 ac->ads->renameIndex = channel_index + 1;
2567                         }
2568                 }
2569         }
2570         
2571         /* free temp data and tag for refresh */
2572         ANIM_animdata_freelist(&anim_data);
2573         ED_region_tag_redraw(ac->ar);
2574 }
2575
2576 static int animchannels_rename_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
2577 {
2578         bAnimContext ac;
2579         ARegion *ar;
2580         View2D *v2d;
2581         int channel_index;
2582         float x, y;
2583         
2584         /* get editor data */
2585         if (ANIM_animdata_get_context(C, &ac) == 0)
2586                 return OPERATOR_CANCELLED;
2587                 
2588         /* get useful pointers from animation context data */
2589         ar = ac.ar;
2590         v2d = &ar->v2d;
2591         
2592         /* figure out which channel user clicked in 
2593          * Note: although channels technically start at (y = ACHANNEL_FIRST), we need to adjust by half a channel's height
2594          *              so that the tops of channels get caught ok. Since ACHANNEL_FIRST is really ACHANNEL_HEIGHT, we simply use
2595          *              ACHANNEL_HEIGHT_HALF.
2596          */
2597         UI_view2d_region_to_view(v2d, event->mval[0], event->mval[1], &x, &y);
2598         
2599         if (ac.datatype == ANIMCONT_NLA) {
2600                 SpaceNla *snla = (SpaceNla *)ac.sl;
2601                 UI_view2d_listview_view_to_cell(v2d, NLACHANNEL_NAMEWIDTH, NLACHANNEL_STEP(snla), 0, (float)NLACHANNEL_HEIGHT_HALF(snla), x, y, NULL, &channel_index);
2602         }
2603         else {
2604                 UI_view2d_listview_view_to_cell(v2d, ACHANNEL_NAMEWIDTH, ACHANNEL_STEP, 0, (float)ACHANNEL_HEIGHT_HALF, x, y, NULL, &channel_index);
2605         }
2606         
2607         /* handle click */
2608         rename_anim_channels(&ac, channel_index);
2609         
2610         return OPERATOR_FINISHED;
2611 }
2612
2613 static void ANIM_OT_channels_rename(wmOperatorType *ot)
2614 {
2615         /* identifiers */
2616         ot->name = "Rename Channels";
2617         ot->idname = "ANIM_OT_channels_rename";
2618         ot->description = "Rename animation channel under mouse";
2619         
2620         /* api callbacks */
2621         ot->invoke = animchannels_rename_invoke;
2622         ot->poll = animedit_poll_channels_active;
2623 }
2624
2625 /* ******************** Mouse-Click Operator *********************** */
2626 /* Handle selection changes due to clicking on channels. Settings will get caught by UI code... */
2627
2628 static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index, short selectmode)
2629 {
2630         ListBase anim_data = {NULL, NULL};
2631         bAnimListElem *ale;
2632         int filter;
2633         int notifierFlags = 0;
2634         
2635         /* get the channel that was clicked on */
2636         /* filter channels */
2637         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
2638         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
2639         
2640         /* get channel from index */
2641         ale = BLI_findlink(&anim_data, channel_index);
2642         if (ale == NULL) {
2643                 /* channel not found */
2644                 if (G.debug & G_DEBUG)
2645                         printf("Error: animation channel (index = %d) not found in mouse_anim_channels()\n", channel_index);
2646                 
2647                 ANIM_animdata_freelist(&anim_data);
2648                 return 0;
2649         }
2650
2651         /* selectmode -1 is a special case for ActionGroups only, which selects all of the channels underneath it only... */
2652         /* TODO: should this feature be extended to work with other channel types too? */
2653         if ((selectmode == -1) && (ale->type != ANIMTYPE_GROUP)) {
2654                 /* normal channels should not behave normally in this case */
2655                 ANIM_animdata_freelist(&anim_data);
2656                 return 0;
2657         }
2658
2659         /* action to take depends on what channel we've got */
2660         /* WARNING: must keep this in sync with the equivalent function in nla_channels.c */
2661         switch (ale->type) {
2662                 case ANIMTYPE_SCENE:
2663                 {
2664                         Scene *sce = (Scene *)ale->data;
2665                         AnimData *adt = sce->adt;
2666                         
2667                         /* set selection status */
2668                         if (selectmode == SELECT_INVERT) {
2669                                 /* swap select */
2670                                 sce->flag ^= SCE_DS_SELECTED;
2671                                 if (adt) adt->flag ^= ADT_UI_SELECTED;
2672                         }
2673                         else {
2674                                 sce->flag |= SCE_DS_SELECTED;
2675                                 if (adt) adt->flag |= ADT_UI_SELECTED;
2676                         }
2677                         
2678                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2679                         break;
2680                 }
2681                 case ANIMTYPE_OBJECT:
2682                 {
2683                         bDopeSheet *ads = (bDopeSheet *)ac->data;
2684                         Scene *sce = (Scene *)ads->source;
2685                         Base *base = (Base *)ale->data;
2686                         Object *ob = base->object;
2687                         AnimData *adt = ob->adt;
2688                         
2689                         /* set selection status */
2690                         if (selectmode == SELECT_INVERT) {
2691                                 /* swap select */
2692                                 base->flag ^= SELECT;
2693                                 ob->flag = base->flag;
2694                                 
2695                                 if (adt) adt->flag ^= ADT_UI_SELECTED;
2696                         }
2697                         else {
2698                                 Base *b;
2699                                 
2700                                 /* deselect all */
2701                                 /* TODO: should this deselect all other types of channels too? */
2702                                 for (b = sce->base.first; b; b = b->next) {
2703                                         b->flag &= ~SELECT;
2704                                         b->object->flag = b->flag;
2705                                         if (b->object->adt) b->object->adt->flag &= ~(ADT_UI_SELECTED | ADT_UI_ACTIVE);
2706                                 }
2707                                 
2708                                 /* select object now */
2709                                 base->flag |= SELECT;
2710                                 ob->flag |= SELECT;
2711                                 if (adt) adt->flag |= ADT_UI_SELECTED;
2712                         }
2713                         
2714                         /* change active object - regardless of whether it is now selected [T37883] */
2715                         ED_base_object_activate(C, base); /* adds notifier */
2716                         
2717                         if ((adt) && (adt->flag & ADT_UI_SELECTED))
2718                                 adt->flag |= ADT_UI_ACTIVE;
2719                         
2720                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2721                         break;
2722                 }
2723                 case ANIMTYPE_FILLACTD: /* Action Expander */
2724                 case ANIMTYPE_DSMAT:    /* Datablock AnimData Expanders */
2725                 case ANIMTYPE_DSLAM:
2726                 case ANIMTYPE_DSCAM:
2727                 case ANIMTYPE_DSCUR:
2728                 case ANIMTYPE_DSSKEY:
2729                 case ANIMTYPE_DSWOR:
2730                 case ANIMTYPE_DSPART:
2731                 case ANIMTYPE_DSMBALL:
2732                 case ANIMTYPE_DSARM:
2733                 case ANIMTYPE_DSMESH:
2734                 case ANIMTYPE_DSNTREE:
2735                 case ANIMTYPE_DSTEX:
2736                 case ANIMTYPE_DSLAT:
2737                 case ANIMTYPE_DSLINESTYLE:
2738                 case ANIMTYPE_DSSPK:
2739                 {
2740                         /* sanity checking... */
2741                         if (ale->adt) {
2742                                 /* select/deselect */
2743                                 if (selectmode == SELECT_INVERT) {
2744                                         /* inverse selection status of this AnimData block only */
2745                                         ale->adt->flag ^= ADT_UI_SELECTED;
2746                                 }
2747                                 else {
2748                                         /* select AnimData block by itself */
2749                                         ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
2750                                         ale->adt->flag |= ADT_UI_SELECTED;
2751                                 }
2752                                 
2753                                 /* set active? */
2754                                 if ((ale->adt) && (ale->adt->flag & ADT_UI_SELECTED))
2755                                         ale->adt->flag |= ADT_UI_ACTIVE;
2756                         }
2757                         
2758                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2759                         break;
2760                 }
2761                 case ANIMTYPE_GROUP: 
2762                 {
2763                         bActionGroup *agrp = (bActionGroup *)ale->data;
2764                         
2765                         Object *ob = NULL;
2766                         bPoseChannel *pchan = NULL;
2767                         
2768                         
2769                         /* Armatures-Specific Feature:
2770                          * Since groups are used to collect F-Curves of the same Bone by default
2771                          * (via Keying Sets) so that they can be managed better, we try to make
2772                          * things here easier for animators by mapping group selection to bone
2773                          * selection. 
2774                          *
2775                          * Only do this if "Only Selected" dopesheet filter is not active, or else it
2776                          * becomes too unpredictable/tricky to manage
2777                          */
2778                         if ((ac->ads->filterflag & ADS_FILTER_ONLYSEL) == 0) {
2779                                 if ((ale->id) && (GS(ale->id->name) == ID_OB)) {
2780                                         ob = (Object *)ale->id;
2781                                         
2782                                         if (ob->type == OB_ARMATURE) {
2783                                                 /* Assume for now that any group with corresponding name is what we want
2784                                                  * (i.e. for an armature whose location is animated, things would break
2785                                                  * if the user were to add a bone named "Location").
2786                                                  *
2787                                                  * TODO: check the first F-Curve or so to be sure...
2788                                                  */
2789                                                 pchan = BKE_pose_channel_find_name(ob->pose, agrp->name);
2790                                         }       
2791                                 }
2792                         }
2793                         
2794                         /* select/deselect group */
2795                         if (selectmode == SELECT_INVERT) {
2796                                 /* inverse selection status of this group only */
2797                                 agrp->flag ^= AGRP_SELECTED;
2798                         }
2799                         else if (selectmode == -1) {
2800                                 /* select all in group (and deselect everthing else) */
2801                                 FCurve *fcu;
2802                                 
2803                                 /* deselect all other channels */
2804                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
2805                                 if (pchan) ED_pose_deselectall(ob, 0);
2806                                 
2807                                 /* only select channels in group and group itself */
2808                                 for (fcu = agrp->channels.first; fcu && fcu->grp == agrp; fcu = fcu->next)
2809                                         fcu->flag |= FCURVE_SELECTED;
2810                                 agrp->flag |= AGRP_SELECTED;
2811                         }
2812                         else {
2813                                 /* select group by itself */
2814                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
2815                                 if (pchan) ED_pose_deselectall(ob, 0);
2816                                 
2817                                 agrp->flag |= AGRP_SELECTED;
2818                         }
2819                         
2820                         /* if group is selected now, make group the 'active' one in the visible list */
2821                         if (agrp->flag & AGRP_SELECTED) {
2822                                 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, agrp, ANIMTYPE_GROUP);
2823                                 if (pchan) ED_pose_bone_select(ob, pchan, true);
2824                         }
2825                         else {
2826                                 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, NULL, ANIMTYPE_GROUP);
2827                                 if (pchan) ED_pose_bone_select(ob, pchan, false);
2828                         }
2829                                 
2830                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2831                         break;
2832                 }
2833                 case ANIMTYPE_FCURVE: 
2834                 {
2835                         FCurve *fcu = (FCurve *)ale->data;
2836                         
2837                         /* select/deselect */
2838                         if (selectmode == SELECT_INVERT) {
2839                                 /* inverse selection status of this F-Curve only */
2840                                 fcu->flag ^= FCURVE_SELECTED;
2841                         }
2842                         else {
2843                                 /* select F-Curve by itself */
2844                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
2845                                 fcu->flag |= FCURVE_SELECTED;
2846                         }
2847                         
2848                         /* if F-Curve is selected now, make F-Curve the 'active' one in the visible list */
2849                         if (fcu->flag & FCURVE_SELECTED)
2850                                 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, fcu, ANIMTYPE_FCURVE);
2851                                 
2852                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2853                         break;
2854                 }
2855                 case ANIMTYPE_SHAPEKEY: 
2856                 {
2857                         KeyBlock *kb = (KeyBlock *)ale->data;
2858                         
2859                         /* select/deselect */
2860                         if (selectmode == SELECT_INVERT) {
2861                                 /* inverse selection status of this ShapeKey only */
2862                                 kb->flag ^= KEYBLOCK_SEL;
2863                         }
2864                         else {
2865                                 /* select ShapeKey by itself */
2866                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
2867                                 kb->flag |= KEYBLOCK_SEL;
2868                         }
2869                                 
2870                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2871                         break;
2872                 }
2873                 case ANIMTYPE_GPDATABLOCK:
2874                 {
2875                         bGPdata *gpd = (bGPdata *)ale->data;
2876                         
2877                         /* toggle expand 
2878                          *      - although the triangle widget already allows this, the whole channel can also be used for this purpose
2879                          */
2880                         gpd->flag ^= GP_DATA_EXPAND;
2881                         
2882                         notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
2883                         break;
2884                 }
2885                 case ANIMTYPE_GPLAYER:
2886                 {
2887                         bGPDlayer *gpl = (bGPDlayer *)ale->data;
2888                         
2889                         /* select/deselect */
2890                         if (selectmode == SELECT_INVERT) {
2891                                 /* invert selection status of this layer only */
2892                                 gpl->flag ^= GP_LAYER_SELECT;
2893                         }
2894                         else {
2895                                 /* select layer by itself */
2896                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
2897                                 gpl->flag |= GP_LAYER_SELECT;
2898                         }
2899                         
2900                         notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
2901                         break;
2902                 }
2903                 case ANIMTYPE_MASKDATABLOCK:
2904                 {
2905                         Mask *mask = (Mask *)ale->data;
2906                         
2907                         /* toggle expand
2908                          *      - although the triangle widget already allows this, the whole channel can also be used for this purpose