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