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