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