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