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