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