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