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