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