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