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