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