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