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