Previous commit uncovered another bug - Ungrouped FCurves couldn't be rearranged...
[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 /* typedef for channel rearranging function 
773  * < list: list that channels belong to
774  * < island: island to be moved
775  * > return[0]: whether operation was a success
776  */
777 typedef bool (*AnimChanRearrangeFp)(ListBase *list, tReorderChannelIsland *island);
778
779 /* get rearranging function, given 'rearrange' mode */
780 static AnimChanRearrangeFp rearrange_get_mode_func(short mode)
781 {
782         switch (mode) {
783                 case REARRANGE_ANIMCHAN_TOP:
784                         return rearrange_island_top;
785                 case REARRANGE_ANIMCHAN_UP:
786                         return rearrange_island_up;
787                 case REARRANGE_ANIMCHAN_DOWN:
788                         return rearrange_island_down;
789                 case REARRANGE_ANIMCHAN_BOTTOM:
790                         return rearrange_island_bottom;
791                 default:
792                         return NULL;
793         }
794 }
795
796 /* Rearrange Islands Generics ------------------------------------- */
797
798 /* add channel into list of islands */
799 static void rearrange_animchannel_add_to_islands(ListBase *islands, ListBase *srcList, Link *channel, short type, const bool is_hidden)
800 {
801         tReorderChannelIsland *island = islands->last;  /* always try to add to last island if possible */
802         bool is_sel = false, is_untouchable = false;
803         
804         /* get flags - selected and untouchable from the channel */
805         switch (type) {
806                 case ANIMTYPE_GROUP:
807                 {
808                         bActionGroup *agrp = (bActionGroup *)channel;
809                         
810                         is_sel = SEL_AGRP(agrp);
811                         is_untouchable = (agrp->flag & AGRP_TEMP) != 0;
812                         break;
813                 }
814                 case ANIMTYPE_FCURVE:
815                 {
816                         FCurve *fcu = (FCurve *)channel;
817                         
818                         is_sel = SEL_FCU(fcu);
819                         break;
820                 }
821                 case ANIMTYPE_NLATRACK:
822                 {
823                         NlaTrack *nlt = (NlaTrack *)channel;
824                         
825                         is_sel = SEL_NLT(nlt);
826                         break;
827                 }
828                 default:
829                         printf("rearrange_animchannel_add_to_islands(): don't know how to handle channels of type %d\n", type);
830                         return;
831         }
832         
833         /* do we need to add to a new island? */
834         if (/* 1) no islands yet */
835             (island == NULL) ||
836             /* 2) unselected islands have single channels only - to allow up/down movement */
837             ((island->flag & REORDER_ISLAND_SELECTED) == 0) ||
838             /* 3) if channel is unselected, stop existing island (it was either wrong sel status, or full already) */
839             (is_sel == 0) ||
840             /* 4) hidden status changes */
841             ((island->flag & REORDER_ISLAND_HIDDEN) != is_hidden)
842            )
843         {
844                 /* create a new island now */
845                 island = MEM_callocN(sizeof(tReorderChannelIsland), "tReorderChannelIsland");
846                 BLI_addtail(islands, island);
847                 
848                 if (is_sel)
849                         island->flag |= REORDER_ISLAND_SELECTED;
850                 if (is_untouchable)
851                         island->flag |= REORDER_ISLAND_UNTOUCHABLE;
852                 if (is_hidden)
853                         island->flag |= REORDER_ISLAND_HIDDEN;
854         }
855
856         /* add channel to island - need to remove it from its existing list first though */
857         BLI_remlink(srcList, channel);
858         BLI_addtail(&island->channels, channel);
859 }
860
861 /* flatten islands out into a single list again */
862 static void rearrange_animchannel_flatten_islands(ListBase *islands, ListBase *srcList)
863 {
864         tReorderChannelIsland *island, *isn = NULL;
865         
866         /* make sure srcList is empty now */
867         BLI_assert(BLI_listbase_is_empty(srcList));
868         
869         /* go through merging islands */
870         for (island = islands->first; island; island = isn) {
871                 isn = island->next;
872                 
873                 /* merge island channels back to main list, then delete the island */
874                 BLI_movelisttolist(srcList, &island->channels);
875                 BLI_freelinkN(islands, island);
876         }
877 }
878
879 /* ............................. */
880
881 static void rearrange_animchannels_filter_visible(ListBase *anim_data_visible, bAnimContext *ac, short type)
882 {
883         ListBase anim_data = {NULL, NULL};
884         bAnimListElem *ale, *ale_next;
885     int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
886         
887         /* get all visible channels */
888     ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
889         
890         /* now, only keep the ones that are of the types we are interested in */
891         for (ale = anim_data.first; ale; ale = ale_next) {
892                 ale_next = ale->next;
893                 
894                 if (ale->type != type) {
895                         BLI_freelinkN(&anim_data, ale);
896                 }
897         }
898         
899         /* return cleaned up list */
900         *anim_data_visible = anim_data;
901 }
902
903 /* performing rearranging of channels using islands */
904 static bool rearrange_animchannel_islands(ListBase *list, AnimChanRearrangeFp rearrange_func,
905                                           short mode, short type, ListBase *anim_data_visible)
906 {
907         ListBase islands = {NULL, NULL};
908         Link *channel, *chanNext = NULL;
909         bool done = false;
910         
911         /* don't waste effort on an empty list */
912         if (BLI_listbase_is_empty(list))
913                 return 0;
914         
915         /* group channels into islands */
916         for (channel = list->first; channel; channel = chanNext) {
917                 /* find out whether this channel is present in anim_data_visible or not! */
918                 const bool is_hidden = (BLI_findptr(anim_data_visible, channel, offsetof(bAnimListElem, data)) == NULL);
919                 chanNext = channel->next;
920                 rearrange_animchannel_add_to_islands(&islands, list, channel, type, is_hidden);
921         }
922         
923         /* perform moving of selected islands now, but only if there is more than one of 'em so that something will happen 
924          *      - scanning of the list is performed in the opposite direction to the direction we're moving things, so that we 
925          *        shouldn't need to encounter items we've moved already
926          */
927         if (islands.first != islands.last) {
928                 tReorderChannelIsland *first = (mode > 0) ? islands.last : islands.first;
929                 tReorderChannelIsland *island, *isn = NULL;
930                 
931                 for (island = first; island; island = isn) {
932                         isn = (mode > 0) ? island->prev : island->next;
933                         
934                         /* perform rearranging */
935                         if (rearrange_func(&islands, island)) {
936                                 island->flag |= REORDER_ISLAND_MOVED;
937                                 done = true;
938                         }
939                 }
940         }
941         
942         /* ungroup islands */
943         rearrange_animchannel_flatten_islands(&islands, list);
944         
945         /* did we do anything? */
946         return done;
947 }
948
949 /* NLA Specific Stuff ----------------------------------------------------- */
950
951 /* Change the order NLA Tracks within NLA Stack
952  * ! NLA tracks are displayed in opposite order, so directions need care
953  *      mode: REARRANGE_ANIMCHAN_*  
954  */
955 static void rearrange_nla_channels(bAnimContext *ac, AnimData *adt, short mode)
956 {
957         AnimChanRearrangeFp rearrange_func;
958         ListBase anim_data_visible = {NULL, NULL};
959         
960         /* hack: invert mode so that functions will work in right order */
961         mode *= -1;
962         
963         /* get rearranging function */
964         rearrange_func = rearrange_get_mode_func(mode);
965         if (rearrange_func == NULL)
966                 return;
967         
968         /* Filter visible data. */
969         rearrange_animchannels_filter_visible(&anim_data_visible, ac, ANIMTYPE_NLATRACK);
970         
971         /* perform rearranging on tracks list */
972         rearrange_animchannel_islands(&adt->nla_tracks, rearrange_func, mode, ANIMTYPE_NLATRACK, &anim_data_visible);
973         
974         /* free temp data */
975         BLI_freelistN(&anim_data_visible);
976 }
977
978 /* Drivers Specific Stuff ------------------------------------------------- */
979
980 /* Change the order drivers within AnimData block
981  *      mode: REARRANGE_ANIMCHAN_*  
982  */
983 static void rearrange_driver_channels(bAnimContext *ac, AnimData *adt, short mode)
984 {
985         /* get rearranging function */
986         AnimChanRearrangeFp rearrange_func = rearrange_get_mode_func(mode);
987         ListBase anim_data_visible = {NULL, NULL};
988         
989         if (rearrange_func == NULL)
990                 return;
991         
992         /* only consider drivers if they're accessible */
993         if (EXPANDED_DRVD(adt) == 0)
994                 return;
995         
996         /* Filter visible data. */
997         rearrange_animchannels_filter_visible(&anim_data_visible, ac, ANIMTYPE_FCURVE);
998         
999         /* perform rearranging on drivers list (drivers are really just F-Curves) */
1000         rearrange_animchannel_islands(&adt->drivers, rearrange_func, mode, ANIMTYPE_FCURVE, &anim_data_visible);
1001         
1002         /* free temp data */
1003         BLI_freelistN(&anim_data_visible);
1004 }
1005
1006 /* Action Specific Stuff ------------------------------------------------- */
1007
1008 /* make sure all action-channels belong to a group (and clear action's list) */
1009 static void split_groups_action_temp(bAction *act, bActionGroup *tgrp)
1010 {
1011         bActionGroup *agrp;
1012         FCurve *fcu;
1013         
1014         if (act == NULL)
1015                 return;
1016         
1017         /* Separate F-Curves into lists per group */
1018         for (agrp = act->groups.first; agrp; agrp = agrp->next) {
1019                 if (agrp->channels.first) {
1020                         fcu = agrp->channels.last;
1021                         act->curves.first = fcu->next;
1022                         
1023                         fcu = agrp->channels.first;
1024                         fcu->prev = NULL;
1025                         
1026                         fcu = agrp->channels.last;
1027                         fcu->next = NULL;
1028                 }
1029         }
1030         
1031         /* Initialize memory for temp-group */
1032         memset(tgrp, 0, sizeof(bActionGroup));
1033         tgrp->flag |= (AGRP_EXPANDED | AGRP_TEMP);
1034         BLI_strncpy(tgrp->name, "#TempGroup", sizeof(tgrp->name));
1035         
1036         /* Move any action-channels not already moved, to the temp group */
1037         if (act->curves.first) {
1038                 /* start of list */
1039                 fcu = act->curves.first;
1040                 fcu->prev = NULL;
1041                 tgrp->channels.first = fcu;
1042                 act->curves.first = NULL;
1043                 
1044                 /* end of list */
1045                 fcu = act->curves.last;
1046                 fcu->next = NULL;
1047                 tgrp->channels.last = fcu;
1048                 act->curves.last = NULL;
1049                 
1050                 /* ensure that all of these get their group set to this temp group 
1051                  * (so that visibility filtering works)
1052                  */
1053                 for (fcu = tgrp->channels.first; fcu; fcu = fcu->next) {
1054                         fcu->grp = tgrp;
1055                 }
1056         }
1057         
1058         /* Add temp-group to list */
1059         BLI_addtail(&act->groups, tgrp);
1060 }
1061
1062 /* link lists of channels that groups have */
1063 static void join_groups_action_temp(bAction *act)
1064 {
1065         bActionGroup *agrp;
1066         
1067         for (agrp = act->groups.first; agrp; agrp = agrp->next) {
1068                 ListBase tempGroup;
1069                 
1070                 /* add list of channels to action's channels */
1071                 tempGroup = agrp->channels;
1072                 BLI_movelisttolist(&act->curves, &agrp->channels);
1073                 agrp->channels = tempGroup;
1074                 
1075                 /* clear moved flag */
1076                 agrp->flag &= ~AGRP_MOVED;
1077                 
1078                 /* if group was temporary one:
1079                  * - unassign all FCurves which were temporarily added to it
1080                  * - remove from list (but don't free as it's on the stack!)
1081                  */
1082                 if (agrp->flag & AGRP_TEMP) {
1083                         FCurve *fcu;
1084                         
1085                         for (fcu = agrp->channels.first; fcu; fcu = fcu->next) {
1086                                 fcu->grp = NULL;
1087                         }
1088                         
1089                         BLI_remlink(&act->groups, agrp);
1090                         break;
1091                 }
1092         }
1093 }
1094
1095 /* Change the order of anim-channels within action 
1096  *      mode: REARRANGE_ANIMCHAN_*  
1097  */
1098 static void rearrange_action_channels(bAnimContext *ac, bAction *act, short mode)
1099 {
1100         bActionGroup tgrp;
1101         ListBase anim_data_visible = {NULL, NULL};
1102         bool do_channels;
1103         
1104         /* get rearranging function */
1105         AnimChanRearrangeFp rearrange_func = rearrange_get_mode_func(mode);
1106         
1107         if (rearrange_func == NULL)
1108                 return;
1109         
1110         /* make sure we're only operating with groups (vs a mixture of groups+curves) */
1111         split_groups_action_temp(act, &tgrp);
1112         
1113         /* Filter visible data. */
1114         rearrange_animchannels_filter_visible(&anim_data_visible, ac, ANIMTYPE_GROUP);
1115         
1116         /* rearrange groups first 
1117          *      - the group's channels will only get considered if nothing happened when rearranging the groups
1118          *        i.e. the rearrange function returned 0
1119          */
1120         do_channels = (rearrange_animchannel_islands(&act->groups, rearrange_func, mode, ANIMTYPE_GROUP,
1121                                                      &anim_data_visible) == 0);
1122         
1123         /* free temp data */
1124         BLI_freelistN(&anim_data_visible);
1125         
1126         if (do_channels) {
1127                 bActionGroup *agrp;
1128                 
1129                 /* Filter visible data. */
1130                 rearrange_animchannels_filter_visible(&anim_data_visible, ac, ANIMTYPE_FCURVE);
1131                 
1132                 for (agrp = act->groups.first; agrp; agrp = agrp->next) {
1133                         /* only consider F-Curves if they're visible (group expanded) */
1134                         if (EXPANDED_AGRP(ac, agrp)) {
1135                                 rearrange_animchannel_islands(&agrp->channels, rearrange_func, mode, ANIMTYPE_FCURVE,
1136                                                               &anim_data_visible);
1137                         }
1138                 }
1139                 
1140                 /* free temp data */
1141                 BLI_freelistN(&anim_data_visible);
1142         }
1143         
1144         /* assemble lists into one list (and clear moved tags) */
1145         join_groups_action_temp(act);
1146 }
1147
1148 /* ------------------- */
1149
1150 static int animchannels_rearrange_exec(bContext *C, wmOperator *op)
1151 {
1152         bAnimContext ac;
1153         short mode;
1154         
1155         /* get editor data */
1156         if (ANIM_animdata_get_context(C, &ac) == 0)
1157                 return OPERATOR_CANCELLED;
1158                 
1159         /* get mode */
1160         mode = RNA_enum_get(op->ptr, "direction");
1161         
1162         /* method to move channels depends on the editor */
1163         if (ac.datatype == ANIMCONT_GPENCIL) {
1164                 /* Grease Pencil channels */
1165                 printf("Grease Pencil not supported for moving yet\n");
1166         }
1167         else if (ac.datatype == ANIMCONT_MASK) {
1168                 /* Grease Pencil channels */
1169                 printf("Mask does not supported for moving yet\n");
1170         }
1171         else if (ac.datatype == ANIMCONT_ACTION) {
1172                 /* Directly rearrange action's channels */
1173                 rearrange_action_channels(&ac, ac.data, mode);
1174         }
1175         else {
1176                 ListBase anim_data = {NULL, NULL};
1177                 bAnimListElem *ale;
1178                 int filter;
1179                 
1180                 /* get animdata blocks */
1181                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA);
1182                 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1183                 
1184                 for (ale = anim_data.first; ale; ale = ale->next) {
1185                         AnimData *adt = ale->data;
1186                         
1187                         switch (ac.datatype) {
1188                                 case ANIMCONT_NLA: /* NLA-tracks only */
1189                                         rearrange_nla_channels(&ac, adt, mode);
1190                                         break;
1191                                 
1192                                 case ANIMCONT_DRIVERS: /* Drivers list only */
1193                                         rearrange_driver_channels(&ac, adt, mode);
1194                                         break;
1195                                 
1196                                 case ANIMCONT_SHAPEKEY: // DOUBLE CHECK ME...
1197                                 default: /* some collection of actions */
1198                                         if (adt->action)
1199                                                 rearrange_action_channels(&ac, adt->action, mode);
1200                                         else if (G.debug & G_DEBUG)
1201                                                 printf("Animdata has no action\n");
1202                                         break;
1203                         }
1204                 }
1205                 
1206                 /* free temp data */
1207                 ANIM_animdata_freelist(&anim_data);
1208         }
1209         
1210         /* send notifier that things have changed */
1211         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1212         
1213         return OPERATOR_FINISHED;
1214 }
1215
1216 static void ANIM_OT_channels_move(wmOperatorType *ot)
1217 {
1218         /* identifiers */
1219         ot->name = "Move Channels";
1220         ot->idname = "ANIM_OT_channels_move";
1221         ot->description = "Rearrange selected animation channels";
1222         
1223         /* api callbacks */
1224         ot->exec = animchannels_rearrange_exec;
1225         ot->poll = animedit_poll_channels_nla_tweakmode_off;
1226         
1227         /* flags */
1228         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1229         
1230         /* props */
1231         ot->prop = RNA_def_enum(ot->srna, "direction", prop_animchannel_rearrange_types, REARRANGE_ANIMCHAN_DOWN, "Direction", "");
1232 }
1233
1234 /* ******************** Group Channel Operator ************************ */
1235
1236 static int animchannels_grouping_poll(bContext *C)
1237 {
1238         ScrArea *sa = CTX_wm_area(C);
1239         SpaceLink *sl;
1240
1241         /* channels region test */
1242         /* TODO: could enhance with actually testing if channels region? */
1243         if (ELEM(NULL, sa, CTX_wm_region(C)))
1244                 return 0;
1245                 
1246         /* animation editor test - must be suitable modes only */
1247         sl = CTX_wm_space_data(C);
1248         
1249         switch (sa->spacetype) {
1250                 /* supported... */
1251                 case SPACE_ACTION:
1252                 {
1253                         SpaceAction *saction = (SpaceAction *)sl;
1254                         
1255                         /* dopesheet and action only - all others are for other datatypes or have no groups */
1256                         if (ELEM(saction->mode, SACTCONT_ACTION, SACTCONT_DOPESHEET) == 0)
1257                                 return 0;
1258                 }
1259                         break;
1260                         
1261                 case SPACE_IPO:
1262                 {
1263                         SpaceIpo *sipo = (SpaceIpo *)sl;
1264                         
1265                         /* drivers can't have groups... */
1266                         if (sipo->mode != SIPO_MODE_ANIMATION)
1267                                 return 0;
1268                 }
1269                         break;
1270                         
1271                 /* unsupported... */
1272                 default:
1273                         return 0;
1274         }
1275         
1276         return 1;
1277 }
1278
1279 /* ----------------------------------------------------------- */
1280
1281 static void animchannels_group_channels(bAnimContext *ac, bAnimListElem *adt_ref, const char name[])
1282 {       
1283         AnimData *adt = adt_ref->adt;
1284         bAction *act = adt->action;
1285         
1286         if (act) {
1287                 ListBase anim_data = {NULL, NULL};
1288                 int filter;
1289                 
1290                 /* find selected F-Curves to re-group */
1291                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL);
1292                 ANIM_animdata_filter(ac, &anim_data, filter, adt_ref, ANIMCONT_CHANNEL);
1293                 
1294                 if (anim_data.first) {
1295                         bActionGroup *agrp;
1296                         bAnimListElem *ale;
1297                         
1298                         /* create new group, which should now be part of the action */
1299                         agrp = action_groups_add_new(act, name);
1300                         BLI_assert(agrp != NULL);
1301                         
1302                         /* transfer selected F-Curves across to new group  */
1303                         for (ale = anim_data.first; ale; ale = ale->next) {
1304                                 FCurve *fcu = (FCurve *)ale->data;
1305                                 bActionGroup *grp = fcu->grp;
1306                                 
1307                                 /* remove F-Curve from group, then group too if it is now empty */
1308                                 action_groups_remove_channel(act, fcu);
1309                                 
1310                                 if ((grp) && BLI_listbase_is_empty(&grp->channels)) {
1311                                         BLI_freelinkN(&act->groups, grp);
1312                                 }
1313                                 
1314                                 /* add F-Curve to group */
1315                                 action_groups_add_channel(act, agrp, fcu);
1316                         }
1317                 }
1318                 
1319                 /* cleanup */
1320                 ANIM_animdata_freelist(&anim_data);
1321         }
1322 }
1323
1324 static int animchannels_group_exec(bContext *C, wmOperator *op)
1325 {
1326         bAnimContext ac;
1327         char name[MAX_NAME];
1328         
1329         /* get editor data */
1330         if (ANIM_animdata_get_context(C, &ac) == 0)
1331                 return OPERATOR_CANCELLED;
1332         
1333         /* get name for new group */
1334         RNA_string_get(op->ptr, "name", name);
1335         
1336         /* XXX: name for group should never be empty... */
1337         if (name[0]) {
1338                 ListBase anim_data = {NULL, NULL};
1339                 bAnimListElem *ale;
1340                 int filter;
1341                 
1342                 /* handle each animdata block separately, so that the regrouping doesn't flow into blocks  */
1343                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA | ANIMFILTER_NODUPLIS);
1344                 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1345                 
1346                 for (ale = anim_data.first; ale; ale = ale->next) {
1347                         animchannels_group_channels(&ac, ale, name);
1348                 }
1349                 
1350                 /* free temp data */
1351                 ANIM_animdata_freelist(&anim_data);
1352                 
1353                 /* updatss */
1354                 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1355         }
1356         
1357         return OPERATOR_FINISHED;
1358 }
1359
1360 static void ANIM_OT_channels_group(wmOperatorType *ot)
1361 {
1362         /* identifiers */
1363         ot->name = "Group Channels";
1364         ot->idname = "ANIM_OT_channels_group";
1365         ot->description = "Add selected F-Curves to a new group";
1366         
1367         /* callbacks */
1368         ot->invoke = WM_operator_props_popup;
1369         ot->exec = animchannels_group_exec;
1370         ot->poll = animchannels_grouping_poll;
1371         
1372         /* flags */
1373         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1374         
1375         /* props */
1376         ot->prop = RNA_def_string(ot->srna, "name", "New Group", 
1377                                   sizeof(((bActionGroup *)NULL)->name), 
1378                                   "Name", "Name of newly created group");
1379         /* RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE); */ /* XXX: still not too sure about this - keeping same text is confusing... */
1380 }
1381
1382 /* ----------------------------------------------------------- */
1383
1384 static int animchannels_ungroup_exec(bContext *C, wmOperator *UNUSED(op))
1385 {
1386         bAnimContext ac;
1387         
1388         ListBase anim_data = {NULL, NULL};
1389         bAnimListElem *ale;
1390         int filter;
1391         
1392         /* get editor data */
1393         if (ANIM_animdata_get_context(C, &ac) == 0)
1394                 return OPERATOR_CANCELLED;
1395                 
1396         /* just selected F-Curves... */
1397         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_NODUPLIS);
1398         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1399         
1400         for (ale = anim_data.first; ale; ale = ale->next) {
1401                 /* find action for this F-Curve... */
1402                 if (ale->adt && ale->adt->action) {
1403                         FCurve  *fcu = (FCurve *)ale->data;
1404                         bAction *act = ale->adt->action;
1405                         
1406                         /* only proceed to remove if F-Curve is in a group... */
1407                         if (fcu->grp) { 
1408                                 bActionGroup *agrp = fcu->grp;
1409                                 
1410                                 /* remove F-Curve from group and add at tail (ungrouped) */
1411                                 action_groups_remove_channel(act, fcu);
1412                                 BLI_addtail(&act->curves, fcu);
1413                                 
1414                                 /* delete group if it is now empty */
1415                                 if (BLI_listbase_is_empty(&agrp->channels)) {
1416                                         BLI_freelinkN(&act->groups, agrp);
1417                                 }
1418                         }
1419                 }
1420         }
1421         
1422         /* cleanup */
1423         ANIM_animdata_freelist(&anim_data);
1424         
1425         /* updates */
1426         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1427         
1428         return OPERATOR_FINISHED;
1429 }
1430
1431 static void ANIM_OT_channels_ungroup(wmOperatorType *ot)
1432 {
1433         /* identifiers */
1434         ot->name = "Ungroup Channels";
1435         ot->idname = "ANIM_OT_channels_ungroup";
1436         ot->description = "Remove selected F-Curves from their current groups";
1437         
1438         /* callbacks */
1439         ot->exec = animchannels_ungroup_exec;
1440         ot->poll = animchannels_grouping_poll;
1441         
1442         /* flags */
1443         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1444 }
1445
1446 /* ******************** Delete Channel Operator *********************** */
1447
1448 static int animchannels_delete_exec(bContext *C, wmOperator *UNUSED(op))
1449 {
1450         bAnimContext ac;
1451         ListBase anim_data = {NULL, NULL};
1452         bAnimListElem *ale;
1453         int filter;
1454         
1455         /* get editor data */
1456         if (ANIM_animdata_get_context(C, &ac) == 0)
1457                 return OPERATOR_CANCELLED;
1458         
1459         /* cannot delete in shapekey */
1460         if (ac.datatype == ANIMCONT_SHAPEKEY) 
1461                 return OPERATOR_CANCELLED;
1462                 
1463                 
1464         /* do groups only first (unless in Drivers mode, where there are none) */
1465         if (ac.datatype != ANIMCONT_DRIVERS) {
1466                 /* filter data */
1467                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
1468                 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1469                 
1470                 /* delete selected groups and their associated channels */
1471                 for (ale = anim_data.first; ale; ale = ale->next) {
1472                         /* only groups - don't check other types yet, since they may no-longer exist */
1473                         if (ale->type == ANIMTYPE_GROUP) {
1474                                 bActionGroup *agrp = (bActionGroup *)ale->data;
1475                                 AnimData *adt = ale->adt;
1476                                 FCurve *fcu, *fcn;
1477                                 
1478                                 /* skip this group if no AnimData available, as we can't safely remove the F-Curves */
1479                                 if (adt == NULL)
1480                                         continue;
1481                                 
1482                                 /* delete all of the Group's F-Curves, but no others */
1483                                 for (fcu = agrp->channels.first; fcu && fcu->grp == agrp; fcu = fcn) {
1484                                         fcn = fcu->next;
1485                                         
1486                                         /* remove from group and action, then free */
1487                                         action_groups_remove_channel(adt->action, fcu);
1488                                         free_fcurve(fcu);
1489                                 }
1490                                 
1491                                 /* free the group itself */
1492                                 if (adt->action)
1493                                         BLI_freelinkN(&adt->action->groups, agrp);
1494                                 else
1495                                         MEM_freeN(agrp);
1496                         }
1497                 }
1498                 
1499                 /* cleanup */
1500                 ANIM_animdata_freelist(&anim_data);
1501         }
1502         
1503         /* filter data */
1504         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
1505         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1506         
1507         /* delete selected data channels */
1508         for (ale = anim_data.first; ale; ale = ale->next) {
1509                 switch (ale->type) {
1510                         case ANIMTYPE_FCURVE: 
1511                         {
1512                                 /* F-Curves if we can identify its parent */
1513                                 AnimData *adt = ale->adt;
1514                                 FCurve *fcu = (FCurve *)ale->data;
1515                                 
1516                                 /* try to free F-Curve */
1517                                 ANIM_fcurve_delete_from_animdata(&ac, adt, fcu);
1518                                 break;
1519                         }
1520                         case ANIMTYPE_GPLAYER:
1521                         {
1522                                 /* Grease Pencil layer */
1523                                 bGPdata *gpd = (bGPdata *)ale->id;
1524                                 bGPDlayer *gpl = (bGPDlayer *)ale->data;
1525                                 
1526                                 /* try to delete the layer's data and the layer itself */
1527                                 free_gpencil_frames(gpl);
1528                                 BLI_freelinkN(&gpd->layers, gpl);
1529                                 break;
1530                         }
1531                         case ANIMTYPE_MASKLAYER:
1532                         {
1533                                 /* Mask layer */
1534                                 Mask *mask = (Mask *)ale->id;
1535                                 MaskLayer *masklay = (MaskLayer *)ale->data;
1536                                 
1537                                 /* try to delete the layer's data and the layer itself */
1538                                 BKE_mask_layer_remove(mask, masklay);
1539                                 break;
1540                         }
1541                 }
1542         }
1543         
1544         /* cleanup */
1545         ANIM_animdata_freelist(&anim_data);
1546         
1547         /* send notifier that things have changed */
1548         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1549         
1550         return OPERATOR_FINISHED;
1551 }
1552  
1553 static void ANIM_OT_channels_delete(wmOperatorType *ot)
1554 {
1555         /* identifiers */
1556         ot->name = "Delete Channels";
1557         ot->idname = "ANIM_OT_channels_delete";
1558         ot->description = "Delete all selected animation channels";
1559         
1560         /* api callbacks */
1561         ot->exec = animchannels_delete_exec;
1562         ot->poll = animedit_poll_channels_active;
1563         
1564         /* flags */
1565         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1566 }
1567
1568 /* ******************** Set Channel Visibility Operator *********************** */
1569 /* NOTE: this operator is only valid in the Graph Editor channels region */
1570
1571 static int animchannels_visibility_set_exec(bContext *C, wmOperator *UNUSED(op))
1572 {
1573         bAnimContext ac;
1574         ListBase anim_data = {NULL, NULL};
1575         ListBase all_data = {NULL, NULL};
1576         bAnimListElem *ale;
1577         int filter;
1578         
1579         /* get editor data */
1580         if (ANIM_animdata_get_context(C, &ac) == 0)
1581                 return OPERATOR_CANCELLED;
1582         
1583         /* get list of all channels that selection may need to be flushed to 
1584          * - hierarchy mustn't affect what we have access to here...
1585          */
1586         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_NODUPLIS);
1587         ANIM_animdata_filter(&ac, &all_data, filter, ac.data, ac.datatype);
1588                 
1589         /* hide all channels not selected
1590          * - hierarchy matters if we're doing this from the channels region
1591          *   since we only want to apply this to channels we can "see", 
1592          *   and have these affect their relatives
1593          * - but for Graph Editor, this gets used also from main region
1594          *   where hierarchy doesn't apply, as for [#21276]
1595          */
1596         if ((ac.spacetype == SPACE_IPO) && (ac.regiontype != RGN_TYPE_CHANNELS)) {
1597                 /* graph editor (case 2) */
1598                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_UNSEL | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
1599         }
1600         else {
1601                 /* standard case */
1602                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_UNSEL | ANIMFILTER_NODUPLIS);
1603         }
1604         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1605         
1606         for (ale = anim_data.first; ale; ale = ale->next) {
1607                 /* clear setting first */
1608                 ANIM_channel_setting_set(&ac, ale, ACHANNEL_SETTING_VISIBLE, ACHANNEL_SETFLAG_CLEAR);
1609                 
1610                 /* now also flush selection status as appropriate 
1611                  * NOTE: in some cases, this may result in repeat flushing being performed
1612                  */
1613                 ANIM_flush_setting_anim_channels(&ac, &all_data, ale, ACHANNEL_SETTING_VISIBLE, 0);
1614         }
1615         
1616         ANIM_animdata_freelist(&anim_data);
1617         
1618         /* make all the selected channels visible */
1619         filter = (ANIMFILTER_SEL | ANIMFILTER_NODUPLIS);
1620         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1621
1622         for (ale = anim_data.first; ale; ale = ale->next) {
1623                 /* hack: skip object channels for now, since flushing those will always flush everything, but they are always included */
1624                 /* TODO: find out why this is the case, and fix that */
1625                 if (ale->type == ANIMTYPE_OBJECT)
1626                         continue;
1627                 
1628                 /* enable the setting */
1629                 ANIM_channel_setting_set(&ac, ale, ACHANNEL_SETTING_VISIBLE, ACHANNEL_SETFLAG_ADD);
1630                 
1631                 /* now, also flush selection status up/down as appropriate */
1632                 ANIM_flush_setting_anim_channels(&ac, &all_data, ale, ACHANNEL_SETTING_VISIBLE, 1);
1633         }
1634         
1635         ANIM_animdata_freelist(&anim_data);
1636         BLI_freelistN(&all_data);
1637         
1638         
1639         /* send notifier that things have changed */
1640         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1641         
1642         return OPERATOR_FINISHED;
1643 }
1644
1645 static void ANIM_OT_channels_visibility_set(wmOperatorType *ot)
1646 {
1647         /* identifiers */
1648         ot->name = "Set Visibility";
1649         ot->idname = "ANIM_OT_channels_visibility_set";
1650         ot->description = "Make only the selected animation channels visible in the Graph Editor";
1651         
1652         /* api callbacks */
1653         ot->exec = animchannels_visibility_set_exec;
1654         ot->poll = ED_operator_graphedit_active;
1655         
1656         /* flags */
1657         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1658 }
1659
1660
1661 /* ******************** Toggle Channel Visibility Operator *********************** */
1662 /* NOTE: this operator is only valid in the Graph Editor channels region */
1663
1664 static int animchannels_visibility_toggle_exec(bContext *C, wmOperator *UNUSED(op))
1665 {
1666         bAnimContext ac;
1667         ListBase anim_data = {NULL, NULL};
1668         ListBase all_data = {NULL, NULL};
1669         bAnimListElem *ale;
1670         int filter;
1671         short vis = ACHANNEL_SETFLAG_ADD;
1672         
1673         /* get editor data */
1674         if (ANIM_animdata_get_context(C, &ac) == 0)
1675                 return OPERATOR_CANCELLED;
1676                 
1677         /* get list of all channels that selection may need to be flushed to 
1678          * - hierarchy mustn't affect what we have access to here...
1679          */
1680         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_NODUPLIS);
1681         ANIM_animdata_filter(&ac, &all_data, filter, ac.data, ac.datatype);
1682                 
1683         /* filter data
1684          * - restrict this to only applying on settings we can get to in the list
1685          */
1686         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_NODUPLIS);
1687         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1688         
1689         /* See if we should be making showing all selected or hiding */
1690         for (ale = anim_data.first; ale; ale = ale->next) {
1691                 /* set the setting in the appropriate way (if available) */
1692                 if (ANIM_channel_setting_get(&ac, ale, ACHANNEL_SETTING_VISIBLE)) {
1693                         vis = ACHANNEL_SETFLAG_CLEAR;
1694                         break;
1695                 }
1696         }
1697
1698         /* Now set the flags */
1699         for (ale = anim_data.first; ale; ale = ale->next) {
1700                 /* hack: skip object channels for now, since flushing those will always flush everything, but they are always included */
1701                 /* TODO: find out why this is the case, and fix that */
1702                 if (ale->type == ANIMTYPE_OBJECT)
1703                         continue;
1704                 
1705                 /* change the setting */
1706                 ANIM_channel_setting_set(&ac, ale, ACHANNEL_SETTING_VISIBLE, vis);
1707                 
1708                 /* now, also flush selection status up/down as appropriate */
1709                 ANIM_flush_setting_anim_channels(&ac, &all_data, ale, ACHANNEL_SETTING_VISIBLE, (vis == ACHANNEL_SETFLAG_ADD));
1710         }
1711         
1712         /* cleanup */
1713         ANIM_animdata_freelist(&anim_data);
1714         BLI_freelistN(&all_data);
1715         
1716         /* send notifier that things have changed */
1717         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1718         
1719         return OPERATOR_FINISHED;
1720 }
1721
1722 static void ANIM_OT_channels_visibility_toggle(wmOperatorType *ot)
1723 {
1724         /* identifiers */
1725         ot->name = "Toggle Visibility";
1726         ot->idname = "ANIM_OT_channels_visibility_toggle";
1727         ot->description = "Toggle visibility in Graph Editor of all selected animation channels";
1728         
1729         /* api callbacks */
1730         ot->exec = animchannels_visibility_toggle_exec;
1731         ot->poll = ED_operator_graphedit_active;
1732         
1733         /* flags */
1734         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1735 }
1736
1737 /* ********************** Set Flags Operator *********************** */
1738
1739 /* defines for setting animation-channel flags */
1740 static EnumPropertyItem prop_animchannel_setflag_types[] = {
1741         {ACHANNEL_SETFLAG_TOGGLE, "TOGGLE", 0, "Toggle", ""},
1742         {ACHANNEL_SETFLAG_CLEAR, "DISABLE", 0, "Disable", ""},
1743         {ACHANNEL_SETFLAG_ADD, "ENABLE", 0, "Enable", ""},
1744         {ACHANNEL_SETFLAG_INVERT, "INVERT", 0, "Invert", ""},
1745         {0, NULL, 0, NULL, NULL}
1746 };
1747
1748 /* defines for set animation-channel settings */
1749 // TODO: could add some more types, but those are really quite dependent on the mode...
1750 static EnumPropertyItem prop_animchannel_settings_types[] = {
1751         {ACHANNEL_SETTING_PROTECT, "PROTECT", 0, "Protect", ""},
1752         {ACHANNEL_SETTING_MUTE, "MUTE", 0, "Mute", ""},
1753         {0, NULL, 0, NULL, NULL}
1754 };
1755
1756
1757 /* ------------------- */
1758
1759 /* Set/clear a particular flag (setting) for all selected + visible channels 
1760  *      setting: the setting to modify
1761  *      mode: eAnimChannels_SetFlag
1762  *      onlysel: only selected channels get the flag set
1763  */
1764 // TODO: enable a setting which turns flushing on/off?
1765 static void setflag_anim_channels(bAnimContext *ac, short setting, short mode, short onlysel, short flush)
1766 {
1767         ListBase anim_data = {NULL, NULL};
1768         ListBase all_data = {NULL, NULL};
1769         bAnimListElem *ale;
1770         int filter;
1771         
1772         /* filter data that we need if flush is on */
1773         if (flush) {
1774                 /* get list of all channels that selection may need to be flushed to 
1775                  * - hierarchy visibility needs to be ignored so that settings can get flushed
1776                  *   "down" inside closed containers
1777                  */
1778                 filter = ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS;
1779                 ANIM_animdata_filter(ac, &all_data, filter, ac->data, ac->datatype);
1780         }
1781         
1782         /* filter data that we're working on 
1783          * - hierarchy matters if we're doing this from the channels region
1784          *   since we only want to apply this to channels we can "see", 
1785          *   and have these affect their relatives
1786          * - but for Graph Editor, this gets used also from main region
1787          *   where hierarchy doesn't apply [#21276]
1788          */
1789         if ((ac->spacetype == SPACE_IPO) && (ac->regiontype != RGN_TYPE_CHANNELS)) {
1790                 /* graph editor (case 2) */
1791                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
1792         }
1793         else {
1794                 /* standard case */
1795                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_NODUPLIS);
1796         }
1797         if (onlysel) filter |= ANIMFILTER_SEL;
1798         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1799         
1800         /* if toggling, check if disable or enable */
1801         if (mode == ACHANNEL_SETFLAG_TOGGLE) {
1802                 /* default to turn all on, unless we encounter one that's on... */
1803                 mode = ACHANNEL_SETFLAG_ADD;
1804                 
1805                 /* see if we should turn off instead... */
1806                 for (ale = anim_data.first; ale; ale = ale->next) {
1807                         /* set the setting in the appropriate way (if available) */
1808                         if (ANIM_channel_setting_get(ac, ale, setting) > 0) {
1809                                 mode = ACHANNEL_SETFLAG_CLEAR;
1810                                 break;
1811                         }
1812                 }
1813         }
1814         
1815         /* apply the setting */
1816         for (ale = anim_data.first; ale; ale = ale->next) {
1817                 /* skip channel if setting is not available */
1818                 if (ANIM_channel_setting_get(ac, ale, setting) == -1)
1819                         continue;
1820                 
1821                 /* set the setting in the appropriate way */
1822                 ANIM_channel_setting_set(ac, ale, setting, mode);
1823                 
1824                 /* if flush status... */
1825                 if (flush)
1826                         ANIM_flush_setting_anim_channels(ac, &all_data, ale, setting, mode);
1827         }
1828         
1829         ANIM_animdata_freelist(&anim_data);
1830         BLI_freelistN(&all_data);
1831 }
1832
1833 /* ------------------- */
1834
1835 static int animchannels_setflag_exec(bContext *C, wmOperator *op)
1836 {
1837         bAnimContext ac;
1838         short mode, setting;
1839         short flush = 1;
1840         
1841         /* get editor data */
1842         if (ANIM_animdata_get_context(C, &ac) == 0)
1843                 return OPERATOR_CANCELLED;
1844                 
1845         /* mode (eAnimChannels_SetFlag), setting (eAnimChannel_Settings) */
1846         mode = RNA_enum_get(op->ptr, "mode");
1847         setting = RNA_enum_get(op->ptr, "type");
1848         
1849         /* check if setting is flushable */
1850         if (setting == ACHANNEL_SETTING_EXPAND)
1851                 flush = 0;
1852         
1853         /* modify setting 
1854          *      - only selected channels are affected
1855          */
1856         setflag_anim_channels(&ac, setting, mode, 1, flush);
1857         
1858         /* send notifier that things have changed */
1859         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1860         
1861         return OPERATOR_FINISHED;
1862 }
1863
1864 /* duplicate of 'ANIM_OT_channels_setting_toggle' for menu title only, weak! */
1865 static void ANIM_OT_channels_setting_enable(wmOperatorType *ot)
1866 {
1867         PropertyRNA *prop;
1868         
1869         /* identifiers */
1870         ot->name = "Enable Channel Setting";
1871         ot->idname = "ANIM_OT_channels_setting_enable";
1872         ot->description = "Enable specified setting on all selected animation channels";
1873         
1874         /* api callbacks */
1875         ot->invoke = WM_menu_invoke;
1876         ot->exec = animchannels_setflag_exec;
1877         ot->poll = animedit_poll_channels_active;
1878         
1879         /* flags */
1880         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1881         
1882         /* props */
1883         /* flag-setting mode */
1884         prop = RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_ADD, "Mode", "");
1885         RNA_def_property_flag(prop, PROP_HIDDEN);
1886         /* setting to set */
1887         ot->prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
1888 }
1889 /* duplicate of 'ANIM_OT_channels_setting_toggle' for menu title only, weak! */
1890 static void ANIM_OT_channels_setting_disable(wmOperatorType *ot)
1891 {
1892         PropertyRNA *prop;
1893         
1894         /* identifiers */
1895         ot->name = "Disable Channel Setting";
1896         ot->idname = "ANIM_OT_channels_setting_disable";
1897         ot->description = "Disable specified setting on all selected animation channels";
1898         
1899         /* api callbacks */
1900         ot->invoke = WM_menu_invoke;
1901         ot->exec = animchannels_setflag_exec;
1902         ot->poll = animedit_poll_channels_active;
1903         
1904         /* flags */
1905         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1906         
1907         /* props */
1908         /* flag-setting mode */
1909         prop = RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_CLEAR, "Mode", "");
1910         RNA_def_property_flag(prop, PROP_HIDDEN); /* internal hack - don't expose */
1911         /* setting to set */
1912         ot->prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
1913 }
1914
1915 static void ANIM_OT_channels_setting_toggle(wmOperatorType *ot)
1916 {
1917         PropertyRNA *prop;
1918         
1919         /* identifiers */
1920         ot->name = "Toggle Channel Setting";
1921         ot->idname = "ANIM_OT_channels_setting_toggle";
1922         ot->description = "Toggle specified setting on all selected animation channels";
1923         
1924         /* api callbacks */
1925         ot->invoke = WM_menu_invoke;
1926         ot->exec = animchannels_setflag_exec;
1927         ot->poll = animedit_poll_channels_active;
1928         
1929         /* flags */
1930         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1931         
1932         /* props */
1933         /* flag-setting mode */
1934         prop = RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_TOGGLE, "Mode", "");
1935         RNA_def_property_flag(prop, PROP_HIDDEN); /* internal hack - don't expose */
1936         /* setting to set */
1937         ot->prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
1938 }
1939
1940 static void ANIM_OT_channels_editable_toggle(wmOperatorType *ot)
1941 {
1942         PropertyRNA *prop;
1943         
1944         /* identifiers */
1945         ot->name = "Toggle Channel Editability";
1946         ot->idname = "ANIM_OT_channels_editable_toggle";
1947         ot->description = "Toggle editability of selected channels";
1948         
1949         /* api callbacks */
1950         ot->exec = animchannels_setflag_exec;
1951         ot->poll = animedit_poll_channels_active;
1952         
1953         /* flags */
1954         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1955         
1956         /* props */
1957         /* flag-setting mode */
1958         RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_TOGGLE, "Mode", "");
1959         /* setting to set */
1960         prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, ACHANNEL_SETTING_PROTECT, "Type", "");
1961         RNA_def_property_flag(prop, PROP_HIDDEN); /* internal hack - don't expose */
1962 }
1963
1964 /* ********************** Expand Channels Operator *********************** */
1965
1966 static int animchannels_expand_exec(bContext *C, wmOperator *op)
1967 {
1968         bAnimContext ac;
1969         short onlysel = 1;
1970         
1971         /* get editor data */
1972         if (ANIM_animdata_get_context(C, &ac) == 0)
1973                 return OPERATOR_CANCELLED;
1974                 
1975         /* only affect selected channels? */
1976         if (RNA_boolean_get(op->ptr, "all"))
1977                 onlysel = 0;
1978         
1979         /* modify setting */
1980         setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_ADD, onlysel, 0);
1981         
1982         /* send notifier that things have changed */
1983         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1984         
1985         return OPERATOR_FINISHED;
1986 }
1987
1988 static void ANIM_OT_channels_expand(wmOperatorType *ot)
1989 {
1990         /* identifiers */
1991         ot->name = "Expand Channels";
1992         ot->idname = "ANIM_OT_channels_expand";
1993         ot->description = "Expand (i.e. open) all selected expandable animation channels";
1994         
1995         /* api callbacks */
1996         ot->exec = animchannels_expand_exec;
1997         ot->poll = animedit_poll_channels_active;
1998         
1999         /* flags */
2000         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2001         
2002         /* props */
2003         ot->prop = RNA_def_boolean(ot->srna, "all", 1, "All", "Expand all channels (not just selected ones)");
2004 }
2005
2006 /* ********************** Collapse Channels Operator *********************** */
2007
2008 static int animchannels_collapse_exec(bContext *C, wmOperator *op)
2009 {
2010         bAnimContext ac;
2011         short onlysel = 1;
2012         
2013         /* get editor data */
2014         if (ANIM_animdata_get_context(C, &ac) == 0)
2015                 return OPERATOR_CANCELLED;
2016                 
2017         /* only affect selected channels? */
2018         if (RNA_boolean_get(op->ptr, "all"))
2019                 onlysel = 0;
2020         
2021         /* modify setting */
2022         setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_CLEAR, onlysel, 0);
2023         
2024         /* send notifier that things have changed */
2025         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
2026         
2027         return OPERATOR_FINISHED;
2028 }
2029
2030 static void ANIM_OT_channels_collapse(wmOperatorType *ot)
2031 {
2032         /* identifiers */
2033         ot->name = "Collapse Channels";
2034         ot->idname = "ANIM_OT_channels_collapse";
2035         ot->description = "Collapse (i.e. close) all selected expandable animation channels";
2036         
2037         /* api callbacks */
2038         ot->exec = animchannels_collapse_exec;
2039         ot->poll = animedit_poll_channels_active;
2040         
2041         /* flags */
2042         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2043         
2044         /* props */
2045         ot->prop = RNA_def_boolean(ot->srna, "all", 1, "All", "Collapse all channels (not just selected ones)");
2046 }
2047
2048 /* ******************* Reenable Disabled Operator ******************* */
2049
2050 static int animchannels_enable_poll(bContext *C)
2051 {
2052         ScrArea *sa = CTX_wm_area(C);
2053
2054         /* channels region test */
2055         /* TODO: could enhance with actually testing if channels region? */
2056         if (ELEM(NULL, sa, CTX_wm_region(C)))
2057                 return 0;
2058                 
2059         /* animation editor test - Action/Dopesheet/etc. and Graph only */
2060         if (ELEM(sa->spacetype, SPACE_ACTION, SPACE_IPO) == 0)
2061                 return 0;
2062                 
2063         return 1;
2064 }
2065
2066 static int animchannels_enable_exec(bContext *C, wmOperator *UNUSED(op))
2067 {
2068         bAnimContext ac;
2069         
2070         ListBase anim_data = {NULL, NULL};
2071         bAnimListElem *ale;
2072         int filter;
2073         
2074         /* get editor data */
2075         if (ANIM_animdata_get_context(C, &ac) == 0)
2076                 return OPERATOR_CANCELLED;
2077         
2078         /* filter data */
2079         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_NODUPLIS);
2080         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
2081         
2082         /* loop through filtered data and clean curves */
2083         for (ale = anim_data.first; ale; ale = ale->next) {
2084                 FCurve *fcu = (FCurve *)ale->data;
2085                 
2086                 /* remove disabled flags from F-Curves */
2087                 fcu->flag &= ~FCURVE_DISABLED;
2088                 
2089                 /* for drivers, let's do the same too */
2090                 if (fcu->driver)
2091                         fcu->driver->flag &= ~DRIVER_FLAG_INVALID;
2092                         
2093                 /* tag everything for updates - in particular, this is needed to get drivers working again */
2094                 ale->update |= ANIM_UPDATE_DEPS;
2095         }
2096         
2097         ANIM_animdata_update(&ac, &anim_data);
2098         ANIM_animdata_freelist(&anim_data);
2099                 
2100         /* send notifier that things have changed */
2101         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
2102         
2103         return OPERATOR_FINISHED;
2104 }
2105
2106 static void ANIM_OT_channels_fcurves_enable(wmOperatorType *ot)
2107 {
2108         /* identifiers */
2109         ot->name = "Revive Disabled F-Curves";
2110         ot->idname = "ANIM_OT_channels_fcurves_enable";
2111         ot->description = "Clears 'disabled' tag from all F-Curves to get broken F-Curves working again";
2112         
2113         /* api callbacks */
2114         ot->exec = animchannels_enable_exec;
2115         ot->poll = animchannels_enable_poll;
2116         
2117         /* flags */
2118         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2119 }
2120
2121 /* ********************** Select All Operator *********************** */
2122
2123 static int animchannels_deselectall_exec(bContext *C, wmOperator *op)
2124 {
2125         bAnimContext ac;
2126         
2127         /* get editor data */
2128         if (ANIM_animdata_get_context(C, &ac) == 0)
2129                 return OPERATOR_CANCELLED;
2130                 
2131         /* 'standard' behavior - check if selected, then apply relevant selection */
2132         if (RNA_boolean_get(op->ptr, "invert"))
2133                 ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, 0, ACHANNEL_SETFLAG_TOGGLE);
2134         else
2135                 ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, 1, ACHANNEL_SETFLAG_ADD);
2136         
2137         /* send notifier that things have changed */
2138         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL);
2139         
2140         return OPERATOR_FINISHED;
2141 }
2142  
2143 static void ANIM_OT_channels_select_all_toggle(wmOperatorType *ot)
2144 {
2145         /* identifiers */
2146         ot->name = "Select All";
2147         ot->idname = "ANIM_OT_channels_select_all_toggle";
2148         ot->description = "Toggle selection of all animation channels";
2149         
2150         /* api callbacks */
2151         ot->exec = animchannels_deselectall_exec;
2152         ot->poll = animedit_poll_channels_nla_tweakmode_off;
2153         
2154         /* flags */
2155         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2156         
2157         /* props */
2158         ot->prop = RNA_def_boolean(ot->srna, "invert", 0, "Invert", "");
2159 }
2160
2161 /* ******************** Borderselect Operator *********************** */
2162
2163 static void borderselect_anim_channels(bAnimContext *ac, rcti *rect, short selectmode)
2164 {
2165         ListBase anim_data = {NULL, NULL};
2166         bAnimListElem *ale;
2167         int filter;
2168         
2169         SpaceNla *snla = (SpaceNla *)ac->sl;
2170         View2D *v2d = &ac->ar->v2d;
2171         rctf rectf;
2172         float ymin, ymax;
2173         
2174         /* set initial y extents */
2175         if (ac->datatype == ANIMCONT_NLA) {
2176                 ymin = (float)(-NLACHANNEL_HEIGHT(snla));
2177                 ymax = 0.0f;
2178         }
2179         else {
2180                 ymin = 0.0f;
2181                 ymax = (float)(-ACHANNEL_HEIGHT);
2182         }
2183         
2184         /* convert border-region to view coordinates */
2185         UI_view2d_region_to_view(v2d, rect->xmin, rect->ymin + 2, &rectf.xmin, &rectf.ymin);
2186         UI_view2d_region_to_view(v2d, rect->xmax, rect->ymax - 2, &rectf.xmax, &rectf.ymax);
2187         
2188         /* filter data */
2189         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
2190         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
2191         
2192         /* loop over data, doing border select */
2193         for (ale = anim_data.first; ale; ale = ale->next) {
2194                 if (ac->datatype == ANIMCONT_NLA)
2195                         ymin = ymax - NLACHANNEL_STEP(snla);
2196                 else
2197                         ymin = ymax - ACHANNEL_STEP;
2198                 
2199                 /* if channel is within border-select region, alter it */
2200                 if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))) {
2201                         /* set selection flags only */
2202                         ANIM_channel_setting_set(ac, ale, ACHANNEL_SETTING_SELECT, selectmode);
2203                         
2204                         /* type specific actions */
2205                         switch (ale->type) {
2206                                 case ANIMTYPE_GROUP:
2207                                 {
2208                                         bActionGroup *agrp = (bActionGroup *)ale->data;
2209                                         
2210                                         /* Armatures-Specific Feature:
2211                                          * See mouse_anim_channels() -> ANIMTYPE_GROUP case for more details (T38737)
2212                                          */
2213                                         if ((ac->ads->filterflag & ADS_FILTER_ONLYSEL) == 0) {
2214                                                 if ((ale->id) && (GS(ale->id->name) == ID_OB)) {
2215                                                         Object *ob = (Object *)ale->id;
2216                                                         
2217                                                         if (ob->type == OB_ARMATURE) {
2218                                                                 /* Assume for now that any group with corresponding name is what we want
2219                                                                  * (i.e. for an armature whose location is animated, things would break
2220                                                                  * if the user were to add a bone named "Location").
2221                                                                  *
2222                                                                  * TODO: check the first F-Curve or so to be sure...
2223                                                                  */
2224                                                                 bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, agrp->name);
2225                                                                 
2226                                                                 if (agrp->flag & AGRP_SELECTED) {
2227                                                                         ED_pose_bone_select(ob, pchan, true);
2228                                                                 }
2229                                                                 else {
2230                                                                         ED_pose_bone_select(ob, pchan, false);
2231                                                                 }
2232                                                         }
2233                                                 }
2234                                         }
2235                                         
2236                                         /* always clear active flag after doing this */
2237                                         agrp->flag &= ~AGRP_ACTIVE;
2238                                         break;
2239                                 }
2240                                 case ANIMTYPE_NLATRACK:
2241                                 {
2242                                         NlaTrack *nlt = (NlaTrack *)ale->data;
2243                                         
2244                                         /* for now, it's easier just to do this here manually, as defining a new type 
2245                                          * currently adds complications when doing other stuff 
2246                                          */
2247                                         ACHANNEL_SET_FLAG(nlt, selectmode, NLATRACK_SELECTED);
2248                                         break;
2249                                 }
2250                         }
2251                 }
2252                 
2253                 /* set minimum extent to be the maximum of the next channel */
2254                 ymax = ymin;
2255         }
2256         
2257         /* cleanup */
2258         ANIM_animdata_freelist(&anim_data);
2259 }
2260
2261 /* ------------------- */
2262
2263 static int animchannels_borderselect_exec(bContext *C, wmOperator *op)
2264 {
2265         bAnimContext ac;
2266         rcti rect;
2267         short selectmode = 0;
2268         int gesture_mode;
2269         bool extend;
2270         
2271         /* get editor data */
2272         if (ANIM_animdata_get_context(C, &ac) == 0)
2273                 return OPERATOR_CANCELLED;
2274         
2275         /* get settings from operator */
2276         WM_operator_properties_border_to_rcti(op, &rect);
2277         
2278         gesture_mode = RNA_int_get(op->ptr, "gesture_mode");
2279         extend = RNA_boolean_get(op->ptr, "extend");
2280
2281         if (!extend)
2282                 ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, 1, ACHANNEL_SETFLAG_CLEAR);
2283
2284         if (gesture_mode == GESTURE_MODAL_SELECT)
2285                 selectmode = ACHANNEL_SETFLAG_ADD;
2286         else
2287                 selectmode = ACHANNEL_SETFLAG_CLEAR;
2288         
2289         /* apply borderselect animation channels */
2290         borderselect_anim_channels(&ac, &rect, selectmode);
2291         
2292         /* send notifier that things have changed */
2293         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL);
2294         
2295         return OPERATOR_FINISHED;
2296
2297
2298 static void ANIM_OT_channels_select_border(wmOperatorType *ot)
2299 {
2300         /* identifiers */
2301         ot->name = "Border Select";
2302         ot->idname = "ANIM_OT_channels_select_border";
2303         ot->description = "Select all animation channels within the specified region";
2304         
2305         /* api callbacks */
2306         ot->invoke = WM_border_select_invoke;
2307         ot->exec = animchannels_borderselect_exec;
2308         ot->modal = WM_border_select_modal;
2309         ot->cancel = WM_border_select_cancel;
2310         
2311         ot->poll = animedit_poll_channels_nla_tweakmode_off;
2312         
2313         /* flags */
2314         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2315         
2316         /* rna */
2317         WM_operator_properties_gesture_border(ot, true);
2318 }
2319
2320 /* ******************* Rename Operator ***************************** */
2321 /* Allow renaming some channels by clicking on them */
2322
2323 static void rename_anim_channels(bAnimContext *ac, int channel_index)
2324 {
2325         ListBase anim_data = {NULL, NULL};
2326         bAnimChannelType *acf;
2327         bAnimListElem *ale;
2328         int filter;
2329         
2330         /* get the channel that was clicked on */
2331         /* filter channels */
2332         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
2333         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
2334         
2335         /* get channel from index */
2336         ale = BLI_findlink(&anim_data, channel_index);
2337         if (ale == NULL) {
2338                 /* channel not found */
2339                 if (G.debug & G_DEBUG)
2340                         printf("Error: animation channel (index = %d) not found in rename_anim_channels()\n", channel_index);
2341                 
2342                 ANIM_animdata_freelist(&anim_data);
2343                 return;
2344         }
2345         
2346         /* check that channel can be renamed */
2347         acf = ANIM_channel_get_typeinfo(ale);
2348         if (acf && acf->name_prop) {
2349                 PointerRNA ptr;
2350                 PropertyRNA *prop;
2351                 
2352                 /* ok if we can get name property to edit from this channel */
2353                 if (acf->name_prop(ale, &ptr, &prop)) {
2354                         /* actually showing the rename textfield is done on redraw,
2355                          * so here we just store the index of this channel in the 
2356                          * dopesheet data, which will get utilized when drawing the
2357                          * channel...
2358                          *
2359                          * +1 factor is for backwards compat issues
2360                          */
2361                         if (ac->ads) {
2362                                 ac->ads->renameIndex = channel_index + 1;
2363                         }
2364                 }
2365         }
2366         
2367         /* free temp data and tag for refresh */
2368         ANIM_animdata_freelist(&anim_data);
2369         ED_region_tag_redraw(ac->ar);
2370 }
2371
2372 static int animchannels_rename_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
2373 {
2374         bAnimContext ac;
2375         ARegion *ar;
2376         View2D *v2d;
2377         int channel_index;
2378         float x, y;
2379         
2380         /* get editor data */
2381         if (ANIM_animdata_get_context(C, &ac) == 0)
2382                 return OPERATOR_CANCELLED;
2383                 
2384         /* get useful pointers from animation context data */
2385         ar = ac.ar;
2386         v2d = &ar->v2d;
2387         
2388         /* figure out which channel user clicked in 
2389          * Note: although channels technically start at (y = ACHANNEL_FIRST), we need to adjust by half a channel's height
2390          *              so that the tops of channels get caught ok. Since ACHANNEL_FIRST is really ACHANNEL_HEIGHT, we simply use
2391          *              ACHANNEL_HEIGHT_HALF.
2392          */
2393         UI_view2d_region_to_view(v2d, event->mval[0], event->mval[1], &x, &y);
2394         
2395         if (ac.datatype == ANIMCONT_NLA) {
2396                 SpaceNla *snla = (SpaceNla *)ac.sl;
2397                 UI_view2d_listview_view_to_cell(v2d, NLACHANNEL_NAMEWIDTH, NLACHANNEL_STEP(snla), 0, (float)NLACHANNEL_HEIGHT_HALF(snla), x, y, NULL, &channel_index);
2398         }
2399         else {
2400                 UI_view2d_listview_view_to_cell(v2d, ACHANNEL_NAMEWIDTH, ACHANNEL_STEP, 0, (float)ACHANNEL_HEIGHT_HALF, x, y, NULL, &channel_index);
2401         }
2402         
2403         /* handle click */
2404         rename_anim_channels(&ac, channel_index);
2405         
2406         return OPERATOR_FINISHED;
2407 }
2408
2409 static void ANIM_OT_channels_rename(wmOperatorType *ot)
2410 {
2411         /* identifiers */
2412         ot->name = "Rename Channels";
2413         ot->idname = "ANIM_OT_channels_rename";
2414         ot->description = "Rename animation channel under mouse";
2415         
2416         /* api callbacks */
2417         ot->invoke = animchannels_rename_invoke;
2418         ot->poll = animedit_poll_channels_active;
2419 }
2420
2421 /* ******************** Mouse-Click Operator *********************** */
2422 /* Handle selection changes due to clicking on channels. Settings will get caught by UI code... */
2423
2424 static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index, short selectmode)
2425 {
2426         ListBase anim_data = {NULL, NULL};
2427         bAnimListElem *ale;
2428         int filter;
2429         int notifierFlags = 0;
2430         
2431         /* get the channel that was clicked on */
2432         /* filter channels */
2433         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
2434         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
2435         
2436         /* get channel from index */
2437         ale = BLI_findlink(&anim_data, channel_index);
2438         if (ale == NULL) {
2439                 /* channel not found */
2440                 if (G.debug & G_DEBUG)
2441                         printf("Error: animation channel (index = %d) not found in mouse_anim_channels()\n", channel_index);
2442                 
2443                 ANIM_animdata_freelist(&anim_data);
2444                 return 0;
2445         }
2446
2447         /* selectmode -1 is a special case for ActionGroups only, which selects all of the channels underneath it only... */
2448         /* TODO: should this feature be extended to work with other channel types too? */
2449         if ((selectmode == -1) && (ale->type != ANIMTYPE_GROUP)) {
2450                 /* normal channels should not behave normally in this case */
2451                 ANIM_animdata_freelist(&anim_data);
2452                 return 0;
2453         }
2454
2455         /* action to take depends on what channel we've got */
2456         /* WARNING: must keep this in sync with the equivalent function in nla_channels.c */
2457         switch (ale->type) {
2458                 case ANIMTYPE_SCENE:
2459                 {
2460                         Scene *sce = (Scene *)ale->data;
2461                         AnimData *adt = sce->adt;
2462                         
2463                         /* set selection status */
2464                         if (selectmode == SELECT_INVERT) {
2465                                 /* swap select */
2466                                 sce->flag ^= SCE_DS_SELECTED;
2467                                 if (adt) adt->flag ^= ADT_UI_SELECTED;
2468                         }
2469                         else {
2470                                 sce->flag |= SCE_DS_SELECTED;
2471                                 if (adt) adt->flag |= ADT_UI_SELECTED;
2472                         }
2473                         
2474                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2475                         break;
2476                 }
2477                 case ANIMTYPE_OBJECT:
2478                 {
2479                         bDopeSheet *ads = (bDopeSheet *)ac->data;
2480                         Scene *sce = (Scene *)ads->source;
2481                         Base *base = (Base *)ale->data;
2482                         Object *ob = base->object;
2483                         AnimData *adt = ob->adt;
2484                         
2485                         /* set selection status */
2486                         if (selectmode == SELECT_INVERT) {
2487                                 /* swap select */
2488                                 base->flag ^= SELECT;
2489                                 ob->flag = base->flag;
2490                                 
2491                                 if (adt) adt->flag ^= ADT_UI_SELECTED;
2492                         }
2493                         else {
2494                                 Base *b;
2495                                 
2496                                 /* deselect all */
2497                                 /* TODO: should this deselect all other types of channels too? */
2498                                 for (b = sce->base.first; b; b = b->next) {
2499                                         b->flag &= ~SELECT;
2500                                         b->object->flag = b->flag;
2501                                         if (b->object->adt) b->object->adt->flag &= ~(ADT_UI_SELECTED | ADT_UI_ACTIVE);
2502                                 }
2503                                 
2504                                 /* select object now */
2505                                 base->flag |= SELECT;
2506                                 ob->flag |= SELECT;
2507                                 if (adt) adt->flag |= ADT_UI_SELECTED;
2508                         }
2509                         
2510                         /* change active object - regardless of whether it is now selected [T37883] */
2511                         ED_base_object_activate(C, base); /* adds notifier */
2512                         
2513                         if ((adt) && (adt->flag & ADT_UI_SELECTED))
2514                                 adt->flag |= ADT_UI_ACTIVE;
2515                         
2516                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2517                         break;
2518                 }
2519                 case ANIMTYPE_FILLACTD: /* Action Expander */
2520                 case ANIMTYPE_DSMAT:    /* Datablock AnimData Expanders */
2521                 case ANIMTYPE_DSLAM:
2522                 case ANIMTYPE_DSCAM:
2523                 case ANIMTYPE_DSCUR:
2524                 case ANIMTYPE_DSSKEY:
2525                 case ANIMTYPE_DSWOR:
2526                 case ANIMTYPE_DSPART:
2527                 case ANIMTYPE_DSMBALL:
2528                 case ANIMTYPE_DSARM:
2529                 case ANIMTYPE_DSMESH:
2530                 case ANIMTYPE_DSNTREE:
2531                 case ANIMTYPE_DSTEX:
2532                 case ANIMTYPE_DSLAT:
2533                 case ANIMTYPE_DSLINESTYLE:
2534                 case ANIMTYPE_DSSPK:
2535                 {
2536                         /* sanity checking... */
2537                         if (ale->adt) {
2538                                 /* select/deselect */
2539                                 if (selectmode == SELECT_INVERT) {
2540                                         /* inverse selection status of this AnimData block only */
2541                                         ale->adt->flag ^= ADT_UI_SELECTED;
2542                                 }
2543                                 else {
2544                                         /* select AnimData block by itself */
2545                                         ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
2546                                         ale->adt->flag |= ADT_UI_SELECTED;
2547                                 }
2548                                 
2549                                 /* set active? */
2550                                 if ((ale->adt) && (ale->adt->flag & ADT_UI_SELECTED))
2551                                         ale->adt->flag |= ADT_UI_ACTIVE;
2552                         }
2553                         
2554                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2555                         break;
2556                 }
2557                 case ANIMTYPE_GROUP: 
2558                 {
2559                         bActionGroup *agrp = (bActionGroup *)ale->data;
2560                         
2561                         Object *ob = NULL;
2562                         bPoseChannel *pchan = NULL;
2563                         
2564                         
2565                         /* Armatures-Specific Feature:
2566                          * Since groups are used to collect F-Curves of the same Bone by default
2567                          * (via Keying Sets) so that they can be managed better, we try to make
2568                          * things here easier for animators by mapping group selection to bone
2569                          * selection. 
2570                          *
2571                          * Only do this if "Only Selected" dopesheet filter is not active, or else it
2572                          * becomes too unpredictable/tricky to manage
2573                          */
2574                         if ((ac->ads->filterflag & ADS_FILTER_ONLYSEL) == 0) {
2575                                 if ((ale->id) && (GS(ale->id->name) == ID_OB)) {
2576                                         ob = (Object *)ale->id;
2577                                         
2578                                         if (ob->type == OB_ARMATURE) {
2579                                                 /* Assume for now that any group with corresponding name is what we want
2580                                                  * (i.e. for an armature whose location is animated, things would break
2581                                                  * if the user were to add a bone named "Location").
2582                                                  *
2583                                                  * TODO: check the first F-Curve or so to be sure...
2584                                                  */
2585                                                 pchan = BKE_pose_channel_find_name(ob->pose, agrp->name);
2586                                         }       
2587                                 }
2588                         }
2589                         
2590                         /* select/deselect group */
2591                         if (selectmode == SELECT_INVERT) {
2592                                 /* inverse selection status of this group only */
2593                                 agrp->flag ^= AGRP_SELECTED;
2594                         }
2595                         else if (selectmode == -1) {
2596                                 /* select all in group (and deselect everthing else) */
2597                                 FCurve *fcu;
2598                                 
2599                                 /* deselect all other channels */
2600                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
2601                                 if (pchan) ED_pose_deselectall(ob, 0);
2602                                 
2603                                 /* only select channels in group and group itself */
2604                                 for (fcu = agrp->channels.first; fcu && fcu->grp == agrp; fcu = fcu->next)
2605                                         fcu->flag |= FCURVE_SELECTED;
2606                                 agrp->flag |= AGRP_SELECTED;
2607                         }
2608                         else {
2609                                 /* select group by itself */
2610                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
2611                                 if (pchan) ED_pose_deselectall(ob, 0);
2612                                 
2613                                 agrp->flag |= AGRP_SELECTED;
2614                         }
2615                         
2616                         /* if group is selected now, make group the 'active' one in the visible list */
2617                         if (agrp->flag & AGRP_SELECTED) {
2618                                 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, agrp, ANIMTYPE_GROUP);
2619                                 if (pchan) ED_pose_bone_select(ob, pchan, true);
2620                         }
2621                         else {
2622                                 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, NULL, ANIMTYPE_GROUP);
2623                                 if (pchan) ED_pose_bone_select(ob, pchan, false);
2624                         }
2625                                 
2626                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2627                         break;
2628                 }
2629                 case ANIMTYPE_FCURVE: 
2630                 {
2631                         FCurve *fcu = (FCurve *)ale->data;
2632                         
2633                         /* select/deselect */
2634                         if (selectmode == SELECT_INVERT) {
2635                                 /* inverse selection status of this F-Curve only */
2636                                 fcu->flag ^= FCURVE_SELECTED;
2637                         }
2638                         else {
2639                                 /* select F-Curve by itself */
2640                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
2641                                 fcu->flag |= FCURVE_SELECTED;
2642                         }
2643                         
2644                         /* if F-Curve is selected now, make F-Curve the 'active' one in the visible list */
2645                         if (fcu->flag & FCURVE_SELECTED)
2646                                 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, fcu, ANIMTYPE_FCURVE);
2647                                 
2648                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2649                         break;
2650                 }
2651                 case ANIMTYPE_SHAPEKEY: 
2652                 {
2653                         KeyBlock *kb = (KeyBlock *)ale->data;
2654                         
2655                         /* select/deselect */
2656                         if (selectmode == SELECT_INVERT) {
2657                                 /* inverse selection status of this ShapeKey only */
2658                                 kb->flag ^= KEYBLOCK_SEL;
2659                         }
2660                         else {
2661                                 /* select ShapeKey by itself */
2662                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
2663                                 kb->flag |= KEYBLOCK_SEL;
2664                         }
2665                                 
2666                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2667                         break;
2668                 }
2669                 case ANIMTYPE_GPDATABLOCK:
2670                 {
2671                         bGPdata *gpd = (bGPdata *)ale->data;
2672                         
2673                         /* toggle expand 
2674                          *      - although the triangle widget already allows this, the whole channel can also be used for this purpose
2675                          */
2676                         gpd->flag ^= GP_DATA_EXPAND;
2677                         
2678                         notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
2679                         break;
2680                 }
2681                 case ANIMTYPE_GPLAYER:
2682                 {
2683                         bGPDlayer *gpl = (bGPDlayer *)ale->data;
2684                         
2685                         /* select/deselect */
2686                         if (selectmode == SELECT_INVERT) {
2687                                 /* invert selection status of this layer only */
2688                                 gpl->flag ^= GP_LAYER_SELECT;
2689                         }
2690                         else {
2691                                 /* select layer by itself */
2692                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
2693                                 gpl->flag |= GP_LAYER_SELECT;
2694                         }
2695                         
2696                         notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
2697                         break;
2698                 }
2699                 case ANIMTYPE_MASKDATABLOCK:
2700                 {
2701                         Mask *mask = (Mask *)ale->data;
2702                         
2703                         /* toggle expand
2704                          *      - although the triangle widget already allows this, the whole channel can also be used for this purpose
2705                          */
2706                         mask->flag ^= MASK_ANIMF_EXPAND;
2707                         
2708                         notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
2709                         break;
2710                 }
2711                 case ANIMTYPE_MASKLAYER:
2712                 {
2713                         MaskLayer *masklay = (MaskLayer *)ale->data;
2714                         
2715                         /* select/deselect */
2716                         if (selectmode == SELECT_INVERT) {
2717                                 /* invert selection status of this layer only */
2718                                 masklay->flag ^= MASK_LAYERFLAG_SELECT;
2719                         }
2720                         else {
2721                                 /* select layer by itself */
2722                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
2723                                 masklay->flag |= MASK_LAYERFLAG_SELECT;
2724                         }
2725                         
2726                         notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
2727                         break;
2728                 }
2729                 default:
2730                         if (G.debug & G_DEBUG)
2731                                 printf("Error: Invalid channel type in mouse_anim_channels()\n");
2732                         break;
2733         }
2734         
2735         /* free channels */
2736         ANIM_animdata_freelist(&anim_data);
2737         
2738         /* return notifier flags */
2739         return notifierFlags;
2740 }
2741
2742 /* ------------------- */
2743
2744 /* handle clicking */
2745 static int animchannels_mouseclick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2746 {
2747         bAnimContext ac;
2748         ARegion *ar;
2749         View2D *v2d;
2750         int channel_index;
2751         int notifierFlags = 0;
2752         short selectmode;
2753         float x, y;
2754         
2755         
2756         /* get editor data */
2757         if (ANIM_animdata_get_context(C, &ac) == 0)
2758                 return OPERATOR_CANCELLED;
2759                 
2760         /* get useful pointers from animation context data */
2761         ar = ac.ar;
2762         v2d = &ar->v2d;
2763         
2764         /* select mode is either replace (deselect all, then add) or add/extend */
2765         if (RNA_boolean_get(op->ptr, "extend"))
2766                 selectmode = SELECT_INVERT;
2767         else if (RNA_boolean_get(op->ptr, "children_only"))
2768                 selectmode = -1;  /* this is a bit of a special case for ActionGroups only... should it be removed or extended to all instead? */
2769         else
2770                 selectmode = SELECT_REPLACE;
2771         
2772         /* figure out which channel user clicked in 
2773          * Note: although channels technically start at (y = ACHANNEL_FIRST), we need to adjust by half a channel's height
2774          *              so that the tops of channels get caught ok. Since ACHANNEL_FIRST is really ACHANNEL_HEIGHT, we simply use
2775          *              ACHANNEL_HEIGHT_HALF.
2776          */
2777         UI_view2d_region_to_view(v2d, event->mval[0], event->mval[1], &x, &y);
2778         UI_view2d_listview_view_to_cell(v2d, ACHANNEL_NAMEWIDTH, ACHANNEL_STEP, 0, (float)ACHANNEL_HEIGHT_HALF, x, y, NULL, &channel_index);
2779         
2780         /* handle mouse-click in the relevant channel then */
2781         notifierFlags = mouse_anim_channels(C, &ac, channel_index, selectmode);
2782         
2783         /* set notifier that things have changed */
2784         WM_event_add_notifier(C, NC_ANIMATION | notifierFlags, NULL);
2785         
2786         return OPERATOR_FINISHED;
2787 }
2788  
2789 static void ANIM_OT_channels_click(wmOperatorType *ot)
2790 {
2791         PropertyRNA *prop;
2792         
2793         /* identifiers */
2794         ot->name = "Mouse Click on Channels";
2795         ot->idname = "ANIM_OT_channels_click";
2796         ot->description = "Handle mouse-clicks over animation channels";
2797         
2798         /* api callbacks */
2799         ot->invoke = animchannels_mouseclick_invoke;
2800         ot->poll = animedit_poll_channels_active;
2801         
2802         /* flags */
2803         ot->flag = OPTYPE_UNDO;
2804         
2805         /* properties */
2806         /* NOTE: don't save settings, otherwise, can end up with some weird behaviour (sticky extend) */
2807         prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", ""); // SHIFTKEY
2808         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2809         
2810         prop = RNA_def_boolean(ot->srna, "children_only", 0, "Select Children Only", ""); // CTRLKEY|SHIFTKEY
2811         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2812 }
2813
2814 /* ************************************************************************** */
2815 /* Operator Registration */
2816
2817 void ED_operatortypes_animchannels(void)
2818 {
2819         WM_operatortype_append(ANIM_OT_channels_select_all_toggle);
2820         WM_operatortype_append(ANIM_OT_channels_select_border);
2821         
2822         WM_operatortype_append(ANIM_OT_channels_click);
2823         WM_operatortype_append(ANIM_OT_channels_rename);
2824         
2825         WM_operatortype_append(ANIM_OT_channels_setting_enable);
2826         WM_operatortype_append(ANIM_OT_channels_setting_disable);
2827         WM_operatortype_append(ANIM_OT_channels_setting_toggle);
2828
2829         WM_operatortype_append(ANIM_OT_channels_delete);
2830
2831         /* XXX does this need to be a separate operator? */
2832         WM_operatortype_append(ANIM_OT_channels_editable_toggle);
2833         
2834         WM_operatortype_append(ANIM_OT_channels_move);
2835         
2836         WM_operatortype_append(ANIM_OT_channels_expand);
2837         WM_operatortype_append(ANIM_OT_channels_collapse);
2838         
2839         WM_operatortype_append(ANIM_OT_channels_visibility_toggle);
2840         WM_operatortype_append(ANIM_OT_channels_visibility_set);
2841         
2842         WM_operatortype_append(ANIM_OT_channels_fcurves_enable);
2843         
2844         WM_operatortype_append(ANIM_OT_channels_group);
2845         WM_operatortype_append(ANIM_OT_channels_ungroup);
2846 }
2847
2848 // TODO: check on a poll callback for this, to get hotkeys into menus
2849 void ED_keymap_animchannels(wmKeyConfig *keyconf)
2850 {
2851         wmKeyMap *keymap = WM_keymap_find(keyconf, "Animation Channels", 0, 0);
2852         wmKeyMapItem *kmi;
2853
2854         /* selection */
2855         /* click-select */
2856         /* XXX for now, only leftmouse.... */
2857         WM_keymap_add_item(keymap, "ANIM_OT_channels_click", LEFTMOUSE, KM_PRESS, 0, 0);
2858         RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_click", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0)->ptr, "extend", true);
2859         RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_click", LEFTMOUSE, KM_PRESS, KM_CTRL | KM_SHIFT, 0)->ptr, "children_only", true);
2860
2861         /* rename */
2862         WM_keymap_add_item(keymap, "ANIM_OT_channels_rename", LEFTMOUSE, KM_PRESS, KM_CTRL, 0);
2863         
2864         /* deselect all */
2865         WM_keymap_add_item(keymap, "ANIM_OT_channels_select_all_toggle", AKEY, KM_PRESS, 0, 0);
2866         RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_select_all_toggle", IKEY, KM_PRESS, KM_CTRL, 0)->ptr, "invert", true);
2867         
2868         /* borderselect */
2869         WM_keymap_add_item(keymap, "ANIM_OT_channels_select_border", BKEY, KM_PRESS, 0, 0);
2870         WM_keymap_add_item(keymap, "ANIM_OT_channels_select_border", EVT_TWEAK_L, KM_ANY, 0, 0);
2871         
2872         /* delete */
2873         WM_keymap_add_item(keymap, "ANIM_OT_channels_delete", XKEY, KM_PRESS, 0, 0);
2874         WM_keymap_add_item(keymap, "ANIM_OT_channels_delete", DELKEY, KM_PRESS, 0, 0);
2875         
2876         /* settings */
2877         WM_keymap_add_item(keymap, "ANIM_OT_channels_setting_toggle", WKEY, KM_PRESS, KM_SHIFT, 0);
2878         WM_keymap_add_item(keymap, "ANIM_OT_channels_setting_enable", WKEY, KM_PRESS, KM_CTRL | KM_SHIFT, 0);
2879         WM_keymap_add_item(keymap, "ANIM_OT_channels_setting_disable", WKEY, KM_PRESS, KM_ALT, 0);
2880         
2881         /* settings - specialized hotkeys */
2882         WM_keymap_add_item(keymap, "ANIM_OT_channels_editable_toggle", TABKEY, KM_PRESS, 0, 0);
2883         
2884         /* expand/collapse */
2885         WM_keymap_add_item(keymap, "ANIM_OT_channels_expand", PADPLUSKEY, KM_PRESS, 0, 0);
2886         WM_keymap_add_item(keymap, "ANIM_OT_channels_collapse", PADMINUS, KM_PRESS, 0, 0);
2887         
2888         kmi = WM_keymap_add_item(keymap, "ANIM_OT_channels_expand", PADPLUSKEY, KM_PRESS, KM_CTRL, 0);
2889         RNA_boolean_set(kmi->ptr, "all", false);
2890         kmi = WM_keymap_add_item(keymap, "ANIM_OT_channels_collapse", PADMINUS, KM_PRESS, KM_CTRL, 0);
2891         RNA_boolean_set(kmi->ptr, "all", false);
2892
2893         /* rearranging */
2894         RNA_enum_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_move", PAGEUPKEY, KM_PRESS, 0, 0)->ptr, "direction", REARRANGE_ANIMCHAN_UP);
2895         RNA_enum_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_move", PAGEDOWNKEY, KM_PRESS, 0, 0)->ptr, "direction", REARRANGE_ANIMCHAN_DOWN);
2896         RNA_enum_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_move", PAGEUPKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "direction", REARRANGE_ANIMCHAN_TOP);
2897         RNA_enum_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_move", PAGEDOWNKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "direction", REARRANGE_ANIMCHAN_BOTTOM);
2898         
2899         /* grouping */
2900         WM_keymap_add_item(keymap, "ANIM_OT_channels_group", GKEY, KM_PRESS, KM_CTRL, 0);
2901         WM_keymap_add_item(keymap, "ANIM_OT_channels_ungroup", GKEY, KM_PRESS, KM_ALT, 0);
2902         
2903         /* Graph Editor only */
2904         WM_keymap_add_item(keymap, "ANIM_OT_channels_visibility_set", VKEY, KM_PRESS, 0, 0);
2905         WM_keymap_add_item(keymap, "ANIM_OT_channels_visibility_toggle", VKEY, KM_PRESS, KM_SHIFT, 0);
2906 }
2907
2908 /* ************************************************************************** */