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