4 * ***** BEGIN GPL LICENSE BLOCK *****
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.
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.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung
21 * All rights reserved.
23 * Contributor(s): Joshua Leung
25 * ***** END GPL LICENSE BLOCK *****
32 #include "MEM_guardedalloc.h"
34 #include "BLI_blenlib.h"
36 #include "DNA_anim_types.h"
37 #include "DNA_object_types.h"
38 #include "DNA_scene_types.h"
39 #include "DNA_key_types.h"
40 #include "DNA_gpencil_types.h"
42 #include "RNA_access.h"
43 #include "RNA_define.h"
45 #include "BKE_action.h"
46 #include "BKE_fcurve.h"
47 #include "BKE_context.h"
48 #include "BKE_global.h"
50 #include "UI_view2d.h"
52 #include "ED_anim_api.h"
53 #include "ED_keyframes_edit.h" // XXX move the select modes out of there!
54 #include "ED_screen.h"
59 /* ************************************************************************** */
60 /* CHANNELS API - Exposed API */
62 /* -------------------------- Selection ------------------------------------- */
64 /* Set the given animation-channel as the active one for the active context */
65 // TODO: extend for animdata types...
66 void ANIM_set_active_channel (bAnimContext *ac, void *data, short datatype, int filter, void *channel_data, short channel_type)
68 ListBase anim_data = {NULL, NULL};
71 /* try to build list of filtered items */
72 ANIM_animdata_filter(ac, &anim_data, filter, data, datatype);
73 if (anim_data.first == NULL)
76 /* only clear the 'active' flag for the channels of the same type */
77 for (ale= anim_data.first; ale; ale= ale->next) {
78 /* skip if types don't match */
79 if (channel_type != ale->type)
82 /* flag to set depends on type */
86 bActionGroup *agrp= (bActionGroup *)ale->data;
88 ACHANNEL_SET_FLAG(agrp, ACHANNEL_SETFLAG_CLEAR, AGRP_ACTIVE);
93 FCurve *fcu= (FCurve *)ale->data;
95 ACHANNEL_SET_FLAG(fcu, ACHANNEL_SETFLAG_CLEAR, FCURVE_ACTIVE);
98 case ANIMTYPE_NLATRACK:
100 NlaTrack *nlt= (NlaTrack *)ale->data;
102 ACHANNEL_SET_FLAG(nlt, ACHANNEL_SETFLAG_CLEAR, NLATRACK_ACTIVE);
106 case ANIMTYPE_FILLACTD: /* Action Expander */
107 case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
111 case ANIMTYPE_DSSKEY:
113 case ANIMTYPE_DSPART:
114 case ANIMTYPE_DSMBALL:
116 case ANIMTYPE_DSMESH:
118 /* need to verify that this data is valid for now */
120 ACHANNEL_SET_FLAG(ale->adt, ACHANNEL_SETFLAG_CLEAR, ADT_UI_ACTIVE);
127 /* set active flag */
129 switch (channel_type) {
132 bActionGroup *agrp= (bActionGroup *)channel_data;
133 agrp->flag |= AGRP_ACTIVE;
136 case ANIMTYPE_FCURVE:
138 FCurve *fcu= (FCurve *)channel_data;
139 fcu->flag |= FCURVE_ACTIVE;
142 case ANIMTYPE_NLATRACK:
144 NlaTrack *nlt= (NlaTrack *)channel_data;
145 nlt->flag |= NLATRACK_ACTIVE;
149 case ANIMTYPE_FILLACTD: /* Action Expander */
150 case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
154 case ANIMTYPE_DSSKEY:
156 case ANIMTYPE_DSPART:
157 case ANIMTYPE_DSMBALL:
159 case ANIMTYPE_DSMESH:
161 /* need to verify that this data is valid for now */
162 // XXX: ale may be null!
164 ale->adt->flag |= ADT_UI_ACTIVE;
171 BLI_freelistN(&anim_data);
174 /* Deselect all animation channels
175 * - data: pointer to datatype, as contained in bAnimContext
176 * - datatype: the type of data that 'data' represents (eAnimCont_Types)
177 * - test: check if deselecting instead of selecting
178 * - sel: eAnimChannels_SetFlag;
180 void ANIM_deselect_anim_channels (bAnimContext *ac, void *data, short datatype, short test, short sel)
182 ListBase anim_data = {NULL, NULL};
187 filter= ANIMFILTER_VISIBLE|ANIMFILTER_CHANNELS;
188 ANIM_animdata_filter(ac, &anim_data, filter, data, datatype);
190 /* See if we should be selecting or deselecting */
192 for (ale= anim_data.first; ale; ale= ale->next) {
198 if (ale->flag & SCE_DS_SELECTED)
199 sel= ACHANNEL_SETFLAG_CLEAR;
201 case ANIMTYPE_OBJECT:
202 #if 0 /* for now, do not take object selection into account, since it gets too annoying */
203 if (ale->flag & SELECT)
204 sel= ACHANNEL_SETFLAG_CLEAR;
208 if (ale->flag & AGRP_SELECTED)
209 sel= ACHANNEL_SETFLAG_CLEAR;
211 case ANIMTYPE_FCURVE:
212 if (ale->flag & FCURVE_SELECTED)
213 sel= ACHANNEL_SETFLAG_CLEAR;
215 case ANIMTYPE_SHAPEKEY:
216 if (ale->flag & KEYBLOCK_SEL)
217 sel= ACHANNEL_SETFLAG_CLEAR;
219 case ANIMTYPE_NLATRACK:
220 if (ale->flag & NLATRACK_SELECTED)
221 sel= ACHANNEL_SETFLAG_CLEAR;
224 case ANIMTYPE_FILLACTD: /* Action Expander */
225 case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
229 case ANIMTYPE_DSSKEY:
231 case ANIMTYPE_DSPART:
232 case ANIMTYPE_DSMBALL:
234 case ANIMTYPE_DSMESH:
235 case ANIMTYPE_DSNTREE:
238 if ((ale->adt) && (ale->adt->flag & ADT_UI_SELECTED))
239 sel= ACHANNEL_SETFLAG_CLEAR;
246 /* Now set the flags */
247 for (ale= anim_data.first; ale; ale= ale->next) {
251 Scene *scene= (Scene *)ale->data;
253 ACHANNEL_SET_FLAG(scene, sel, SCE_DS_SELECTED);
256 ACHANNEL_SET_FLAG(scene, sel, ADT_UI_SELECTED);
260 case ANIMTYPE_OBJECT:
261 #if 0 /* for now, do not take object selection into account, since it gets too annoying */
263 Base *base= (Base *)ale->data;
264 Object *ob= base->object;
266 ACHANNEL_SET_FLAG(base, sel, SELECT);
267 ACHANNEL_SET_FLAG(ob, sel, SELECT);
270 ACHANNEL_SET_FLAG(ob, sel, ADT_UI_SELECTED);
277 bActionGroup *agrp= (bActionGroup *)ale->data;
279 ACHANNEL_SET_FLAG(agrp, sel, AGRP_SELECTED);
280 agrp->flag &= ~AGRP_ACTIVE;
283 case ANIMTYPE_FCURVE:
285 FCurve *fcu= (FCurve *)ale->data;
287 ACHANNEL_SET_FLAG(fcu, sel, FCURVE_SELECTED);
288 fcu->flag &= ~FCURVE_ACTIVE;
291 case ANIMTYPE_SHAPEKEY:
293 KeyBlock *kb= (KeyBlock *)ale->data;
295 ACHANNEL_SET_FLAG(kb, sel, KEYBLOCK_SEL);
298 case ANIMTYPE_NLATRACK:
300 NlaTrack *nlt= (NlaTrack *)ale->data;
302 ACHANNEL_SET_FLAG(nlt, sel, NLATRACK_SELECTED);
303 nlt->flag &= ~NLATRACK_ACTIVE;
307 case ANIMTYPE_FILLACTD: /* Action Expander */
308 case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
312 case ANIMTYPE_DSSKEY:
314 case ANIMTYPE_DSPART:
315 case ANIMTYPE_DSMBALL:
317 case ANIMTYPE_DSMESH:
318 case ANIMTYPE_DSNTREE:
321 /* need to verify that this data is valid for now */
323 ACHANNEL_SET_FLAG(ale->adt, sel, ADT_UI_SELECTED);
324 ale->adt->flag &= ~ADT_UI_ACTIVE;
332 BLI_freelistN(&anim_data);
335 /* ---------------------------- Graph Editor ------------------------------------- */
337 /* Flush visibility (for Graph Editor) changes up/down hierarchy for changes in the given setting
338 * - anim_data: list of the all the anim channels that can be chosen
339 * -> filtered using ANIMFILTER_CHANNELS only, since if we took VISIBLE too,
340 * then the channels under closed expanders get ignored...
341 * - ale_setting: the anim channel (not in the anim_data list directly, though occuring there)
342 * with the new state of the setting that we want flushed up/down the hierarchy
343 * - setting: type of setting to set
344 * - on: whether the visibility setting has been enabled or disabled
346 void ANIM_flush_setting_anim_channels (bAnimContext *ac, ListBase *anim_data, bAnimListElem *ale_setting, int setting, short on)
348 bAnimListElem *ale, *match=NULL;
349 int prevLevel=0, matchLevel=0;
352 if (ELEM(NULL, anim_data, anim_data->first))
355 /* find the channel that got changed */
356 for (ale= anim_data->first; ale; ale= ale->next) {
357 /* compare data, and type as main way of identifying the channel */
358 if ((ale->data == ale_setting->data) && (ale->type == ale_setting->type)) {
359 /* we also have to check the ID, this is assigned to, since a block may have multiple users */
360 // TODO: is the owner-data more revealing?
361 if (ale->id == ale_setting->id) {
368 printf("ERROR: no channel matching the one changed was found \n");
372 bAnimChannelType *acf= ANIM_channel_get_typeinfo(ale_setting);
375 printf("ERROR: no channel info for the changed channel \n");
379 /* get the level of the channel that was affected
380 * - we define the level as simply being the offset for the start of the channel
382 matchLevel= (acf->get_offset)? acf->get_offset(ac, ale_setting) : 0;
383 prevLevel= matchLevel;
389 * - only flush up if the current state is now enabled (positive 'on' state is default)
390 * (otherwise, it's too much work to force the parents to be inactive too)
392 * For everything else:
393 * - only flush up if the current state is now disabled (negative 'off' state is default)
394 * (otherwise, it's too much work to force the parents to be active too)
396 if ( ((setting == ACHANNEL_SETTING_VISIBLE) && on) ||
397 ((setting != ACHANNEL_SETTING_VISIBLE) && on==0) )
399 /* go backwards in the list, until the highest-ranking element (by indention has been covered) */
400 for (ale= match->prev; ale; ale= ale->prev) {
401 bAnimChannelType *acf= ANIM_channel_get_typeinfo(ale);
404 /* if no channel info was found, skip, since this type might not have any useful info */
408 /* get the level of the current channel traversed
409 * - we define the level as simply being the offset for the start of the channel
411 level= (acf->get_offset)? acf->get_offset(ac, ale) : 0;
413 /* if the level is 'less than' (i.e. more important) the level we're matching
414 * but also 'less than' the level just tried (i.e. only the 1st group above grouped F-Curves,
415 * when toggling visibility of F-Curves, gets flushed, which should happen if we don't let prevLevel
416 * get updated below once the first 1st group is found)...
418 if (level < prevLevel) {
419 /* flush the new status... */
420 ANIM_channel_setting_set(ac, ale, setting, on);
422 /* store this level as the 'old' level now */
425 /* if the level is 'greater than' (i.e. less important) than the previous level... */
426 else if (level > prevLevel) {
427 /* if previous level was a base-level (i.e. 0 offset / root of one hierarchy),
432 /* otherwise, this level weaves into another sibling hierarchy to the previous one just
433 * finished, so skip until we get to the parent of this level
441 /* flush down (always) */
443 /* go forwards in the list, until the lowest-ranking element (by indention has been covered) */
444 for (ale= match->next; ale; ale= ale->next) {
445 bAnimChannelType *acf= ANIM_channel_get_typeinfo(ale);
448 /* if no channel info was found, skip, since this type might not have any useful info */
452 /* get the level of the current channel traversed
453 * - we define the level as simply being the offset for the start of the channel
455 level= (acf->get_offset)? acf->get_offset(ac, ale) : 0;
457 /* if the level is 'greater than' (i.e. less important) the channel that was changed,
458 * flush the new status...
460 if (level > matchLevel)
461 ANIM_channel_setting_set(ac, ale, setting, on);
462 /* however, if the level is 'less than or equal to' the channel that was changed,
463 * (i.e. the current channel is as important if not more important than the changed channel)
464 * then we should stop, since we've found the last one of the children we should flush
469 /* store this level as the 'old' level now */
470 prevLevel= level; // XXX: prevLevel is unused
475 /* -------------------------- F-Curves ------------------------------------- */
477 /* Delete the given F-Curve from its AnimData block */
478 void ANIM_fcurve_delete_from_animdata (bAnimContext *ac, AnimData *adt, FCurve *fcu)
480 /* - if no AnimData, we've got nowhere to remove the F-Curve from
481 * (this doesn't guarantee that the F-Curve is in there, but at least we tried
482 * - if no F-Curve, there is nothing to remove
484 if (ELEM(NULL, adt, fcu))
487 /* remove from whatever list it came from
491 * - TODO... some others?
494 action_groups_remove_channel(adt->action, fcu);
495 else if ((ac) && (ac->datatype == ANIMCONT_DRIVERS))
496 BLI_remlink(&adt->drivers, fcu);
497 else if (adt->action)
498 BLI_remlink(&adt->action->curves, fcu);
500 /* free the F-Curve itself */
504 /* ************************************************************************** */
507 /* ****************** Operator Utilities ********************************** */
509 /* poll callback for being in an Animation Editor channels list region */
510 int animedit_poll_channels_active (bContext *C)
512 ScrArea *sa= CTX_wm_area(C);
514 /* channels region test */
515 // TODO: could enhance with actually testing if channels region?
516 if (ELEM(NULL, sa, CTX_wm_region(C)))
518 /* animation editor test */
519 if (ELEM3(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA) == 0)
525 /* poll callback for Animation Editor channels list region + not in NLA-tweakmode for NLA */
526 int animedit_poll_channels_nla_tweakmode_off (bContext *C)
528 ScrArea *sa= CTX_wm_area(C);
529 Scene *scene = CTX_data_scene(C);
531 /* channels region test */
532 // TODO: could enhance with actually testing if channels region?
533 if (ELEM(NULL, sa, CTX_wm_region(C)))
535 /* animation editor test */
536 if (ELEM3(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA) == 0)
539 /* NLA TweakMode test */
540 if (sa->spacetype == SPACE_NLA) {
541 if ((scene == NULL) || (scene->flag & SCE_NLA_EDIT_ON))
548 /* ****************** Rearrange Channels Operator ******************* */
550 /* constants for channel rearranging */
551 /* WARNING: don't change exising ones without modifying rearrange func accordingly */
553 REARRANGE_ANIMCHAN_TOP= -2,
554 REARRANGE_ANIMCHAN_UP= -1,
555 REARRANGE_ANIMCHAN_DOWN= 1,
556 REARRANGE_ANIMCHAN_BOTTOM= 2
559 /* defines for rearranging channels */
560 EnumPropertyItem prop_animchannel_rearrange_types[] = {
561 {REARRANGE_ANIMCHAN_TOP, "TOP", 0, "To Top", ""},
562 {REARRANGE_ANIMCHAN_UP, "UP", 0, "Up", ""},
563 {REARRANGE_ANIMCHAN_DOWN, "DOWN", 0, "Down", ""},
564 {REARRANGE_ANIMCHAN_BOTTOM, "BOTTOM", 0, "To Bottom", ""},
565 {0, NULL, 0, NULL, NULL}
568 /* Rearrange Utilities --------------------------------------------- */
570 /* checks if a channel should be considered for moving */
571 static short rearrange_animchannel_is_ok (Link *channel, short type)
573 if (type == ANIMTYPE_GROUP) {
574 bActionGroup *agrp= (bActionGroup *)channel;
576 if (SEL_AGRP(agrp) && !(agrp->flag & AGRP_MOVED))
579 else if (type == ANIMTYPE_FCURVE) {
580 FCurve *fcu= (FCurve *)channel;
582 // FIXME: F-Curve visibility is difficult... needs special filtering tests these days...
583 if (SEL_FCU(fcu) && !(fcu->flag & FCURVE_TAGGED))
586 else if (type == ANIMTYPE_NLATRACK) {
587 NlaTrack *nlt = (NlaTrack *)channel;
589 if (SEL_NLT(nlt) && !(nlt->flag & NLASTRIP_FLAG_EDIT_TOUCHED))
596 /* checks if another channel can be placed after the given one */
597 static short rearrange_animchannel_after_ok (Link *channel, short type)
599 if (type == ANIMTYPE_GROUP) {
600 bActionGroup *agrp= (bActionGroup *)channel;
602 if (agrp->flag & AGRP_TEMP)
609 /* Rearrange Methods --------------------------------------------- */
611 static short rearrange_animchannel_top (ListBase *list, Link *channel, short type)
613 if (rearrange_animchannel_is_ok(channel, type)) {
614 /* take it out off the chain keep data */
615 BLI_remlink(list, channel);
617 /* make it first element */
618 BLI_insertlinkbefore(list, list->first, channel);
626 static short rearrange_animchannel_up (ListBase *list, Link *channel, short type)
628 if (rearrange_animchannel_is_ok(channel, type)) {
629 Link *prev= channel->prev;
632 /* take it out off the chain keep data */
633 BLI_remlink(list, channel);
636 BLI_insertlinkbefore(list, prev, channel);
645 static short rearrange_animchannel_down (ListBase *list, Link *channel, short type)
647 if (rearrange_animchannel_is_ok(channel, type)) {
648 Link *next = (channel->next) ? channel->next->next : NULL;
651 /* take it out off the chain keep data */
652 BLI_remlink(list, channel);
655 BLI_insertlinkbefore(list, next, channel);
659 else if (rearrange_animchannel_after_ok(list->last, type)) {
660 /* take it out off the chain keep data */
661 BLI_remlink(list, channel);
664 BLI_addtail(list, channel);
669 /* take it out off the chain keep data */
670 BLI_remlink(list, channel);
672 /* add just before end */
673 BLI_insertlinkbefore(list, list->last, channel);
682 static short rearrange_animchannel_bottom (ListBase *list, Link *channel, short type)
684 if (rearrange_animchannel_is_ok(channel, type)) {
685 if (rearrange_animchannel_after_ok(list->last, type)) {
686 /* take it out off the chain keep data */
687 BLI_remlink(list, channel);
690 BLI_addtail(list, channel);
699 /* Generic Stuff ---------------------------------------------------------- */
701 /* typedef for channel rearranging function
702 * < list: list that channels belong to
703 * < channel: channel to be moved
704 * < type: type of channel (eAnim_ChannelType)
705 * > return[0]: whether operation was a success
707 typedef short (*AnimChanRearrangeFp)(ListBase *list, Link *channel, short type);
709 /* get rearranging function, given 'rearrange' mode */
710 static AnimChanRearrangeFp rearrange_get_mode_func (short mode)
713 case REARRANGE_ANIMCHAN_TOP:
714 return rearrange_animchannel_top;
715 case REARRANGE_ANIMCHAN_UP:
716 return rearrange_animchannel_up;
717 case REARRANGE_ANIMCHAN_DOWN:
718 return rearrange_animchannel_down;
719 case REARRANGE_ANIMCHAN_BOTTOM:
720 return rearrange_animchannel_bottom;
728 /* These iteration helpers (ideally should be inlined, but probably not necessary) */
730 static Link *rearrange_iter_first (ListBase *list, short mode)
732 return (mode > 0) ? list->first : list->last;
735 static Link *rearrange_iter_next (Link *item, short mode)
737 return (mode > 0) ? item->next : item->prev;
742 /* Clear 'tag' on all F-Curves */
743 static void rearrange_clear_fcurve_tags (ListBase *list)
747 for (fcu = list->first; fcu; fcu = fcu->next)
748 fcu->flag &= ~FCURVE_TAGGED;
751 /* NLA Specific Stuff ----------------------------------------------------- */
753 /* Change the order NLA Tracks within NLA Stack
754 * ! NLA tracks are displayed in opposite order, so directions need care
755 * mode: REARRANGE_ANIMCHAN_*
757 static void rearrange_nla_channels (bAnimContext *UNUSED(ac), AnimData *adt, short mode)
759 NlaTrack *nlt, *track;
761 AnimChanRearrangeFp rearrange_func;
763 /* hack: invert mode so that functions will work in right order */
766 /* get rearranging function */
767 rearrange_func = rearrange_get_mode_func(mode);
768 if (rearrange_func == NULL)
771 /* only consider NLA data if it's accessible */
772 //if (EXPANDED_DRVD(adt) == 0)
775 /* clear "moved" flag from all tracks */
776 for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next)
777 nlt->flag &= ~NLASTRIP_FLAG_EDIT_TOUCHED;
779 /* reorder all selected tracks */
780 for (nlt= (NlaTrack *)rearrange_iter_first(&adt->nla_tracks, mode); nlt; nlt= track) {
781 /* Get next channel to consider */
782 track= (NlaTrack *)rearrange_iter_next((Link *)nlt, mode);
784 /* Try to do channel */
785 if (rearrange_func(&adt->nla_tracks, (Link *)nlt, ANIMTYPE_NLATRACK))
786 nlt->flag |= NLASTRIP_FLAG_EDIT_TOUCHED;
789 /* clear "moved" flag from all tracks */
790 for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next)
791 nlt->flag &= ~NLASTRIP_FLAG_EDIT_TOUCHED;
794 /* Drivers Specific Stuff ------------------------------------------------- */
796 /* Change the order drivers within AnimData block
797 * mode: REARRANGE_ANIMCHAN_*
799 static void rearrange_driver_channels (bAnimContext *UNUSED(ac), AnimData *adt, short mode)
803 /* get rearranging function */
804 AnimChanRearrangeFp rearrange_func = rearrange_get_mode_func(mode);
806 if (rearrange_func == NULL)
809 /* only consider drivers if they're accessible */
810 if (EXPANDED_DRVD(adt) == 0)
813 rearrange_clear_fcurve_tags(&adt->drivers);
815 /* reorder all selected driver F-Curves */
816 for (fcu= (FCurve *)rearrange_iter_first(&adt->drivers, mode); fcu; fcu= fcun) {
817 /* Get next channel to consider */
818 fcun= (FCurve *)rearrange_iter_next((Link *)fcu, mode);
820 /* Try to do channel */
821 if (rearrange_func(&adt->drivers, (Link *)fcu, ANIMTYPE_FCURVE))
822 fcu->flag |= FCURVE_TAGGED;
825 rearrange_clear_fcurve_tags(&adt->drivers);
828 /* Action Specific Stuff ------------------------------------------------- */
830 /* make sure all action-channels belong to a group (and clear action's list) */
831 static void split_groups_action_temp (bAction *act, bActionGroup *tgrp)
839 /* clear "moved" flag from all FCurves */
840 rearrange_clear_fcurve_tags(&act->curves);
842 /* Separate F-Curves into lists per group */
843 for (agrp= act->groups.first; agrp; agrp= agrp->next) {
844 if (agrp->channels.first) {
845 fcu= agrp->channels.last;
846 act->curves.first= fcu->next;
848 fcu= agrp->channels.first;
851 fcu= agrp->channels.last;
856 /* Initialise memory for temp-group */
857 memset(tgrp, 0, sizeof(bActionGroup));
858 tgrp->flag |= (AGRP_EXPANDED|AGRP_TEMP);
859 BLI_strncpy(tgrp->name, "#TempGroup", sizeof(tgrp->name));
861 /* Move any action-channels not already moved, to the temp group */
862 if (act->curves.first) {
864 fcu= act->curves.first;
866 tgrp->channels.first= fcu;
867 act->curves.first= NULL;
870 fcu= act->curves.last;
872 tgrp->channels.last= fcu;
873 act->curves.last= NULL;
876 /* Add temp-group to list */
877 BLI_addtail(&act->groups, tgrp);
880 /* link lists of channels that groups have */
881 static void join_groups_action_temp (bAction *act)
885 for (agrp= act->groups.first; agrp; agrp= agrp->next) {
888 /* add list of channels to action's channels */
889 tempGroup= agrp->channels;
890 addlisttolist(&act->curves, &agrp->channels);
891 agrp->channels= tempGroup;
893 /* clear moved flag */
894 agrp->flag &= ~AGRP_MOVED;
896 /* if temp-group... remove from list (but don't free as it's on the stack!) */
897 if (agrp->flag & AGRP_TEMP) {
898 BLI_remlink(&act->groups, agrp);
903 /* clear "moved" flag from all fcurve's */
904 rearrange_clear_fcurve_tags(&act->curves);
907 /* Change the order of anim-channels within action
908 * mode: REARRANGE_ANIMCHAN_*
910 static void rearrange_action_channels (bAnimContext *ac, bAction *act, short mode)
912 bActionGroup *agrp, *grp;
915 short do_channels = 1;
917 /* get rearranging function */
918 AnimChanRearrangeFp rearrange_func = rearrange_get_mode_func(mode);
920 if (rearrange_func == NULL)
923 /* make sure we're only operating with groups */
924 split_groups_action_temp(act, &tgrp);
926 /* rearrange groups first (and then, only consider channels if the groups weren't moved) */
927 for (agrp= (bActionGroup *)rearrange_iter_first(&act->groups, mode); agrp; agrp= grp) {
928 /* Get next group to consider */
929 grp= (bActionGroup *)rearrange_iter_next((Link *)agrp, mode);
931 /* try to do group first */
932 if (rearrange_func(&act->groups, (Link *)agrp, ANIMTYPE_GROUP)) {
934 agrp->flag |= AGRP_MOVED;
939 for (agrp= (bActionGroup *)rearrange_iter_first(&act->groups, mode); agrp; agrp= grp) {
940 /* Get next group to consider */
941 grp= (bActionGroup *)rearrange_iter_next((Link *)agrp, mode);
943 /* only consider F-Curves if they're visible (group expanded) */
944 if (EXPANDED_AGRP(agrp)) {
945 for (fcu= (FCurve *)rearrange_iter_first(&agrp->channels, mode); fcu; fcu= fcun) {
946 /* Get next channel to consider */
947 fcun= (FCurve *)rearrange_iter_next((Link *)fcu, mode);
949 /* Try to do channel */
950 if (rearrange_func(&agrp->channels, (Link *)fcu, ANIMTYPE_FCURVE))
951 fcu->flag |= FCURVE_TAGGED;
957 /* assemble lists into one list (and clear moved tags) */
958 join_groups_action_temp(act);
961 /* ------------------- */
963 static int animchannels_rearrange_exec(bContext *C, wmOperator *op)
967 ListBase anim_data = {NULL, NULL};
973 /* get editor data */
974 if (ANIM_animdata_get_context(C, &ac) == 0)
975 return OPERATOR_CANCELLED;
978 mode= RNA_enum_get(op->ptr, "direction");
980 /* get animdata blocks */
981 filter= (ANIMFILTER_VISIBLE | ANIMFILTER_ANIMDATA);
982 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
984 for (ale = anim_data.first; ale; ale = ale->next) {
985 AnimData *adt= ale->data;
987 switch (ac.datatype) {
988 case ANIMCONT_NLA: /* NLA-tracks only */
989 rearrange_nla_channels(&ac, adt, mode);
992 case ANIMCONT_DRIVERS: /* Drivers list only */
993 rearrange_driver_channels(&ac, adt, mode);
997 case ANIMCONT_GPENCIL: /* Grease Pencil channels */
1001 case ANIMCONT_SHAPEKEY: // DOUBLE CHECK ME...
1003 default: /* some collection of actions */
1004 // FIXME: actions should only be considered once!
1006 rearrange_action_channels(&ac, adt->action, mode);
1008 printf("animdata has no action\n");
1013 /* free temp data */
1014 BLI_freelistN(&anim_data);
1016 /* send notifier that things have changed */
1017 WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN|NA_EDITED, NULL);
1019 return OPERATOR_FINISHED;
1022 void ANIM_OT_channels_move (wmOperatorType *ot)
1025 ot->name= "Move Channels";
1026 ot->idname= "ANIM_OT_channels_move";
1029 ot->exec= animchannels_rearrange_exec;
1030 ot->poll= animedit_poll_channels_nla_tweakmode_off;
1033 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1036 RNA_def_enum(ot->srna, "direction", prop_animchannel_rearrange_types, REARRANGE_ANIMCHAN_DOWN, "Direction", "");
1039 /* ******************** Delete Channel Operator *********************** */
1041 static int animchannels_delete_exec(bContext *C, wmOperator *UNUSED(op))
1044 ListBase anim_data = {NULL, NULL};
1048 /* get editor data */
1049 if (ANIM_animdata_get_context(C, &ac) == 0)
1050 return OPERATOR_CANCELLED;
1052 /* cannot delete in shapekey */
1053 if (ac.datatype == ANIMCONT_SHAPEKEY)
1054 return OPERATOR_CANCELLED;
1057 /* do groups only first (unless in Drivers mode, where there are none) */
1058 if (ac.datatype != ANIMCONT_DRIVERS) {
1060 filter= (ANIMFILTER_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_CHANNELS | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
1061 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1063 /* delete selected groups and their associated channels */
1064 for (ale= anim_data.first; ale; ale= ale->next) {
1065 /* only groups - don't check other types yet, since they may no-longer exist */
1066 if (ale->type == ANIMTYPE_GROUP) {
1067 bActionGroup *agrp= (bActionGroup *)ale->data;
1068 AnimData *adt= ale->adt;
1071 /* skip this group if no AnimData available, as we can't safely remove the F-Curves */
1075 /* delete all of the Group's F-Curves, but no others */
1076 for (fcu= agrp->channels.first; fcu && fcu->grp==agrp; fcu= fcn) {
1079 /* remove from group and action, then free */
1080 action_groups_remove_channel(adt->action, fcu);
1084 /* free the group itself */
1086 BLI_freelinkN(&adt->action->groups, agrp);
1093 BLI_freelistN(&anim_data);
1096 /* now do F-Curves */
1097 if (ac.datatype != ANIMCONT_GPENCIL) {
1099 filter= (ANIMFILTER_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
1100 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1102 /* delete selected F-Curves */
1103 for (ale= anim_data.first; ale; ale= ale->next) {
1104 /* only F-Curves, and only if we can identify its parent */
1105 if (ale->type == ANIMTYPE_FCURVE) {
1106 AnimData *adt= ale->adt;
1107 FCurve *fcu= (FCurve *)ale->data;
1109 /* try to free F-Curve */
1110 ANIM_fcurve_delete_from_animdata(&ac, adt, fcu);
1115 BLI_freelistN(&anim_data);
1118 /* send notifier that things have changed */
1119 WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN|NA_EDITED, NULL);
1121 return OPERATOR_FINISHED;
1124 void ANIM_OT_channels_delete (wmOperatorType *ot)
1127 ot->name= "Delete Channels";
1128 ot->idname= "ANIM_OT_channels_delete";
1129 ot->description= "Delete all selected animation channels";
1132 ot->exec= animchannels_delete_exec;
1133 ot->poll= animedit_poll_channels_active;
1136 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1139 /* ******************** Set Channel Visibility Operator *********************** */
1140 /* NOTE: this operator is only valid in the Graph Editor channels region */
1142 static int animchannels_visibility_set_exec(bContext *C, wmOperator *UNUSED(op))
1145 ListBase anim_data = {NULL, NULL};
1146 ListBase all_data = {NULL, NULL};
1150 /* get editor data */
1151 if (ANIM_animdata_get_context(C, &ac) == 0)
1152 return OPERATOR_CANCELLED;
1154 /* get list of all channels that selection may need to be flushed to */
1155 filter= ANIMFILTER_CHANNELS;
1156 ANIM_animdata_filter(&ac, &all_data, filter, ac.data, ac.datatype);
1158 /* hide all channels not selected */
1159 filter= (ANIMFILTER_VISIBLE | ANIMFILTER_UNSEL | ANIMFILTER_NODUPLIS);
1160 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1162 for (ale= anim_data.first; ale; ale= ale->next) {
1163 /* clear setting first */
1164 ANIM_channel_setting_set(&ac, ale, ACHANNEL_SETTING_VISIBLE, ACHANNEL_SETFLAG_CLEAR);
1166 /* now also flush selection status as appropriate
1167 * NOTE: in some cases, this may result in repeat flushing being performed
1169 ANIM_flush_setting_anim_channels(&ac, &all_data, ale, ACHANNEL_SETTING_VISIBLE, 0);
1172 BLI_freelistN(&anim_data);
1174 /* make all the selected channels visible */
1175 filter= (ANIMFILTER_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_NODUPLIS);
1176 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1178 for (ale= anim_data.first; ale; ale= ale->next) {
1179 /* hack: skip object channels for now, since flushing those will always flush everything, but they are always included */
1180 // TODO: find out why this is the case, and fix that
1181 if (ale->type == ANIMTYPE_OBJECT)
1184 /* enable the setting */
1185 ANIM_channel_setting_set(&ac, ale, ACHANNEL_SETTING_VISIBLE, ACHANNEL_SETFLAG_ADD);
1187 /* now, also flush selection status up/down as appropriate */
1188 ANIM_flush_setting_anim_channels(&ac, &all_data, ale, ACHANNEL_SETTING_VISIBLE, 1);
1191 BLI_freelistN(&anim_data);
1192 BLI_freelistN(&all_data);
1195 /* send notifier that things have changed */
1196 WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN|NA_EDITED, NULL);
1198 return OPERATOR_FINISHED;
1201 void ANIM_OT_channels_visibility_set (wmOperatorType *ot)
1204 ot->name= "Set Visibility";
1205 ot->idname= "ANIM_OT_channels_visibility_set";
1206 ot->description= "Make only the selected animation channels visible in the Graph Editor";
1209 ot->exec= animchannels_visibility_set_exec;
1210 ot->poll= ED_operator_ipo_active;
1213 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1217 /* ******************** Toggle Channel Visibility Operator *********************** */
1218 /* NOTE: this operator is only valid in the Graph Editor channels region */
1220 static int animchannels_visibility_toggle_exec(bContext *C, wmOperator *UNUSED(op))
1223 ListBase anim_data = {NULL, NULL};
1224 ListBase all_data = {NULL, NULL};
1227 short vis= ACHANNEL_SETFLAG_ADD;
1229 /* get editor data */
1230 if (ANIM_animdata_get_context(C, &ac) == 0)
1231 return OPERATOR_CANCELLED;
1233 /* get list of all channels that selection may need to be flushed to */
1234 filter= (ANIMFILTER_CHANNELS | ANIMFILTER_NODUPLIS);
1235 ANIM_animdata_filter(&ac, &all_data, filter, ac.data, ac.datatype);
1238 filter= (ANIMFILTER_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_NODUPLIS);
1239 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1241 /* See if we should be making showing all selected or hiding */
1242 for (ale= anim_data.first; ale; ale= ale->next) {
1243 /* set the setting in the appropriate way (if available) */
1244 if (ANIM_channel_setting_get(&ac, ale, ACHANNEL_SETTING_VISIBLE)) {
1245 vis= ACHANNEL_SETFLAG_CLEAR;
1250 /* Now set the flags */
1251 for (ale= anim_data.first; ale; ale= ale->next) {
1252 /* hack: skip object channels for now, since flushing those will always flush everything, but they are always included */
1253 // TODO: find out why this is the case, and fix that
1254 if (ale->type == ANIMTYPE_OBJECT)
1257 /* change the setting */
1258 ANIM_channel_setting_set(&ac, ale, ACHANNEL_SETTING_VISIBLE, vis);
1260 /* now, also flush selection status up/down as appropriate */
1261 ANIM_flush_setting_anim_channels(&ac, &all_data, ale, ACHANNEL_SETTING_VISIBLE, (vis == ACHANNEL_SETFLAG_ADD));
1265 BLI_freelistN(&anim_data);
1266 BLI_freelistN(&all_data);
1268 /* send notifier that things have changed */
1269 WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN|NA_EDITED, NULL);
1271 return OPERATOR_FINISHED;
1274 void ANIM_OT_channels_visibility_toggle (wmOperatorType *ot)
1277 ot->name= "Toggle Visibility";
1278 ot->idname= "ANIM_OT_channels_visibility_toggle";
1279 ot->description= "Toggle visibility in Graph Editor of all selected animation channels";
1282 ot->exec= animchannels_visibility_toggle_exec;
1283 ot->poll= ED_operator_ipo_active;
1286 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1289 /* ********************** Set Flags Operator *********************** */
1291 /* defines for setting animation-channel flags */
1292 EnumPropertyItem prop_animchannel_setflag_types[] = {
1293 {ACHANNEL_SETFLAG_TOGGLE, "TOGGLE", 0, "Toggle", ""},
1294 {ACHANNEL_SETFLAG_CLEAR, "DISABLE", 0, "Disable", ""},
1295 {ACHANNEL_SETFLAG_ADD, "ENABLE", 0, "Enable", ""},
1296 {ACHANNEL_SETFLAG_INVERT, "INVERT", 0, "Invert", ""},
1297 {0, NULL, 0, NULL, NULL}
1300 /* defines for set animation-channel settings */
1301 // TODO: could add some more types, but those are really quite dependent on the mode...
1302 EnumPropertyItem prop_animchannel_settings_types[] = {
1303 {ACHANNEL_SETTING_PROTECT, "PROTECT", 0, "Protect", ""},
1304 {ACHANNEL_SETTING_MUTE, "MUTE", 0, "Mute", ""},
1305 {0, NULL, 0, NULL, NULL}
1309 /* ------------------- */
1311 /* macro to be used in setflag_anim_channels */
1312 #define ASUBCHANNEL_SEL_OK(ale) ( (onlysel == 0) || \
1313 ((ale->id) && (GS(ale->id->name)==ID_OB) && (((Object *)ale->id)->flag & SELECT)) )
1315 /* Set/clear a particular flag (setting) for all selected + visible channels
1316 * setting: the setting to modify
1317 * mode: eAnimChannels_SetFlag
1318 * onlysel: only selected channels get the flag set
1320 // TODO: enable a setting which turns flushing on/off?
1321 static void setflag_anim_channels (bAnimContext *ac, short setting, short mode, short onlysel, short flush)
1323 ListBase anim_data = {NULL, NULL};
1324 ListBase all_data = {NULL, NULL};
1328 /* filter data that we need if flush is on */
1330 /* get list of all channels that selection may need to be flushed to */
1331 filter= ANIMFILTER_CHANNELS;
1332 ANIM_animdata_filter(ac, &all_data, filter, ac->data, ac->datatype);
1335 /* filter data that we're working on */
1336 // XXX: noduplis enabled so that results don't cancel, but will be problematic for some channels where only type differs
1337 filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CHANNELS | ANIMFILTER_NODUPLIS);
1338 if (onlysel) filter |= ANIMFILTER_SEL;
1339 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1341 /* if toggling, check if disable or enable */
1342 if (mode == ACHANNEL_SETFLAG_TOGGLE) {
1343 /* default to turn all on, unless we encounter one that's on... */
1344 mode= ACHANNEL_SETFLAG_ADD;
1346 /* see if we should turn off instead... */
1347 for (ale= anim_data.first; ale; ale= ale->next) {
1348 /* set the setting in the appropriate way (if available) */
1349 if (ANIM_channel_setting_get(ac, ale, setting) > 0) {
1350 mode= ACHANNEL_SETFLAG_CLEAR;
1356 /* apply the setting */
1357 for (ale= anim_data.first; ale; ale= ale->next) {
1358 /* skip channel if setting is not available */
1359 if (ANIM_channel_setting_get(ac, ale, setting) == -1)
1362 /* set the setting in the appropriate way */
1363 ANIM_channel_setting_set(ac, ale, setting, mode);
1365 /* if flush status... */
1367 ANIM_flush_setting_anim_channels(ac, &all_data, ale, setting, mode);
1370 BLI_freelistN(&anim_data);
1371 BLI_freelistN(&all_data);
1374 /* ------------------- */
1376 static int animchannels_setflag_exec(bContext *C, wmOperator *op)
1379 short mode, setting;
1382 /* get editor data */
1383 if (ANIM_animdata_get_context(C, &ac) == 0)
1384 return OPERATOR_CANCELLED;
1386 /* mode (eAnimChannels_SetFlag), setting (eAnimChannel_Settings) */
1387 mode= RNA_enum_get(op->ptr, "mode");
1388 setting= RNA_enum_get(op->ptr, "type");
1390 /* check if setting is flushable */
1391 if (setting == ACHANNEL_SETTING_EXPAND)
1395 * - only selected channels are affected
1397 setflag_anim_channels(&ac, setting, mode, 1, flush);
1399 /* send notifier that things have changed */
1400 WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN|NA_EDITED, NULL);
1402 return OPERATOR_FINISHED;
1406 void ANIM_OT_channels_setting_enable (wmOperatorType *ot)
1409 ot->name= "Enable Channel Setting";
1410 ot->idname= "ANIM_OT_channels_setting_enable";
1411 ot->description= "Enable specified setting on all selected animation channels";
1414 ot->invoke= WM_menu_invoke;
1415 ot->exec= animchannels_setflag_exec;
1416 ot->poll= animedit_poll_channels_active;
1419 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1422 /* flag-setting mode */
1423 RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_ADD, "Mode", "");
1424 /* setting to set */
1425 ot->prop= RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
1428 void ANIM_OT_channels_setting_disable (wmOperatorType *ot)
1431 ot->name= "Disable Channel Setting";
1432 ot->idname= "ANIM_OT_channels_setting_disable";
1433 ot->description= "Disable specified setting on all selected animation channels";
1436 ot->invoke= WM_menu_invoke;
1437 ot->exec= animchannels_setflag_exec;
1438 ot->poll= animedit_poll_channels_active;
1441 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1444 /* flag-setting mode */
1445 RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_CLEAR, "Mode", "");
1446 /* setting to set */
1447 ot->prop= RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
1450 void ANIM_OT_channels_setting_invert (wmOperatorType *ot)
1453 ot->name= "Invert Channel Setting";
1454 ot->idname= "ANIM_OT_channels_setting_toggle";
1455 ot->description= "Invert specified setting on all selected animation channels";
1458 ot->invoke= WM_menu_invoke;
1459 ot->exec= animchannels_setflag_exec;
1460 ot->poll= animedit_poll_channels_active;
1463 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1466 /* flag-setting mode */
1467 RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_INVERT, "Mode", "");
1468 /* setting to set */
1469 ot->prop= RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
1472 void ANIM_OT_channels_setting_toggle (wmOperatorType *ot)
1475 ot->name= "Toggle Channel Setting";
1476 ot->idname= "ANIM_OT_channels_setting_toggle";
1477 ot->description= "Toggle specified setting on all selected animation channels";
1480 ot->invoke= WM_menu_invoke;
1481 ot->exec= animchannels_setflag_exec;
1482 ot->poll= animedit_poll_channels_active;
1485 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1488 /* flag-setting mode */
1489 RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_TOGGLE, "Mode", "");
1490 /* setting to set */
1491 ot->prop= RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
1494 void ANIM_OT_channels_editable_toggle (wmOperatorType *ot)
1497 ot->name= "Toggle Channel Editability";
1498 ot->idname= "ANIM_OT_channels_editable_toggle";
1499 ot->description= "Toggle editability of selected channels";
1502 ot->exec= animchannels_setflag_exec;
1503 ot->poll= animedit_poll_channels_active;
1506 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1509 /* flag-setting mode */
1510 RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_TOGGLE, "Mode", "");
1511 /* setting to set */
1512 RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, ACHANNEL_SETTING_PROTECT, "Type", "");
1515 /* ********************** Expand Channels Operator *********************** */
1517 static int animchannels_expand_exec (bContext *C, wmOperator *op)
1522 /* get editor data */
1523 if (ANIM_animdata_get_context(C, &ac) == 0)
1524 return OPERATOR_CANCELLED;
1526 /* only affect selected channels? */
1527 if (RNA_boolean_get(op->ptr, "all"))
1530 /* modify setting */
1531 setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_ADD, onlysel, 0);
1533 /* send notifier that things have changed */
1534 WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN|NA_EDITED, NULL);
1536 return OPERATOR_FINISHED;
1539 void ANIM_OT_channels_expand (wmOperatorType *ot)
1542 ot->name= "Expand Channels";
1543 ot->idname= "ANIM_OT_channels_expand";
1544 ot->description= "Expand (i.e. open) all selected expandable animation channels";
1547 ot->exec= animchannels_expand_exec;
1548 ot->poll= animedit_poll_channels_active;
1551 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1554 ot->prop= RNA_def_boolean(ot->srna, "all", 1, "All", "Expand all channels (not just selected ones)");
1557 /* ********************** Collapse Channels Operator *********************** */
1559 static int animchannels_collapse_exec (bContext *C, wmOperator *op)
1564 /* get editor data */
1565 if (ANIM_animdata_get_context(C, &ac) == 0)
1566 return OPERATOR_CANCELLED;
1568 /* only affect selected channels? */
1569 if (RNA_boolean_get(op->ptr, "all"))
1572 /* modify setting */
1573 setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_CLEAR, onlysel, 0);
1575 /* send notifier that things have changed */
1576 WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN|NA_EDITED, NULL);
1578 return OPERATOR_FINISHED;
1581 void ANIM_OT_channels_collapse (wmOperatorType *ot)
1584 ot->name= "Collapse Channels";
1585 ot->idname= "ANIM_OT_channels_collapse";
1586 ot->description= "Collapse (i.e. close) all selected expandable animation channels";
1589 ot->exec= animchannels_collapse_exec;
1590 ot->poll= animedit_poll_channels_active;
1593 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1596 ot->prop= RNA_def_boolean(ot->srna, "all", 1, "All", "Collapse all channels (not just selected ones)");
1599 /* ********************** Select All Operator *********************** */
1601 static int animchannels_deselectall_exec(bContext *C, wmOperator *op)
1605 /* get editor data */
1606 if (ANIM_animdata_get_context(C, &ac) == 0)
1607 return OPERATOR_CANCELLED;
1609 /* 'standard' behaviour - check if selected, then apply relevant selection */
1610 if (RNA_boolean_get(op->ptr, "invert"))
1611 ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, 0, ACHANNEL_SETFLAG_TOGGLE);
1613 ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, 1, ACHANNEL_SETFLAG_ADD);
1615 /* send notifier that things have changed */
1616 WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN|NA_SELECTED, NULL);
1618 return OPERATOR_FINISHED;
1621 void ANIM_OT_channels_select_all_toggle (wmOperatorType *ot)
1624 ot->name= "Select All";
1625 ot->idname= "ANIM_OT_channels_select_all_toggle";
1626 ot->description= "Toggle selection of all animation channels";
1629 ot->exec= animchannels_deselectall_exec;
1630 ot->poll= animedit_poll_channels_nla_tweakmode_off;
1633 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1636 ot->prop= RNA_def_boolean(ot->srna, "invert", 0, "Invert", "");
1639 /* ******************** Borderselect Operator *********************** */
1641 static void borderselect_anim_channels (bAnimContext *ac, rcti *rect, short selectmode)
1643 ListBase anim_data = {NULL, NULL};
1647 View2D *v2d= &ac->ar->v2d;
1651 /* set initial y extents */
1652 if (ac->datatype == ANIMCONT_NLA) {
1653 ymin = (float)(-NLACHANNEL_HEIGHT);
1658 ymax = (float)(-ACHANNEL_HEIGHT);
1661 /* convert border-region to view coordinates */
1662 UI_view2d_region_to_view(v2d, rect->xmin, rect->ymin+2, &rectf.xmin, &rectf.ymin);
1663 UI_view2d_region_to_view(v2d, rect->xmax, rect->ymax-2, &rectf.xmax, &rectf.ymax);
1666 filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CHANNELS);
1667 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1669 /* loop over data, doing border select */
1670 for (ale= anim_data.first; ale; ale= ale->next) {
1671 if (ac->datatype == ANIMCONT_NLA)
1672 ymin= ymax - NLACHANNEL_STEP;
1674 ymin= ymax - ACHANNEL_STEP;
1676 /* if channel is within border-select region, alter it */
1677 if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))) {
1678 /* set selection flags only */
1679 ANIM_channel_setting_set(ac, ale, ACHANNEL_SETTING_SELECT, selectmode);
1681 /* type specific actions */
1682 switch (ale->type) {
1683 case ANIMTYPE_GROUP:
1685 bActionGroup *agrp= (bActionGroup *)ale->data;
1687 /* always clear active flag after doing this */
1688 agrp->flag &= ~AGRP_ACTIVE;
1694 /* set minimum extent to be the maximum of the next channel */
1699 BLI_freelistN(&anim_data);
1702 /* ------------------- */
1704 static int animchannels_borderselect_exec(bContext *C, wmOperator *op)
1711 /* get editor data */
1712 if (ANIM_animdata_get_context(C, &ac) == 0)
1713 return OPERATOR_CANCELLED;
1715 /* get settings from operator */
1716 rect.xmin= RNA_int_get(op->ptr, "xmin");
1717 rect.ymin= RNA_int_get(op->ptr, "ymin");
1718 rect.xmax= RNA_int_get(op->ptr, "xmax");
1719 rect.ymax= RNA_int_get(op->ptr, "ymax");
1721 gesture_mode= RNA_int_get(op->ptr, "gesture_mode");
1722 if (gesture_mode == GESTURE_MODAL_SELECT)
1723 selectmode = ACHANNEL_SETFLAG_ADD;
1725 selectmode = ACHANNEL_SETFLAG_CLEAR;
1727 /* apply borderselect animation channels */
1728 borderselect_anim_channels(&ac, &rect, selectmode);
1730 /* send notifier that things have changed */
1731 WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN|NA_SELECTED, NULL);
1733 return OPERATOR_FINISHED;
1736 void ANIM_OT_channels_select_border(wmOperatorType *ot)
1739 ot->name= "Border Select";
1740 ot->idname= "ANIM_OT_channels_select_border";
1741 ot->description= "Select all animation channels within the specified region";
1744 ot->invoke= WM_border_select_invoke;
1745 ot->exec= animchannels_borderselect_exec;
1746 ot->modal= WM_border_select_modal;
1748 ot->poll= animedit_poll_channels_nla_tweakmode_off;
1751 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1754 WM_operator_properties_gesture_border(ot, FALSE);
1757 /* ******************** Mouse-Click Operator *********************** */
1758 /* Handle selection changes due to clicking on channels. Settings will get caught by UI code... */
1760 static int mouse_anim_channels (bAnimContext *ac, float UNUSED(x), int channel_index, short selectmode)
1762 ListBase anim_data = {NULL, NULL};
1765 int notifierFlags = 0;
1767 /* get the channel that was clicked on */
1768 /* filter channels */
1769 filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CHANNELS);
1770 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1772 /* get channel from index */
1773 ale= BLI_findlink(&anim_data, channel_index);
1775 /* channel not found */
1777 printf("Error: animation channel (index = %d) not found in mouse_anim_channels() \n", channel_index);
1779 BLI_freelistN(&anim_data);
1783 /* selectmode -1 is a special case for ActionGroups only, which selects all of the channels underneath it only... */
1784 // TODO: should this feature be extended to work with other channel types too?
1785 if ((selectmode == -1) && (ale->type != ANIMTYPE_GROUP)) {
1786 /* normal channels should not behave normally in this case */
1787 BLI_freelistN(&anim_data);
1791 /* action to take depends on what channel we've got */
1792 // WARNING: must keep this in sync with the equivalent function in nla_channels.c
1793 switch (ale->type) {
1794 case ANIMTYPE_SCENE:
1796 Scene *sce= (Scene *)ale->data;
1797 AnimData *adt= sce->adt;
1799 /* set selection status */
1800 if (selectmode == SELECT_INVERT) {
1802 sce->flag ^= SCE_DS_SELECTED;
1803 if (adt) adt->flag ^= ADT_UI_SELECTED;
1806 sce->flag |= SCE_DS_SELECTED;
1807 if (adt) adt->flag |= ADT_UI_SELECTED;
1810 notifierFlags |= (ND_ANIMCHAN|NA_SELECTED);
1813 case ANIMTYPE_OBJECT:
1815 bDopeSheet *ads= (bDopeSheet *)ac->data;
1816 Scene *sce= (Scene *)ads->source;
1817 Base *base= (Base *)ale->data;
1818 Object *ob= base->object;
1819 AnimData *adt= ob->adt;
1821 /* set selection status */
1822 if (selectmode == SELECT_INVERT) {
1824 base->flag ^= SELECT;
1825 ob->flag= base->flag;
1827 if (adt) adt->flag ^= ADT_UI_SELECTED;
1833 // TODO: should this deselect all other types of channels too?
1834 for (b= sce->base.first; b; b= b->next) {
1836 b->object->flag= b->flag;
1837 if (b->object->adt) b->object->adt->flag &= ~(ADT_UI_SELECTED|ADT_UI_ACTIVE);
1840 /* select object now */
1841 base->flag |= SELECT;
1843 if (adt) adt->flag |= ADT_UI_SELECTED;
1846 if ((adt) && (adt->flag & ADT_UI_SELECTED))
1847 adt->flag |= ADT_UI_ACTIVE;
1849 notifierFlags |= (ND_ANIMCHAN|NA_SELECTED);
1853 case ANIMTYPE_FILLACTD: /* Action Expander */
1854 case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
1855 case ANIMTYPE_DSLAM:
1856 case ANIMTYPE_DSCAM:
1857 case ANIMTYPE_DSCUR:
1858 case ANIMTYPE_DSSKEY:
1859 case ANIMTYPE_DSWOR:
1860 case ANIMTYPE_DSPART:
1861 case ANIMTYPE_DSMBALL:
1862 case ANIMTYPE_DSARM:
1863 case ANIMTYPE_DSMESH:
1864 case ANIMTYPE_DSNTREE:
1865 case ANIMTYPE_DSTEX:
1867 /* sanity checking... */
1869 /* select/deselect */
1870 if (selectmode == SELECT_INVERT) {
1871 /* inverse selection status of this AnimData block only */
1872 ale->adt->flag ^= ADT_UI_SELECTED;
1875 /* select AnimData block by itself */
1876 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
1877 ale->adt->flag |= ADT_UI_SELECTED;
1881 if ((ale->adt) && (ale->adt->flag & ADT_UI_SELECTED))
1882 ale->adt->flag |= ADT_UI_ACTIVE;
1885 notifierFlags |= (ND_ANIMCHAN|NA_SELECTED);
1889 case ANIMTYPE_GROUP:
1891 bActionGroup *agrp= (bActionGroup *)ale->data;
1893 /* select/deselect group */
1894 if (selectmode == SELECT_INVERT) {
1895 /* inverse selection status of this group only */
1896 agrp->flag ^= AGRP_SELECTED;
1898 else if (selectmode == -1) {
1899 /* select all in group (and deselect everthing else) */
1902 /* deselect all other channels */
1903 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
1905 /* only select channels in group and group itself */
1906 for (fcu= agrp->channels.first; fcu && fcu->grp==agrp; fcu= fcu->next)
1907 fcu->flag |= FCURVE_SELECTED;
1908 agrp->flag |= AGRP_SELECTED;
1911 /* select group by itself */
1912 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
1913 agrp->flag |= AGRP_SELECTED;
1916 /* if group is selected now, make group the 'active' one in the visible list */
1917 if (agrp->flag & AGRP_SELECTED)
1918 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, agrp, ANIMTYPE_GROUP);
1920 notifierFlags |= (ND_ANIMCHAN|NA_SELECTED);
1923 case ANIMTYPE_FCURVE:
1925 FCurve *fcu= (FCurve *)ale->data;
1927 /* select/deselect */
1928 if (selectmode == SELECT_INVERT) {
1929 /* inverse selection status of this F-Curve only */
1930 fcu->flag ^= FCURVE_SELECTED;
1933 /* select F-Curve by itself */
1934 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
1935 fcu->flag |= FCURVE_SELECTED;
1938 /* if F-Curve is selected now, make F-Curve the 'active' one in the visible list */
1939 if (fcu->flag & FCURVE_SELECTED)
1940 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, fcu, ANIMTYPE_FCURVE);
1942 notifierFlags |= (ND_ANIMCHAN|NA_SELECTED);
1945 case ANIMTYPE_SHAPEKEY:
1947 KeyBlock *kb= (KeyBlock *)ale->data;
1949 /* select/deselect */
1950 if (selectmode == SELECT_INVERT) {
1951 /* inverse selection status of this ShapeKey only */
1952 kb->flag ^= KEYBLOCK_SEL;
1955 /* select ShapeKey by itself */
1956 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
1957 kb->flag |= KEYBLOCK_SEL;
1960 notifierFlags |= (ND_ANIMCHAN|NA_SELECTED);
1963 case ANIMTYPE_GPDATABLOCK:
1965 bGPdata *gpd= (bGPdata *)ale->data;
1968 gpd->flag ^= GP_DATA_EXPAND;
1970 notifierFlags |= (ND_ANIMCHAN|NA_EDITED);
1973 case ANIMTYPE_GPLAYER:
1975 #if 0 // XXX future of this is unclear
1976 bGPdata *gpd= (bGPdata *)ale->owner; // xxx depreceated
1977 bGPDlayer *gpl= (bGPDlayer *)ale->data;
1979 if (x >= (ACHANNEL_NAMEWIDTH-16)) {
1981 gpl->flag ^= GP_LAYER_LOCKED;
1983 else if (x >= (ACHANNEL_NAMEWIDTH-32)) {
1985 gpl->flag ^= GP_LAYER_HIDE;
1988 /* select/deselect */
1989 //if (G.qual & LR_SHIFTKEY) {
1990 //select_gplayer_channel(gpd, gpl, SELECT_INVERT);
1993 //deselect_gpencil_layers(data, 0);
1994 //select_gplayer_channel(gpd, gpl, SELECT_INVERT);
1997 #endif // XXX future of this is unclear
2002 printf("Error: Invalid channel type in mouse_anim_channels() \n");
2006 BLI_freelistN(&anim_data);
2008 /* return notifier flags */
2009 return notifierFlags;
2012 /* ------------------- */
2014 /* handle clicking */
2015 static int animchannels_mouseclick_invoke(bContext *C, wmOperator *op, wmEvent *event)
2020 int mval[2], channel_index;
2021 int notifierFlags = 0;
2026 /* get editor data */
2027 if (ANIM_animdata_get_context(C, &ac) == 0)
2028 return OPERATOR_CANCELLED;
2030 /* get useful pointers from animation context data */
2034 /* get mouse coordinates (in region coordinates) */
2035 mval[0]= (event->x - ar->winrct.xmin);
2036 mval[1]= (event->y - ar->winrct.ymin);
2038 /* select mode is either replace (deselect all, then add) or add/extend */
2039 if (RNA_boolean_get(op->ptr, "extend"))
2040 selectmode= SELECT_INVERT;
2041 else if (RNA_boolean_get(op->ptr, "children_only"))
2042 selectmode= -1; /* this is a bit of a special case for ActionGroups only... should it be removed or extended to all instead? */
2044 selectmode= SELECT_REPLACE;
2046 /* figure out which channel user clicked in
2047 * Note: although channels technically start at y= ACHANNEL_FIRST, we need to adjust by half a channel's height
2048 * so that the tops of channels get caught ok. Since ACHANNEL_FIRST is really ACHANNEL_HEIGHT, we simply use
2049 * ACHANNEL_HEIGHT_HALF.
2051 UI_view2d_region_to_view(v2d, mval[0], mval[1], &x, &y);
2052 UI_view2d_listview_view_to_cell(v2d, ACHANNEL_NAMEWIDTH, ACHANNEL_STEP, 0, (float)ACHANNEL_HEIGHT_HALF, x, y, NULL, &channel_index);
2054 /* handle mouse-click in the relevant channel then */
2055 notifierFlags= mouse_anim_channels(&ac, x, channel_index, selectmode);
2057 /* set notifier that things have changed */
2058 WM_event_add_notifier(C, NC_ANIMATION|notifierFlags, NULL);
2060 return OPERATOR_FINISHED;
2063 void ANIM_OT_channels_click (wmOperatorType *ot)
2066 ot->name= "Mouse Click on Channels";
2067 ot->idname= "ANIM_OT_channels_click";
2068 ot->description= "Handle mouse-clicks over animation channels";
2071 ot->invoke= animchannels_mouseclick_invoke;
2072 ot->poll= animedit_poll_channels_active;
2075 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2078 RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", ""); // SHIFTKEY
2079 RNA_def_boolean(ot->srna, "children_only", 0, "Select Children Only", ""); // CTRLKEY|SHIFTKEY
2082 /* ************************************************************************** */
2083 /* Operator Registration */
2085 void ED_operatortypes_animchannels(void)
2087 WM_operatortype_append(ANIM_OT_channels_select_all_toggle);
2088 WM_operatortype_append(ANIM_OT_channels_select_border);
2089 WM_operatortype_append(ANIM_OT_channels_click);
2091 WM_operatortype_append(ANIM_OT_channels_setting_enable);
2092 WM_operatortype_append(ANIM_OT_channels_setting_disable);
2093 WM_operatortype_append(ANIM_OT_channels_setting_invert);
2094 WM_operatortype_append(ANIM_OT_channels_setting_toggle);
2096 WM_operatortype_append(ANIM_OT_channels_delete);
2098 // XXX does this need to be a separate operator?
2099 WM_operatortype_append(ANIM_OT_channels_editable_toggle);
2101 WM_operatortype_append(ANIM_OT_channels_move);
2103 WM_operatortype_append(ANIM_OT_channels_expand);
2104 WM_operatortype_append(ANIM_OT_channels_collapse);
2106 WM_operatortype_append(ANIM_OT_channels_visibility_toggle);
2107 WM_operatortype_append(ANIM_OT_channels_visibility_set);
2110 // TODO: check on a poll callback for this, to get hotkeys into menus
2111 void ED_keymap_animchannels(wmKeyConfig *keyconf)
2113 wmKeyMap *keymap = WM_keymap_find(keyconf, "Animation Channels", 0, 0);
2117 // XXX for now, only leftmouse....
2118 WM_keymap_add_item(keymap, "ANIM_OT_channels_click", LEFTMOUSE, KM_PRESS, 0, 0);
2119 RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_click", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0)->ptr, "extend", 1);
2120 RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_click", LEFTMOUSE, KM_PRESS, KM_CTRL|KM_SHIFT, 0)->ptr, "children_only", 1);
2123 WM_keymap_add_item(keymap, "ANIM_OT_channels_select_all_toggle", AKEY, KM_PRESS, 0, 0);
2124 RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_select_all_toggle", IKEY, KM_PRESS, KM_CTRL, 0)->ptr, "invert", 1);
2127 WM_keymap_add_item(keymap, "ANIM_OT_channels_select_border", BKEY, KM_PRESS, 0, 0);
2128 WM_keymap_add_item(keymap, "ANIM_OT_channels_select_border", EVT_TWEAK_L, KM_ANY, 0, 0);
2131 WM_keymap_add_item(keymap, "ANIM_OT_channels_delete", XKEY, KM_PRESS, 0, 0);
2132 WM_keymap_add_item(keymap, "ANIM_OT_channels_delete", DELKEY, KM_PRESS, 0, 0);
2135 WM_keymap_add_item(keymap, "ANIM_OT_channels_setting_toggle", WKEY, KM_PRESS, KM_SHIFT, 0);
2136 WM_keymap_add_item(keymap, "ANIM_OT_channels_setting_enable", WKEY, KM_PRESS, KM_CTRL|KM_SHIFT, 0);
2137 WM_keymap_add_item(keymap, "ANIM_OT_channels_setting_disable", WKEY, KM_PRESS, KM_ALT, 0);
2139 /* settings - specialised hotkeys */
2140 WM_keymap_add_item(keymap, "ANIM_OT_channels_editable_toggle", TABKEY, KM_PRESS, 0, 0);
2142 /* expand/collapse */
2143 WM_keymap_add_item(keymap, "ANIM_OT_channels_expand", PADPLUSKEY, KM_PRESS, 0, 0);
2144 WM_keymap_add_item(keymap, "ANIM_OT_channels_collapse", PADMINUS, KM_PRESS, 0, 0);
2146 RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_expand", PADPLUSKEY, KM_PRESS, KM_CTRL, 0)->ptr, "all", 0);
2147 RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_collapse", PADMINUS, KM_PRESS, KM_CTRL, 0)->ptr, "all", 0);
2150 RNA_enum_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_move", PAGEUPKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "direction", REARRANGE_ANIMCHAN_UP);
2151 RNA_enum_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_move", PAGEDOWNKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "direction", REARRANGE_ANIMCHAN_DOWN);
2152 RNA_enum_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_move", PAGEUPKEY, KM_PRESS, KM_CTRL|KM_SHIFT, 0)->ptr, "direction", REARRANGE_ANIMCHAN_TOP);
2153 RNA_enum_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_move", PAGEDOWNKEY, KM_PRESS, KM_CTRL|KM_SHIFT, 0)->ptr, "direction", REARRANGE_ANIMCHAN_BOTTOM);
2155 /* Graph Editor only */
2156 WM_keymap_add_item(keymap, "ANIM_OT_channels_visibility_set", VKEY, KM_PRESS, 0, 0);
2157 WM_keymap_add_item(keymap, "ANIM_OT_channels_visibility_toggle", VKEY, KM_PRESS, KM_SHIFT, 0);
2160 /* ************************************************************************** */