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