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