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