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