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