2 * ***** BEGIN GPL LICENSE BLOCK *****
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung
19 * All rights reserved.
21 * Contributor(s): Joshua Leung
23 * ***** END GPL LICENSE BLOCK *****
26 /** \file blender/editors/animation/anim_channels_edit.c
27 * \ingroup edanimation
35 #include "MEM_guardedalloc.h"
37 #include "BLI_blenlib.h"
38 #include "BLI_utildefines.h"
39 #include "BLI_listbase.h"
41 #include "DNA_anim_types.h"
42 #include "DNA_object_types.h"
43 #include "DNA_scene_types.h"
44 #include "DNA_key_types.h"
45 #include "DNA_gpencil_types.h"
46 #include "DNA_mask_types.h"
48 #include "RNA_access.h"
49 #include "RNA_define.h"
51 #include "BKE_animsys.h"
52 #include "BKE_action.h"
53 #include "BKE_fcurve.h"
54 #include "BKE_gpencil.h"
55 #include "BKE_context.h"
56 #include "BKE_library.h"
58 #include "BKE_global.h"
59 #include "BKE_scene.h"
61 #include "DEG_depsgraph.h"
62 #include "DEG_depsgraph_build.h"
64 #include "UI_view2d.h"
66 #include "ED_anim_api.h"
67 #include "ED_armature.h"
68 #include "ED_keyframes_edit.h" // XXX move the select modes out of there!
69 #include "ED_object.h"
70 #include "ED_screen.h"
71 #include "ED_select_utils.h"
76 /* ************************************************************************** */
77 /* CHANNELS API - Exposed API */
79 /* -------------------------- Selection ------------------------------------- */
81 /* Set the given animation-channel as the active one for the active context */
82 // TODO: extend for animdata types...
83 void ANIM_set_active_channel(bAnimContext *ac, void *data, eAnimCont_Types datatype, eAnimFilter_Flags filter, void *channel_data, eAnim_ChannelType channel_type)
85 ListBase anim_data = {NULL, NULL};
88 /* try to build list of filtered items */
89 ANIM_animdata_filter(ac, &anim_data, filter, data, datatype);
90 if (BLI_listbase_is_empty(&anim_data))
93 /* only clear the 'active' flag for the channels of the same type */
94 for (ale = anim_data.first; ale; ale = ale->next) {
95 /* skip if types don't match */
96 if (channel_type != ale->type)
99 /* flag to set depends on type */
103 bActionGroup *agrp = (bActionGroup *)ale->data;
105 ACHANNEL_SET_FLAG(agrp, ACHANNEL_SETFLAG_CLEAR, AGRP_ACTIVE);
108 case ANIMTYPE_FCURVE:
109 case ANIMTYPE_NLACURVE:
111 FCurve *fcu = (FCurve *)ale->data;
113 ACHANNEL_SET_FLAG(fcu, ACHANNEL_SETFLAG_CLEAR, FCURVE_ACTIVE);
116 case ANIMTYPE_NLATRACK:
118 NlaTrack *nlt = (NlaTrack *)ale->data;
120 ACHANNEL_SET_FLAG(nlt, ACHANNEL_SETFLAG_CLEAR, NLATRACK_ACTIVE);
123 case ANIMTYPE_FILLACTD: /* Action Expander */
124 case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
127 case ANIMTYPE_DSCACHEFILE:
129 case ANIMTYPE_DSSKEY:
131 case ANIMTYPE_DSPART:
132 case ANIMTYPE_DSMBALL:
134 case ANIMTYPE_DSMESH:
137 case ANIMTYPE_DSLINESTYLE:
139 case ANIMTYPE_DSGPENCIL:
140 case ANIMTYPE_DSMCLIP:
142 /* need to verify that this data is valid for now */
144 ACHANNEL_SET_FLAG(ale->adt, ACHANNEL_SETFLAG_CLEAR, ADT_UI_ACTIVE);
148 case ANIMTYPE_GPLAYER:
150 bGPDlayer *gpl = (bGPDlayer *)ale->data;
152 ACHANNEL_SET_FLAG(gpl, ACHANNEL_SETFLAG_CLEAR, GP_LAYER_ACTIVE);
158 /* set active flag */
160 switch (channel_type) {
163 bActionGroup *agrp = (bActionGroup *)channel_data;
164 agrp->flag |= AGRP_ACTIVE;
167 case ANIMTYPE_FCURVE:
168 case ANIMTYPE_NLACURVE:
170 FCurve *fcu = (FCurve *)channel_data;
171 fcu->flag |= FCURVE_ACTIVE;
174 case ANIMTYPE_NLATRACK:
176 NlaTrack *nlt = (NlaTrack *)channel_data;
177 nlt->flag |= NLATRACK_ACTIVE;
180 case ANIMTYPE_FILLACTD: /* Action Expander */
181 case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
184 case ANIMTYPE_DSCACHEFILE:
186 case ANIMTYPE_DSSKEY:
188 case ANIMTYPE_DSPART:
189 case ANIMTYPE_DSMBALL:
191 case ANIMTYPE_DSMESH:
193 case ANIMTYPE_DSLINESTYLE:
195 case ANIMTYPE_DSNTREE:
197 case ANIMTYPE_DSGPENCIL:
198 case ANIMTYPE_DSMCLIP:
200 /* need to verify that this data is valid for now */
201 if (ale && ale->adt) {
202 ale->adt->flag |= ADT_UI_ACTIVE;
207 case ANIMTYPE_GPLAYER:
209 bGPDlayer *gpl = (bGPDlayer *)channel_data;
210 gpl->flag |= GP_LAYER_ACTIVE;
214 /* unhandled currently, but may be interesting */
215 case ANIMTYPE_MASKLAYER:
216 case ANIMTYPE_SHAPEKEY:
217 case ANIMTYPE_NLAACTION:
227 ANIM_animdata_freelist(&anim_data);
230 static void select_pchan_for_action_group(bAnimContext *ac, bActionGroup *agrp, bAnimListElem *ale)
232 /* Armatures-Specific Feature:
233 * See mouse_anim_channels() -> ANIMTYPE_GROUP case for more details (T38737)
235 if ((ac->ads->filterflag & ADS_FILTER_ONLYSEL) == 0) {
236 if ((ale->id) && (GS(ale->id->name) == ID_OB)) {
237 Object *ob = (Object *)ale->id;
238 if (ob->type == OB_ARMATURE) {
239 /* Assume for now that any group with corresponding name is what we want
240 * (i.e. for an armature whose location is animated, things would break
241 * if the user were to add a bone named "Location").
243 * TODO: check the first F-Curve or so to be sure...
245 bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, agrp->name);
246 if (agrp->flag & AGRP_SELECTED) {
247 ED_pose_bone_select(ob, pchan, true);
250 ED_pose_bone_select(ob, pchan, false);
257 /* Deselect all animation channels
258 * - data: pointer to datatype, as contained in bAnimContext
259 * - datatype: the type of data that 'data' represents (eAnimCont_Types)
260 * - test: check if deselecting instead of selecting
261 * - sel: eAnimChannels_SetFlag;
263 void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, eAnimCont_Types datatype, bool test, eAnimChannels_SetFlag sel)
265 ListBase anim_data = {NULL, NULL};
270 /* NOTE: no list visible, otherwise, we get dangling */
271 filter = ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS;
272 ANIM_animdata_filter(ac, &anim_data, filter, data, datatype);
274 /* See if we should be selecting or deselecting */
276 for (ale = anim_data.first; ale; ale = ale->next) {
282 if (ale->flag & SCE_DS_SELECTED)
283 sel = ACHANNEL_SETFLAG_CLEAR;
285 case ANIMTYPE_OBJECT:
286 #if 0 /* for now, do not take object selection into account, since it gets too annoying */
287 if (ale->flag & SELECT)
288 sel = ACHANNEL_SETFLAG_CLEAR;
292 if (ale->flag & AGRP_SELECTED)
293 sel = ACHANNEL_SETFLAG_CLEAR;
295 case ANIMTYPE_FCURVE:
296 case ANIMTYPE_NLACURVE:
297 if (ale->flag & FCURVE_SELECTED)
298 sel = ACHANNEL_SETFLAG_CLEAR;
300 case ANIMTYPE_SHAPEKEY:
301 if (ale->flag & KEYBLOCK_SEL)
302 sel = ACHANNEL_SETFLAG_CLEAR;
304 case ANIMTYPE_NLATRACK:
305 if (ale->flag & NLATRACK_SELECTED)
306 sel = ACHANNEL_SETFLAG_CLEAR;
309 case ANIMTYPE_FILLACTD: /* Action Expander */
310 case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
313 case ANIMTYPE_DSCACHEFILE:
315 case ANIMTYPE_DSSKEY:
317 case ANIMTYPE_DSPART:
318 case ANIMTYPE_DSMBALL:
320 case ANIMTYPE_DSMESH:
321 case ANIMTYPE_DSNTREE:
324 case ANIMTYPE_DSLINESTYLE:
326 case ANIMTYPE_DSGPENCIL:
327 case ANIMTYPE_DSMCLIP:
329 if ((ale->adt) && (ale->adt->flag & ADT_UI_SELECTED))
330 sel = ACHANNEL_SETFLAG_CLEAR;
333 case ANIMTYPE_GPLAYER:
334 if (ale->flag & GP_LAYER_SELECT)
335 sel = ACHANNEL_SETFLAG_CLEAR;
337 case ANIMTYPE_MASKLAYER:
338 if (ale->flag & MASK_LAYERFLAG_SELECT)
339 sel = ACHANNEL_SETFLAG_CLEAR;
345 /* Now set the flags */
346 for (ale = anim_data.first; ale; ale = ale->next) {
350 Scene *scene = (Scene *)ale->data;
352 ACHANNEL_SET_FLAG(scene, sel, SCE_DS_SELECTED);
355 ACHANNEL_SET_FLAG(scene, sel, ADT_UI_SELECTED);
359 case ANIMTYPE_OBJECT:
361 #if 0 /* for now, do not take object selection into account, since it gets too annoying */
362 Base *base = (Base *)ale->data;
363 Object *ob = base->object;
365 ACHANNEL_SET_FLAG(base, sel, SELECT);
366 ACHANNEL_SET_FLAG(ob, sel, SELECT);
369 ACHANNEL_SET_FLAG(ob, sel, ADT_UI_SELECTED);
376 bActionGroup *agrp = (bActionGroup *)ale->data;
377 ACHANNEL_SET_FLAG(agrp, sel, AGRP_SELECTED);
378 select_pchan_for_action_group(ac, agrp, ale);
379 agrp->flag &= ~AGRP_ACTIVE;
382 case ANIMTYPE_FCURVE:
383 case ANIMTYPE_NLACURVE:
385 FCurve *fcu = (FCurve *)ale->data;
387 ACHANNEL_SET_FLAG(fcu, sel, FCURVE_SELECTED);
388 fcu->flag &= ~FCURVE_ACTIVE;
391 case ANIMTYPE_SHAPEKEY:
393 KeyBlock *kb = (KeyBlock *)ale->data;
395 ACHANNEL_SET_FLAG(kb, sel, KEYBLOCK_SEL);
398 case ANIMTYPE_NLATRACK:
400 NlaTrack *nlt = (NlaTrack *)ale->data;
402 ACHANNEL_SET_FLAG(nlt, sel, NLATRACK_SELECTED);
403 nlt->flag &= ~NLATRACK_ACTIVE;
406 case ANIMTYPE_FILLACTD: /* Action Expander */
407 case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
410 case ANIMTYPE_DSCACHEFILE:
412 case ANIMTYPE_DSSKEY:
414 case ANIMTYPE_DSPART:
415 case ANIMTYPE_DSMBALL:
417 case ANIMTYPE_DSMESH:
418 case ANIMTYPE_DSNTREE:
421 case ANIMTYPE_DSLINESTYLE:
423 case ANIMTYPE_DSGPENCIL:
424 case ANIMTYPE_DSMCLIP:
426 /* need to verify that this data is valid for now */
428 ACHANNEL_SET_FLAG(ale->adt, sel, ADT_UI_SELECTED);
429 ale->adt->flag &= ~ADT_UI_ACTIVE;
433 case ANIMTYPE_GPLAYER:
435 bGPDlayer *gpl = (bGPDlayer *)ale->data;
437 ACHANNEL_SET_FLAG(gpl, sel, GP_LAYER_SELECT);
440 case ANIMTYPE_MASKLAYER:
442 MaskLayer *masklay = (MaskLayer *)ale->data;
444 ACHANNEL_SET_FLAG(masklay, sel, MASK_LAYERFLAG_SELECT);
451 ANIM_animdata_freelist(&anim_data);
454 /* ---------------------------- Graph Editor ------------------------------------- */
456 /* Flush visibility (for Graph Editor) changes up/down hierarchy for changes in the given setting
457 * - anim_data: list of the all the anim channels that can be chosen
458 * -> filtered using ANIMFILTER_CHANNELS only, since if we took VISIBLE too,
459 * then the channels under closed expanders get ignored...
460 * - ale_setting: the anim channel (not in the anim_data list directly, though occurring there)
461 * with the new state of the setting that we want flushed up/down the hierarchy
462 * - setting: type of setting to set
463 * - on: whether the visibility setting has been enabled or disabled
465 void ANIM_flush_setting_anim_channels(bAnimContext *ac, ListBase *anim_data, bAnimListElem *ale_setting, eAnimChannel_Settings setting, eAnimChannels_SetFlag mode)
467 bAnimListElem *ale, *match = NULL;
468 int prevLevel = 0, matchLevel = 0;
471 if (ELEM(NULL, anim_data, anim_data->first))
474 if (setting == ACHANNEL_SETTING_ALWAYS_VISIBLE) {
478 /* find the channel that got changed */
479 for (ale = anim_data->first; ale; ale = ale->next) {
480 /* compare data, and type as main way of identifying the channel */
481 if ((ale->data == ale_setting->data) && (ale->type == ale_setting->type)) {
482 /* we also have to check the ID, this is assigned to, since a block may have multiple users */
483 /* TODO: is the owner-data more revealing? */
484 if (ale->id == ale_setting->id) {
491 printf("ERROR: no channel matching the one changed was found\n");
495 const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale_setting);
498 printf("ERROR: no channel info for the changed channel\n");
502 /* get the level of the channel that was affected
503 * - we define the level as simply being the offset for the start of the channel
505 matchLevel = (acf->get_offset) ? acf->get_offset(ac, ale_setting) : 0;
506 prevLevel = matchLevel;
512 * - only flush up if the current state is now enabled (positive 'on' state is default)
513 * (otherwise, it's too much work to force the parents to be inactive too)
515 * For everything else:
516 * - only flush up if the current state is now disabled (negative 'off' state is default)
517 * (otherwise, it's too much work to force the parents to be active too)
519 if ( ((setting == ACHANNEL_SETTING_VISIBLE) && (mode != ACHANNEL_SETFLAG_CLEAR)) ||
520 ((setting != ACHANNEL_SETTING_VISIBLE) && (mode == ACHANNEL_SETFLAG_CLEAR)))
522 /* go backwards in the list, until the highest-ranking element (by indention has been covered) */
523 for (ale = match->prev; ale; ale = ale->prev) {
524 const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale);
527 /* if no channel info was found, skip, since this type might not have any useful info */
531 /* get the level of the current channel traversed
532 * - we define the level as simply being the offset for the start of the channel
534 level = (acf->get_offset) ? acf->get_offset(ac, ale) : 0;
536 /* if the level is 'less than' (i.e. more important) the level we're matching
537 * but also 'less than' the level just tried (i.e. only the 1st group above grouped F-Curves,
538 * when toggling visibility of F-Curves, gets flushed, which should happen if we don't let prevLevel
539 * get updated below once the first 1st group is found)...
541 if (level < prevLevel) {
542 /* flush the new status... */
543 ANIM_channel_setting_set(ac, ale, setting, mode);
545 /* store this level as the 'old' level now */
548 /* if the level is 'greater than' (i.e. less important) than the previous level... */
549 else if (level > prevLevel) {
550 /* if previous level was a base-level (i.e. 0 offset / root of one hierarchy),
555 /* otherwise, this level weaves into another sibling hierarchy to the previous one just
556 * finished, so skip until we get to the parent of this level
564 /* flush down (always) */
566 /* go forwards in the list, until the lowest-ranking element (by indention has been covered) */
567 for (ale = match->next; ale; ale = ale->next) {
568 const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale);
571 /* if no channel info was found, skip, since this type might not have any useful info */
575 /* get the level of the current channel traversed
576 * - we define the level as simply being the offset for the start of the channel
578 level = (acf->get_offset) ? acf->get_offset(ac, ale) : 0;
580 /* if the level is 'greater than' (i.e. less important) the channel that was changed,
581 * flush the new status...
583 if (level > matchLevel)
584 ANIM_channel_setting_set(ac, ale, setting, mode);
585 /* however, if the level is 'less than or equal to' the channel that was changed,
586 * (i.e. the current channel is as important if not more important than the changed channel)
587 * then we should stop, since we've found the last one of the children we should flush
592 /* store this level as the 'old' level now */
593 // prevLevel = level; // XXX: prevLevel is unused
598 /* -------------------------- F-Curves ------------------------------------- */
600 /* Delete the given F-Curve from its AnimData block */
601 void ANIM_fcurve_delete_from_animdata(bAnimContext *ac, AnimData *adt, FCurve *fcu)
603 /* - if no AnimData, we've got nowhere to remove the F-Curve from
604 * (this doesn't guarantee that the F-Curve is in there, but at least we tried
605 * - if no F-Curve, there is nothing to remove
607 if (ELEM(NULL, adt, fcu))
610 /* remove from whatever list it came from
614 * - TODO... some others?
616 if ((ac) && (ac->datatype == ANIMCONT_DRIVERS)) {
618 BLI_remlink(&adt->drivers, fcu);
620 else if (adt->action) {
621 bAction *act = adt->action;
623 /* remove from group or action, whichever one "owns" the F-Curve */
625 bActionGroup *agrp = fcu->grp;
627 /* remove F-Curve from group+action */
628 action_groups_remove_channel(act, fcu);
630 /* if group has no more channels, remove it too,
631 * otherwise can have many dangling groups [#33541]
633 if (BLI_listbase_is_empty(&agrp->channels)) {
634 BLI_freelinkN(&act->groups, agrp);
638 BLI_remlink(&act->curves, fcu);
641 /* if action has no more F-Curves as a result of this, unlink it from
642 * AnimData if it did not come from a NLA Strip being tweaked.
644 * This is done so that we don't have dangling Object+Action entries in
645 * channel list that are empty, and linger around long after the data they
646 * are for has disappeared (and probably won't come back).
648 if (BLI_listbase_is_empty(&act->curves) && (adt->flag & ADT_NLA_EDIT_ON) == 0) {
654 /* free the F-Curve itself */
658 /* ************************************************************************** */
661 /* ****************** Operator Utilities ********************************** */
663 /* poll callback for being in an Animation Editor channels list region */
664 static bool animedit_poll_channels_active(bContext *C)
666 ScrArea *sa = CTX_wm_area(C);
668 /* channels region test */
669 /* TODO: could enhance with actually testing if channels region? */
670 if (ELEM(NULL, sa, CTX_wm_region(C)))
672 /* animation editor test */
673 if (ELEM(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA) == 0)
679 /* poll callback for Animation Editor channels list region + not in NLA-tweakmode for NLA */
680 static bool animedit_poll_channels_nla_tweakmode_off(bContext *C)
682 ScrArea *sa = CTX_wm_area(C);
683 Scene *scene = CTX_data_scene(C);
685 /* channels region test */
686 /* TODO: could enhance with actually testing if channels region? */
687 if (ELEM(NULL, sa, CTX_wm_region(C)))
689 /* animation editor test */
690 if (ELEM(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA) == 0)
693 /* NLA TweakMode test */
694 if (sa->spacetype == SPACE_NLA) {
695 if ((scene == NULL) || (scene->flag & SCE_NLA_EDIT_ON))
702 /* ****************** Rearrange Channels Operator ******************* */
704 /* constants for channel rearranging */
705 /* WARNING: don't change existing ones without modifying rearrange func accordingly */
706 typedef enum eRearrangeAnimChan_Mode {
707 REARRANGE_ANIMCHAN_TOP = -2,
708 REARRANGE_ANIMCHAN_UP = -1,
709 REARRANGE_ANIMCHAN_DOWN = 1,
710 REARRANGE_ANIMCHAN_BOTTOM = 2
711 } eRearrangeAnimChan_Mode;
713 /* defines for rearranging channels */
714 static const EnumPropertyItem prop_animchannel_rearrange_types[] = {
715 {REARRANGE_ANIMCHAN_TOP, "TOP", 0, "To Top", ""},
716 {REARRANGE_ANIMCHAN_UP, "UP", 0, "Up", ""},
717 {REARRANGE_ANIMCHAN_DOWN, "DOWN", 0, "Down", ""},
718 {REARRANGE_ANIMCHAN_BOTTOM, "BOTTOM", 0, "To Bottom", ""},
719 {0, NULL, 0, NULL, NULL}
722 /* Reordering "Islands" Defines ----------------------------------- */
724 /* Island definition - just a listbase container */
725 typedef struct tReorderChannelIsland {
726 struct tReorderChannelIsland *next, *prev;
728 ListBase channels; /* channels within this region with the same state */
729 int flag; /* eReorderIslandFlag */
730 } tReorderChannelIsland;
732 /* flags for channel reordering islands */
733 typedef enum eReorderIslandFlag {
734 REORDER_ISLAND_SELECTED = (1 << 0), /* island is selected */
735 REORDER_ISLAND_UNTOUCHABLE = (1 << 1), /* island should be ignored */
736 REORDER_ISLAND_MOVED = (1 << 2), /* island has already been moved */
737 REORDER_ISLAND_HIDDEN = (1 << 3), /* island is not visible */
738 } eReorderIslandFlag;
741 /* Rearrange Methods --------------------------------------------- */
743 static bool rearrange_island_ok(tReorderChannelIsland *island)
745 /* island must not be untouchable */
746 if (island->flag & REORDER_ISLAND_UNTOUCHABLE)
749 /* island should be selected to be moved */
750 return (island->flag & REORDER_ISLAND_SELECTED) && !(island->flag & REORDER_ISLAND_MOVED);
753 /* ............................. */
755 static bool rearrange_island_top(ListBase *list, tReorderChannelIsland *island)
757 if (rearrange_island_ok(island)) {
758 /* remove from current position */
759 BLI_remlink(list, island);
761 /* make it first element */
762 BLI_insertlinkbefore(list, list->first, island);
770 static bool rearrange_island_up(ListBase *list, tReorderChannelIsland *island)
772 if (rearrange_island_ok(island)) {
773 /* moving up = moving before the previous island, otherwise we're in the same place */
774 tReorderChannelIsland *prev = island->prev;
776 /* Skip hidden islands! */
777 while (prev && prev->flag & REORDER_ISLAND_HIDDEN) {
782 /* remove from current position */
783 BLI_remlink(list, island);
786 BLI_insertlinkbefore(list, prev, island);
795 static bool rearrange_island_down(ListBase *list, tReorderChannelIsland *island)
797 if (rearrange_island_ok(island)) {
798 /* moving down = moving after the next island, otherwise we're in the same place */
799 tReorderChannelIsland *next = island->next;
801 /* Skip hidden islands! */
802 while (next && next->flag & REORDER_ISLAND_HIDDEN) {
807 /* can only move past if next is not untouchable (i.e. nothing can go after it) */
808 if ((next->flag & REORDER_ISLAND_UNTOUCHABLE) == 0) {
809 /* remove from current position */
810 BLI_remlink(list, island);
813 BLI_insertlinkafter(list, next, island);
818 /* else: no next channel, so we're at the bottom already, so can't move */
824 static bool rearrange_island_bottom(ListBase *list, tReorderChannelIsland *island)
826 if (rearrange_island_ok(island)) {
827 tReorderChannelIsland *last = list->last;
829 /* remove island from current position */
830 BLI_remlink(list, island);
832 /* add before or after the last channel? */
833 if ((last->flag & REORDER_ISLAND_UNTOUCHABLE) == 0) {
834 /* can add after it */
835 BLI_addtail(list, island);
838 /* can at most go just before it, since last cannot be moved */
839 BLI_insertlinkbefore(list, last, island);
849 /* ............................. */
852 * typedef for channel rearranging function
854 * \param list: List of tReorderChannelIsland's that channels belong to
855 * \param island: Island to be moved
856 * \return Whether operation was a success
858 typedef bool (*AnimChanRearrangeFp)(ListBase *list, tReorderChannelIsland *island);
860 /* get rearranging function, given 'rearrange' mode */
861 static AnimChanRearrangeFp rearrange_get_mode_func(eRearrangeAnimChan_Mode mode)
864 case REARRANGE_ANIMCHAN_TOP:
865 return rearrange_island_top;
866 case REARRANGE_ANIMCHAN_UP:
867 return rearrange_island_up;
868 case REARRANGE_ANIMCHAN_DOWN:
869 return rearrange_island_down;
870 case REARRANGE_ANIMCHAN_BOTTOM:
871 return rearrange_island_bottom;
877 /* Rearrange Islands Generics ------------------------------------- */
879 /* add channel into list of islands */
880 static void rearrange_animchannel_add_to_islands(ListBase *islands, ListBase *srcList,
881 Link *channel, eAnim_ChannelType type,
882 const bool is_hidden)
884 tReorderChannelIsland *island = islands->last; /* always try to add to last island if possible */
885 bool is_sel = false, is_untouchable = false;
887 /* get flags - selected and untouchable from the channel */
891 bActionGroup *agrp = (bActionGroup *)channel;
893 is_sel = SEL_AGRP(agrp);
894 is_untouchable = (agrp->flag & AGRP_TEMP) != 0;
897 case ANIMTYPE_FCURVE:
898 case ANIMTYPE_NLACURVE:
900 FCurve *fcu = (FCurve *)channel;
902 is_sel = SEL_FCU(fcu);
905 case ANIMTYPE_NLATRACK:
907 NlaTrack *nlt = (NlaTrack *)channel;
909 is_sel = SEL_NLT(nlt);
912 case ANIMTYPE_GPLAYER:
914 bGPDlayer *gpl = (bGPDlayer *)channel;
916 is_sel = SEL_GPL(gpl);
920 printf("rearrange_animchannel_add_to_islands(): don't know how to handle channels of type %u\n", type);
924 /* do we need to add to a new island? */
925 if (/* 1) no islands yet */
927 /* 2) unselected islands have single channels only - to allow up/down movement */
928 ((island->flag & REORDER_ISLAND_SELECTED) == 0) ||
929 /* 3) if channel is unselected, stop existing island (it was either wrong sel status, or full already) */
931 /* 4) hidden status changes */
932 ((island->flag & REORDER_ISLAND_HIDDEN) != is_hidden)
935 /* create a new island now */
936 island = MEM_callocN(sizeof(tReorderChannelIsland), "tReorderChannelIsland");
937 BLI_addtail(islands, island);
940 island->flag |= REORDER_ISLAND_SELECTED;
942 island->flag |= REORDER_ISLAND_UNTOUCHABLE;
944 island->flag |= REORDER_ISLAND_HIDDEN;
947 /* add channel to island - need to remove it from its existing list first though */
948 BLI_remlink(srcList, channel);
949 BLI_addtail(&island->channels, channel);
952 /* flatten islands out into a single list again */
953 static void rearrange_animchannel_flatten_islands(ListBase *islands, ListBase *srcList)
955 tReorderChannelIsland *island, *isn = NULL;
957 /* make sure srcList is empty now */
958 BLI_assert(BLI_listbase_is_empty(srcList));
960 /* go through merging islands */
961 for (island = islands->first; island; island = isn) {
964 /* merge island channels back to main list, then delete the island */
965 BLI_movelisttolist(srcList, &island->channels);
966 BLI_freelinkN(islands, island);
970 /* ............................. */
972 /* get a list of all bAnimListElem's of a certain type which are currently visible */
973 static void rearrange_animchannels_filter_visible(ListBase *anim_data_visible, bAnimContext *ac, eAnim_ChannelType type)
975 ListBase anim_data = {NULL, NULL};
976 bAnimListElem *ale, *ale_next;
977 int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
979 /* get all visible channels */
980 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
982 /* now, only keep the ones that are of the types we are interested in */
983 for (ale = anim_data.first; ale; ale = ale_next) {
984 ale_next = ale->next;
986 if (ale->type != type) {
987 BLI_freelinkN(&anim_data, ale);
991 /* return cleaned up list */
992 *anim_data_visible = anim_data;
995 /* performing rearranging of channels using islands */
996 static bool rearrange_animchannel_islands(ListBase *list, AnimChanRearrangeFp rearrange_func,
997 eRearrangeAnimChan_Mode mode, eAnim_ChannelType type,
998 ListBase *anim_data_visible)
1000 ListBase islands = {NULL, NULL};
1001 Link *channel, *chanNext = NULL;
1004 /* don't waste effort on an empty list */
1005 if (BLI_listbase_is_empty(list))
1008 /* group channels into islands */
1009 for (channel = list->first; channel; channel = chanNext) {
1010 /* find out whether this channel is present in anim_data_visible or not! */
1011 const bool is_hidden = (BLI_findptr(anim_data_visible, channel, offsetof(bAnimListElem, data)) == NULL);
1012 chanNext = channel->next;
1013 rearrange_animchannel_add_to_islands(&islands, list, channel, type, is_hidden);
1016 /* perform moving of selected islands now, but only if there is more than one of 'em so that something will happen
1017 * - scanning of the list is performed in the opposite direction to the direction we're moving things, so that we
1018 * shouldn't need to encounter items we've moved already
1020 if (islands.first != islands.last) {
1021 tReorderChannelIsland *first = (mode > 0) ? islands.last : islands.first;
1022 tReorderChannelIsland *island, *isn = NULL;
1024 for (island = first; island; island = isn) {
1025 isn = (mode > 0) ? island->prev : island->next;
1027 /* perform rearranging */
1028 if (rearrange_func(&islands, island)) {
1029 island->flag |= REORDER_ISLAND_MOVED;
1035 /* ungroup islands */
1036 rearrange_animchannel_flatten_islands(&islands, list);
1038 /* did we do anything? */
1042 /* NLA Specific Stuff ----------------------------------------------------- */
1044 /* Change the order NLA Tracks within NLA Stack
1045 * ! NLA tracks are displayed in opposite order, so directions need care
1046 * mode: REARRANGE_ANIMCHAN_*
1048 static void rearrange_nla_channels(bAnimContext *ac, AnimData *adt, eRearrangeAnimChan_Mode mode)
1050 AnimChanRearrangeFp rearrange_func;
1051 ListBase anim_data_visible = {NULL, NULL};
1053 /* hack: invert mode so that functions will work in right order */
1056 /* get rearranging function */
1057 rearrange_func = rearrange_get_mode_func(mode);
1058 if (rearrange_func == NULL)
1061 /* Filter visible data. */
1062 rearrange_animchannels_filter_visible(&anim_data_visible, ac, ANIMTYPE_NLATRACK);
1064 /* perform rearranging on tracks list */
1065 rearrange_animchannel_islands(&adt->nla_tracks, rearrange_func, mode, ANIMTYPE_NLATRACK, &anim_data_visible);
1067 /* free temp data */
1068 BLI_freelistN(&anim_data_visible);
1071 /* Drivers Specific Stuff ------------------------------------------------- */
1073 /* Change the order drivers within AnimData block
1074 * mode: REARRANGE_ANIMCHAN_*
1076 static void rearrange_driver_channels(bAnimContext *ac, AnimData *adt, eRearrangeAnimChan_Mode mode)
1078 /* get rearranging function */
1079 AnimChanRearrangeFp rearrange_func = rearrange_get_mode_func(mode);
1080 ListBase anim_data_visible = {NULL, NULL};
1082 if (rearrange_func == NULL)
1085 /* only consider drivers if they're accessible */
1086 if (EXPANDED_DRVD(adt) == 0)
1089 /* Filter visible data. */
1090 rearrange_animchannels_filter_visible(&anim_data_visible, ac, ANIMTYPE_FCURVE);
1092 /* perform rearranging on drivers list (drivers are really just F-Curves) */
1093 rearrange_animchannel_islands(&adt->drivers, rearrange_func, mode, ANIMTYPE_FCURVE, &anim_data_visible);
1095 /* free temp data */
1096 BLI_freelistN(&anim_data_visible);
1099 /* Action Specific Stuff ------------------------------------------------- */
1101 /* make sure all action-channels belong to a group (and clear action's list) */
1102 static void split_groups_action_temp(bAction *act, bActionGroup *tgrp)
1110 /* Separate F-Curves into lists per group */
1111 for (agrp = act->groups.first; agrp; agrp = agrp->next) {
1112 if (agrp->channels.first) {
1113 fcu = agrp->channels.last;
1114 act->curves.first = fcu->next;
1116 fcu = agrp->channels.first;
1119 fcu = agrp->channels.last;
1124 /* Initialize memory for temp-group */
1125 memset(tgrp, 0, sizeof(bActionGroup));
1126 tgrp->flag |= (AGRP_EXPANDED | AGRP_TEMP);
1127 BLI_strncpy(tgrp->name, "#TempGroup", sizeof(tgrp->name));
1129 /* Move any action-channels not already moved, to the temp group */
1130 if (act->curves.first) {
1132 fcu = act->curves.first;
1134 tgrp->channels.first = fcu;
1135 act->curves.first = NULL;
1138 fcu = act->curves.last;
1140 tgrp->channels.last = fcu;
1141 act->curves.last = NULL;
1143 /* ensure that all of these get their group set to this temp group
1144 * (so that visibility filtering works)
1146 for (fcu = tgrp->channels.first; fcu; fcu = fcu->next) {
1151 /* Add temp-group to list */
1152 BLI_addtail(&act->groups, tgrp);
1155 /* link lists of channels that groups have */
1156 static void join_groups_action_temp(bAction *act)
1160 for (agrp = act->groups.first; agrp; agrp = agrp->next) {
1163 /* add list of channels to action's channels */
1164 tempGroup = agrp->channels;
1165 BLI_movelisttolist(&act->curves, &agrp->channels);
1166 agrp->channels = tempGroup;
1168 /* clear moved flag */
1169 agrp->flag &= ~AGRP_MOVED;
1171 /* if group was temporary one:
1172 * - unassign all FCurves which were temporarily added to it
1173 * - remove from list (but don't free as it's on the stack!)
1175 if (agrp->flag & AGRP_TEMP) {
1178 for (fcu = agrp->channels.first; fcu; fcu = fcu->next) {
1182 BLI_remlink(&act->groups, agrp);
1188 /* Change the order of anim-channels within action
1189 * mode: REARRANGE_ANIMCHAN_*
1191 static void rearrange_action_channels(bAnimContext *ac, bAction *act, eRearrangeAnimChan_Mode mode)
1194 ListBase anim_data_visible = {NULL, NULL};
1197 /* get rearranging function */
1198 AnimChanRearrangeFp rearrange_func = rearrange_get_mode_func(mode);
1200 if (rearrange_func == NULL)
1203 /* make sure we're only operating with groups (vs a mixture of groups+curves) */
1204 split_groups_action_temp(act, &tgrp);
1206 /* Filter visible data. */
1207 rearrange_animchannels_filter_visible(&anim_data_visible, ac, ANIMTYPE_GROUP);
1209 /* rearrange groups first
1210 * - the group's channels will only get considered if nothing happened when rearranging the groups
1211 * i.e. the rearrange function returned 0
1213 do_channels = (rearrange_animchannel_islands(&act->groups, rearrange_func, mode, ANIMTYPE_GROUP,
1214 &anim_data_visible) == 0);
1216 /* free temp data */
1217 BLI_freelistN(&anim_data_visible);
1222 /* Filter visible data. */
1223 rearrange_animchannels_filter_visible(&anim_data_visible, ac, ANIMTYPE_FCURVE);
1225 for (agrp = act->groups.first; agrp; agrp = agrp->next) {
1226 /* only consider F-Curves if they're visible (group expanded) */
1227 if (EXPANDED_AGRP(ac, agrp)) {
1228 rearrange_animchannel_islands(&agrp->channels, rearrange_func, mode, ANIMTYPE_FCURVE,
1229 &anim_data_visible);
1233 /* free temp data */
1234 BLI_freelistN(&anim_data_visible);
1237 /* assemble lists into one list (and clear moved tags) */
1238 join_groups_action_temp(act);
1241 /* ------------------- */
1243 static void rearrange_nla_control_channels(bAnimContext *ac, AnimData *adt, eRearrangeAnimChan_Mode mode)
1245 ListBase anim_data_visible = {NULL, NULL};
1250 /* get rearranging function */
1251 AnimChanRearrangeFp rearrange_func = rearrange_get_mode_func(mode);
1253 if (rearrange_func == NULL)
1256 /* skip if these curves aren't being shown */
1257 if (adt->flag & ADT_NLA_SKEYS_COLLAPSED)
1260 /* Filter visible data. */
1261 rearrange_animchannels_filter_visible(&anim_data_visible, ac, ANIMTYPE_NLACURVE);
1263 /* we cannot rearrange between strips, but within each strip, we can rearrange those curves */
1264 for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next) {
1265 for (strip = nlt->strips.first; strip; strip = strip->next) {
1266 rearrange_animchannel_islands(&strip->fcurves, rearrange_func, mode, ANIMTYPE_NLACURVE,
1267 &anim_data_visible);
1271 /* free temp data */
1272 BLI_freelistN(&anim_data_visible);
1275 /* ------------------- */
1277 static void rearrange_gpencil_channels(bAnimContext *ac, eRearrangeAnimChan_Mode mode)
1279 ListBase anim_data = {NULL, NULL};
1283 /* get rearranging function */
1284 AnimChanRearrangeFp rearrange_func = rearrange_get_mode_func(mode);
1286 if (rearrange_func == NULL)
1289 /* get Grease Pencil datablocks */
1290 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA);
1291 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1293 for (ale = anim_data.first; ale; ale = ale->next) {
1294 ListBase anim_data_visible = {NULL, NULL};
1295 bGPdata *gpd = ale->data;
1297 /* only consider layers if this datablock is open */
1298 BLI_assert(ale->type == ANIMTYPE_GPDATABLOCK);
1299 if ((gpd->flag & GP_DATA_EXPAND) == 0)
1302 /* Filter visible data. */
1303 rearrange_animchannels_filter_visible(&anim_data_visible, ac, ANIMTYPE_GPLAYER);
1305 /* rearrange datablock's layers */
1306 rearrange_animchannel_islands(&gpd->layers, rearrange_func, mode, ANIMTYPE_GPLAYER, &anim_data_visible);
1308 /* free visible layers data */
1309 BLI_freelistN(&anim_data_visible);
1312 /* free GPD channel data */
1313 ANIM_animdata_freelist(&anim_data);
1316 /* ------------------- */
1318 static int animchannels_rearrange_exec(bContext *C, wmOperator *op)
1321 eRearrangeAnimChan_Mode mode;
1323 /* get editor data */
1324 if (ANIM_animdata_get_context(C, &ac) == 0)
1325 return OPERATOR_CANCELLED;
1328 mode = RNA_enum_get(op->ptr, "direction");
1330 /* method to move channels depends on the editor */
1331 if (ac.datatype == ANIMCONT_GPENCIL) {
1332 /* Grease Pencil channels */
1333 rearrange_gpencil_channels(&ac, mode);
1335 else if (ac.datatype == ANIMCONT_MASK) {
1336 /* Grease Pencil channels */
1337 printf("Mask does not supported for moving yet\n");
1339 else if (ac.datatype == ANIMCONT_ACTION) {
1340 /* Directly rearrange action's channels */
1341 rearrange_action_channels(&ac, ac.data, mode);
1344 ListBase anim_data = {NULL, NULL};
1348 /* get animdata blocks */
1349 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA);
1350 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1352 for (ale = anim_data.first; ale; ale = ale->next) {
1353 AnimData *adt = ale->data;
1355 switch (ac.datatype) {
1356 case ANIMCONT_NLA: /* NLA-tracks only */
1357 rearrange_nla_channels(&ac, adt, mode);
1358 DEG_id_tag_update(ale->id, ID_RECALC_ANIMATION | ID_RECALC_COPY_ON_WRITE);
1361 case ANIMCONT_DRIVERS: /* Drivers list only */
1362 rearrange_driver_channels(&ac, adt, mode);
1365 case ANIMCONT_ACTION: /* Single Action only... */
1366 case ANIMCONT_SHAPEKEY: // DOUBLE CHECK ME...
1369 rearrange_action_channels(&ac, adt->action, mode);
1370 else if (G.debug & G_DEBUG)
1371 printf("Animdata has no action\n");
1375 default: /* DopeSheet/Graph Editor - Some Actions + NLA Control Curves */
1377 /* NLA Control Curves */
1378 if (adt->nla_tracks.first)
1379 rearrange_nla_control_channels(&ac, adt, mode);
1383 rearrange_action_channels(&ac, adt->action, mode);
1384 else if (G.debug & G_DEBUG)
1385 printf("Animdata has no action\n");
1391 /* free temp data */
1392 ANIM_animdata_freelist(&anim_data);
1395 /* send notifier that things have changed */
1396 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1398 return OPERATOR_FINISHED;
1401 static void ANIM_OT_channels_move(wmOperatorType *ot)
1404 ot->name = "Move Channels";
1405 ot->idname = "ANIM_OT_channels_move";
1406 ot->description = "Rearrange selected animation channels";
1409 ot->exec = animchannels_rearrange_exec;
1410 ot->poll = animedit_poll_channels_nla_tweakmode_off;
1413 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1416 ot->prop = RNA_def_enum(ot->srna, "direction", prop_animchannel_rearrange_types, REARRANGE_ANIMCHAN_DOWN, "Direction", "");
1419 /* ******************** Group Channel Operator ************************ */
1421 static bool animchannels_grouping_poll(bContext *C)
1423 ScrArea *sa = CTX_wm_area(C);
1426 /* channels region test */
1427 /* TODO: could enhance with actually testing if channels region? */
1428 if (ELEM(NULL, sa, CTX_wm_region(C)))
1431 /* animation editor test - must be suitable modes only */
1432 sl = CTX_wm_space_data(C);
1434 switch (sa->spacetype) {
1438 SpaceAction *saction = (SpaceAction *)sl;
1440 /* dopesheet and action only - all others are for other datatypes or have no groups */
1441 if (ELEM(saction->mode, SACTCONT_ACTION, SACTCONT_DOPESHEET) == 0)
1448 SpaceIpo *sipo = (SpaceIpo *)sl;
1450 /* drivers can't have groups... */
1451 if (sipo->mode != SIPO_MODE_ANIMATION)
1456 /* unsupported... */
1464 /* ----------------------------------------------------------- */
1466 static void animchannels_group_channels(bAnimContext *ac, bAnimListElem *adt_ref, const char name[])
1468 AnimData *adt = adt_ref->adt;
1469 bAction *act = adt->action;
1472 ListBase anim_data = {NULL, NULL};
1475 /* find selected F-Curves to re-group */
1476 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL);
1477 ANIM_animdata_filter(ac, &anim_data, filter, adt_ref, ANIMCONT_CHANNEL);
1479 if (anim_data.first) {
1483 /* create new group, which should now be part of the action */
1484 agrp = action_groups_add_new(act, name);
1485 BLI_assert(agrp != NULL);
1487 /* transfer selected F-Curves across to new group */
1488 for (ale = anim_data.first; ale; ale = ale->next) {
1489 FCurve *fcu = (FCurve *)ale->data;
1490 bActionGroup *grp = fcu->grp;
1492 /* remove F-Curve from group, then group too if it is now empty */
1493 action_groups_remove_channel(act, fcu);
1495 if ((grp) && BLI_listbase_is_empty(&grp->channels)) {
1496 BLI_freelinkN(&act->groups, grp);
1499 /* add F-Curve to group */
1500 action_groups_add_channel(act, agrp, fcu);
1505 ANIM_animdata_freelist(&anim_data);
1509 static int animchannels_group_exec(bContext *C, wmOperator *op)
1512 char name[MAX_NAME];
1514 /* get editor data */
1515 if (ANIM_animdata_get_context(C, &ac) == 0)
1516 return OPERATOR_CANCELLED;
1518 /* get name for new group */
1519 RNA_string_get(op->ptr, "name", name);
1521 /* XXX: name for group should never be empty... */
1523 ListBase anim_data = {NULL, NULL};
1527 /* handle each animdata block separately, so that the regrouping doesn't flow into blocks */
1528 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA | ANIMFILTER_NODUPLIS);
1529 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1531 for (ale = anim_data.first; ale; ale = ale->next) {
1532 animchannels_group_channels(&ac, ale, name);
1535 /* free temp data */
1536 ANIM_animdata_freelist(&anim_data);
1539 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1542 return OPERATOR_FINISHED;
1545 static void ANIM_OT_channels_group(wmOperatorType *ot)
1548 ot->name = "Group Channels";
1549 ot->idname = "ANIM_OT_channels_group";
1550 ot->description = "Add selected F-Curves to a new group";
1553 ot->invoke = WM_operator_props_popup;
1554 ot->exec = animchannels_group_exec;
1555 ot->poll = animchannels_grouping_poll;
1558 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1561 ot->prop = RNA_def_string(ot->srna, "name", "New Group",
1562 sizeof(((bActionGroup *)NULL)->name),
1563 "Name", "Name of newly created group");
1564 /* RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE); */ /* XXX: still not too sure about this - keeping same text is confusing... */
1567 /* ----------------------------------------------------------- */
1569 static int animchannels_ungroup_exec(bContext *C, wmOperator *UNUSED(op))
1573 ListBase anim_data = {NULL, NULL};
1577 /* get editor data */
1578 if (ANIM_animdata_get_context(C, &ac) == 0)
1579 return OPERATOR_CANCELLED;
1581 /* just selected F-Curves... */
1582 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_NODUPLIS);
1583 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1585 for (ale = anim_data.first; ale; ale = ale->next) {
1586 /* find action for this F-Curve... */
1587 if (ale->adt && ale->adt->action) {
1588 FCurve *fcu = (FCurve *)ale->data;
1589 bAction *act = ale->adt->action;
1591 /* only proceed to remove if F-Curve is in a group... */
1593 bActionGroup *agrp = fcu->grp;
1595 /* remove F-Curve from group and add at tail (ungrouped) */
1596 action_groups_remove_channel(act, fcu);
1597 BLI_addtail(&act->curves, fcu);
1599 /* delete group if it is now empty */
1600 if (BLI_listbase_is_empty(&agrp->channels)) {
1601 BLI_freelinkN(&act->groups, agrp);
1608 ANIM_animdata_freelist(&anim_data);
1611 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1613 return OPERATOR_FINISHED;
1616 static void ANIM_OT_channels_ungroup(wmOperatorType *ot)
1619 ot->name = "Ungroup Channels";
1620 ot->idname = "ANIM_OT_channels_ungroup";
1621 ot->description = "Remove selected F-Curves from their current groups";
1624 ot->exec = animchannels_ungroup_exec;
1625 ot->poll = animchannels_grouping_poll;
1628 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1631 /* ******************** Delete Channel Operator *********************** */
1633 static int animchannels_delete_exec(bContext *C, wmOperator *UNUSED(op))
1636 ListBase anim_data = {NULL, NULL};
1640 /* get editor data */
1641 if (ANIM_animdata_get_context(C, &ac) == 0)
1642 return OPERATOR_CANCELLED;
1644 /* cannot delete in shapekey */
1645 if (ac.datatype == ANIMCONT_SHAPEKEY)
1646 return OPERATOR_CANCELLED;
1649 /* do groups only first (unless in Drivers mode, where there are none) */
1650 if (ac.datatype != ANIMCONT_DRIVERS) {
1652 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
1653 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1655 /* delete selected groups and their associated channels */
1656 for (ale = anim_data.first; ale; ale = ale->next) {
1657 /* only groups - don't check other types yet, since they may no-longer exist */
1658 if (ale->type == ANIMTYPE_GROUP) {
1659 bActionGroup *agrp = (bActionGroup *)ale->data;
1660 AnimData *adt = ale->adt;
1663 /* skip this group if no AnimData available, as we can't safely remove the F-Curves */
1667 /* delete all of the Group's F-Curves, but no others */
1668 for (fcu = agrp->channels.first; fcu && fcu->grp == agrp; fcu = fcn) {
1671 /* remove from group and action, then free */
1672 action_groups_remove_channel(adt->action, fcu);
1676 /* free the group itself */
1678 BLI_freelinkN(&adt->action->groups, agrp);
1679 DEG_id_tag_update_ex(CTX_data_main(C), &adt->action->id, ID_RECALC_COPY_ON_WRITE);
1687 ANIM_animdata_freelist(&anim_data);
1691 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
1692 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1694 /* delete selected data channels */
1695 for (ale = anim_data.first; ale; ale = ale->next) {
1696 switch (ale->type) {
1697 case ANIMTYPE_FCURVE:
1699 /* F-Curves if we can identify its parent */
1700 AnimData *adt = ale->adt;
1701 FCurve *fcu = (FCurve *)ale->data;
1703 /* try to free F-Curve */
1704 ANIM_fcurve_delete_from_animdata(&ac, adt, fcu);
1705 ale->update = ANIM_UPDATE_DEPS;
1708 case ANIMTYPE_NLACURVE:
1710 /* NLA Control Curve - Deleting it should disable the corresponding setting... */
1711 NlaStrip *strip = (NlaStrip *)ale->owner;
1712 FCurve *fcu = (FCurve *)ale->data;
1714 if (STREQ(fcu->rna_path, "strip_time")) {
1715 strip->flag &= ~NLASTRIP_FLAG_USR_TIME;
1717 else if (STREQ(fcu->rna_path, "influence")) {
1718 strip->flag &= ~NLASTRIP_FLAG_USR_INFLUENCE;
1721 printf("ERROR: Trying to delete NLA Control Curve for unknown property '%s'\n", fcu->rna_path);
1724 /* unlink and free the F-Curve */
1725 BLI_remlink(&strip->fcurves, fcu);
1727 ale->update = ANIM_UPDATE_DEPS;
1730 case ANIMTYPE_GPLAYER:
1732 /* Grease Pencil layer */
1733 bGPdata *gpd = (bGPdata *)ale->id;
1734 bGPDlayer *gpl = (bGPDlayer *)ale->data;
1736 /* try to delete the layer's data and the layer itself */
1737 BKE_gpencil_layer_delete(gpd, gpl);
1738 ale->update = ANIM_UPDATE_DEPS;
1741 case ANIMTYPE_MASKLAYER:
1744 Mask *mask = (Mask *)ale->id;
1745 MaskLayer *masklay = (MaskLayer *)ale->data;
1747 /* try to delete the layer's data and the layer itself */
1748 BKE_mask_layer_remove(mask, masklay);
1755 ANIM_animdata_update(&ac, &anim_data);
1756 ANIM_animdata_freelist(&anim_data);
1758 /* send notifier that things have changed */
1759 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1760 DEG_relations_tag_update(CTX_data_main(C));
1762 return OPERATOR_FINISHED;
1765 static void ANIM_OT_channels_delete(wmOperatorType *ot)
1768 ot->name = "Delete Channels";
1769 ot->idname = "ANIM_OT_channels_delete";
1770 ot->description = "Delete all selected animation channels";
1773 ot->exec = animchannels_delete_exec;
1774 ot->poll = animedit_poll_channels_active;
1777 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1780 /* ********************** Set Flags Operator *********************** */
1782 /* defines for setting animation-channel flags */
1783 static const EnumPropertyItem prop_animchannel_setflag_types[] = {
1784 {ACHANNEL_SETFLAG_TOGGLE, "TOGGLE", 0, "Toggle", ""},
1785 {ACHANNEL_SETFLAG_CLEAR, "DISABLE", 0, "Disable", ""},
1786 {ACHANNEL_SETFLAG_ADD, "ENABLE", 0, "Enable", ""},
1787 {ACHANNEL_SETFLAG_INVERT, "INVERT", 0, "Invert", ""},
1788 {0, NULL, 0, NULL, NULL}
1791 /* defines for set animation-channel settings */
1792 // TODO: could add some more types, but those are really quite dependent on the mode...
1793 static const EnumPropertyItem prop_animchannel_settings_types[] = {
1794 {ACHANNEL_SETTING_PROTECT, "PROTECT", 0, "Protect", ""},
1795 {ACHANNEL_SETTING_MUTE, "MUTE", 0, "Mute", ""},
1796 {0, NULL, 0, NULL, NULL}
1800 /* ------------------- */
1802 /* Set/clear a particular flag (setting) for all selected + visible channels
1803 * setting: the setting to modify
1804 * mode: eAnimChannels_SetFlag
1805 * onlysel: only selected channels get the flag set
1807 // TODO: enable a setting which turns flushing on/off?
1808 static void setflag_anim_channels(bAnimContext *ac, eAnimChannel_Settings setting, eAnimChannels_SetFlag mode, bool onlysel, bool flush)
1810 ListBase anim_data = {NULL, NULL};
1811 ListBase all_data = {NULL, NULL};
1815 /* filter data that we need if flush is on */
1817 /* get list of all channels that selection may need to be flushed to
1818 * - hierarchy visibility needs to be ignored so that settings can get flushed
1819 * "down" inside closed containers
1821 filter = ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS;
1822 ANIM_animdata_filter(ac, &all_data, filter, ac->data, ac->datatype);
1825 /* filter data that we're working on
1826 * - hierarchy matters if we're doing this from the channels region
1827 * since we only want to apply this to channels we can "see",
1828 * and have these affect their relatives
1829 * - but for Graph Editor, this gets used also from main region
1830 * where hierarchy doesn't apply [#21276]
1832 if ((ac->spacetype == SPACE_IPO) && (ac->regiontype != RGN_TYPE_CHANNELS)) {
1833 /* graph editor (case 2) */
1834 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
1838 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_NODUPLIS);
1840 if (onlysel) filter |= ANIMFILTER_SEL;
1841 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1843 /* if toggling, check if disable or enable */
1844 if (mode == ACHANNEL_SETFLAG_TOGGLE) {
1845 /* default to turn all on, unless we encounter one that's on... */
1846 mode = ACHANNEL_SETFLAG_ADD;
1848 /* see if we should turn off instead... */
1849 for (ale = anim_data.first; ale; ale = ale->next) {
1850 /* set the setting in the appropriate way (if available) */
1851 if (ANIM_channel_setting_get(ac, ale, setting) > 0) {
1852 mode = ACHANNEL_SETFLAG_CLEAR;
1858 /* apply the setting */
1859 for (ale = anim_data.first; ale; ale = ale->next) {
1860 /* skip channel if setting is not available */
1861 if (ANIM_channel_setting_get(ac, ale, setting) == -1)
1864 /* set the setting in the appropriate way */
1865 ANIM_channel_setting_set(ac, ale, setting, mode);
1867 /* if flush status... */
1869 ANIM_flush_setting_anim_channels(ac, &all_data, ale, setting, mode);
1872 ANIM_animdata_freelist(&anim_data);
1873 BLI_freelistN(&all_data);
1876 /* ------------------- */
1878 static int animchannels_setflag_exec(bContext *C, wmOperator *op)
1881 eAnimChannel_Settings setting;
1882 eAnimChannels_SetFlag mode;
1885 /* get editor data */
1886 if (ANIM_animdata_get_context(C, &ac) == 0)
1887 return OPERATOR_CANCELLED;
1889 /* mode (eAnimChannels_SetFlag), setting (eAnimChannel_Settings) */
1890 mode = RNA_enum_get(op->ptr, "mode");
1891 setting = RNA_enum_get(op->ptr, "type");
1893 /* check if setting is flushable */
1894 if (setting == ACHANNEL_SETTING_EXPAND)
1898 * - only selected channels are affected
1900 setflag_anim_channels(&ac, setting, mode, true, flush);
1902 /* send notifier that things have changed */
1903 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1905 return OPERATOR_FINISHED;
1908 /* duplicate of 'ANIM_OT_channels_setting_toggle' for menu title only, weak! */
1909 static void ANIM_OT_channels_setting_enable(wmOperatorType *ot)
1914 ot->name = "Enable Channel Setting";
1915 ot->idname = "ANIM_OT_channels_setting_enable";
1916 ot->description = "Enable specified setting on all selected animation channels";
1919 ot->invoke = WM_menu_invoke;
1920 ot->exec = animchannels_setflag_exec;
1921 ot->poll = animedit_poll_channels_active;
1924 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1927 /* flag-setting mode */
1928 prop = RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_ADD, "Mode", "");
1929 RNA_def_property_flag(prop, PROP_HIDDEN);
1930 /* setting to set */
1931 ot->prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
1933 /* duplicate of 'ANIM_OT_channels_setting_toggle' for menu title only, weak! */
1934 static void ANIM_OT_channels_setting_disable(wmOperatorType *ot)
1939 ot->name = "Disable Channel Setting";
1940 ot->idname = "ANIM_OT_channels_setting_disable";
1941 ot->description = "Disable specified setting on all selected animation channels";
1944 ot->invoke = WM_menu_invoke;
1945 ot->exec = animchannels_setflag_exec;
1946 ot->poll = animedit_poll_channels_active;
1949 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1952 /* flag-setting mode */
1953 prop = RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_CLEAR, "Mode", "");
1954 RNA_def_property_flag(prop, PROP_HIDDEN); /* internal hack - don't expose */
1955 /* setting to set */
1956 ot->prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
1959 static void ANIM_OT_channels_setting_toggle(wmOperatorType *ot)
1964 ot->name = "Toggle Channel Setting";
1965 ot->idname = "ANIM_OT_channels_setting_toggle";
1966 ot->description = "Toggle specified setting on all selected animation channels";
1969 ot->invoke = WM_menu_invoke;
1970 ot->exec = animchannels_setflag_exec;
1971 ot->poll = animedit_poll_channels_active;
1974 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1977 /* flag-setting mode */
1978 prop = RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_TOGGLE, "Mode", "");
1979 RNA_def_property_flag(prop, PROP_HIDDEN); /* internal hack - don't expose */
1980 /* setting to set */
1981 ot->prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
1984 static void ANIM_OT_channels_editable_toggle(wmOperatorType *ot)
1989 ot->name = "Toggle Channel Editability";
1990 ot->idname = "ANIM_OT_channels_editable_toggle";
1991 ot->description = "Toggle editability of selected channels";
1994 ot->exec = animchannels_setflag_exec;
1995 ot->poll = animedit_poll_channels_active;
1998 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2001 /* flag-setting mode */
2002 RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_TOGGLE, "Mode", "");
2003 /* setting to set */
2004 prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, ACHANNEL_SETTING_PROTECT, "Type", "");
2005 RNA_def_property_flag(prop, PROP_HIDDEN); /* internal hack - don't expose */
2008 /* ********************** Expand Channels Operator *********************** */
2010 static int animchannels_expand_exec(bContext *C, wmOperator *op)
2013 bool onlysel = true;
2015 /* get editor data */
2016 if (ANIM_animdata_get_context(C, &ac) == 0)
2017 return OPERATOR_CANCELLED;
2019 /* only affect selected channels? */
2020 if (RNA_boolean_get(op->ptr, "all"))
2023 /* modify setting */
2024 setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_ADD, onlysel, false);
2026 /* send notifier that things have changed */
2027 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
2029 return OPERATOR_FINISHED;
2032 static void ANIM_OT_channels_expand(wmOperatorType *ot)
2035 ot->name = "Expand Channels";
2036 ot->idname = "ANIM_OT_channels_expand";
2037 ot->description = "Expand (i.e. open) all selected expandable animation channels";
2040 ot->exec = animchannels_expand_exec;
2041 ot->poll = animedit_poll_channels_active;
2044 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2047 ot->prop = RNA_def_boolean(ot->srna, "all", 1, "All", "Expand all channels (not just selected ones)");
2050 /* ********************** Collapse Channels Operator *********************** */
2052 static int animchannels_collapse_exec(bContext *C, wmOperator *op)
2055 bool onlysel = true;
2057 /* get editor data */
2058 if (ANIM_animdata_get_context(C, &ac) == 0)
2059 return OPERATOR_CANCELLED;
2061 /* only affect selected channels? */
2062 if (RNA_boolean_get(op->ptr, "all"))
2065 /* modify setting */
2066 setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_CLEAR, onlysel, false);
2068 /* send notifier that things have changed */
2069 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
2071 return OPERATOR_FINISHED;
2074 static void ANIM_OT_channels_collapse(wmOperatorType *ot)
2077 ot->name = "Collapse Channels";
2078 ot->idname = "ANIM_OT_channels_collapse";
2079 ot->description = "Collapse (i.e. close) all selected expandable animation channels";
2082 ot->exec = animchannels_collapse_exec;
2083 ot->poll = animedit_poll_channels_active;
2086 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2089 ot->prop = RNA_def_boolean(ot->srna, "all", true, "All", "Collapse all channels (not just selected ones)");
2092 /* ************ Remove All "Empty" AnimData Blocks Operator ********* */
2093 /* We define "empty" AnimData blocks here as those which have all 3 of criteria:
2094 * 1) No active action OR that active actions are empty
2095 * Assuming that all legitimate entries will have an action,
2096 * and that empty actions
2097 * 2) No NLA Tracks + NLA Strips
2098 * Assuming that users haven't set up any of these as "placeholders"
2099 * for convenience sake, and that most that exist were either unintentional
2100 * or are no longer wanted
2104 static int animchannels_clean_empty_exec(bContext *C, wmOperator *UNUSED(op))
2108 ListBase anim_data = {NULL, NULL};
2112 /* get editor data */
2113 if (ANIM_animdata_get_context(C, &ac) == 0)
2114 return OPERATOR_CANCELLED;
2116 /* get animdata blocks */
2117 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA);
2118 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
2120 for (ale = anim_data.first; ale; ale = ale->next) {
2122 AnimData *adt = ale->data;
2124 bool action_empty = false;
2125 bool nla_empty = false;
2126 bool drivers_empty = false;
2129 BLI_assert((id != NULL) && (adt != NULL));
2131 /* check if this is "empty" and can be deleted */
2132 /* (For now, there are only these 3 criteria) */
2134 /* 1) Active Action is missing or empty */
2135 if (ELEM(NULL, adt->action, adt->action->curves.first)) {
2136 action_empty = true;
2139 /* TODO: check for keyframe + fmodifier data on these too */
2142 /* 2) No NLA Tracks and/or NLA Strips */
2143 if (adt->nla_tracks.first == NULL) {
2150 for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next) {
2151 if (nlt->strips.first) {
2152 /* stop searching, as we found one that actually had stuff we don't want lost
2153 * NOTE: nla_empty gets reset to false, as a previous track may have been empty
2158 else if (nlt->strips.first == NULL) {
2159 /* this track is empty, but another one may still have stuff in it, so can't break yet */
2166 drivers_empty = (adt->drivers.first == NULL);
2169 /* remove AnimData? */
2170 if (action_empty && nla_empty && drivers_empty) {
2171 BKE_animdata_free(id, true);
2175 /* free temp data */
2176 ANIM_animdata_freelist(&anim_data);
2178 /* send notifier that things have changed */
2179 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
2181 return OPERATOR_FINISHED;
2184 static void ANIM_OT_channels_clean_empty(wmOperatorType *ot)
2187 ot->name = "Remove Empty Animation Data";
2188 ot->idname = "ANIM_OT_channels_clean_empty";
2189 ot->description = "Delete all empty animation data containers from visible data-blocks";
2192 ot->exec = animchannels_clean_empty_exec;
2193 ot->poll = animedit_poll_channels_nla_tweakmode_off;
2196 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2199 /* ******************* Reenable Disabled Operator ******************* */
2201 static bool animchannels_enable_poll(bContext *C)
2203 ScrArea *sa = CTX_wm_area(C);
2205 /* channels region test */
2206 /* TODO: could enhance with actually testing if channels region? */
2207 if (ELEM(NULL, sa, CTX_wm_region(C)))
2210 /* animation editor test - Action/Dopesheet/etc. and Graph only */
2211 if (ELEM(sa->spacetype, SPACE_ACTION, SPACE_IPO) == 0)
2217 static int animchannels_enable_exec(bContext *C, wmOperator *UNUSED(op))
2221 ListBase anim_data = {NULL, NULL};
2225 /* get editor data */
2226 if (ANIM_animdata_get_context(C, &ac) == 0)
2227 return OPERATOR_CANCELLED;
2230 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_NODUPLIS);
2231 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
2233 /* loop through filtered data and clean curves */
2234 for (ale = anim_data.first; ale; ale = ale->next) {
2235 FCurve *fcu = (FCurve *)ale->data;
2237 /* remove disabled flags from F-Curves */
2238 fcu->flag &= ~FCURVE_DISABLED;
2240 /* for drivers, let's do the same too */
2242 fcu->driver->flag &= ~DRIVER_FLAG_INVALID;
2244 /* tag everything for updates - in particular, this is needed to get drivers working again */
2245 ale->update |= ANIM_UPDATE_DEPS;
2248 ANIM_animdata_update(&ac, &anim_data);
2249 ANIM_animdata_freelist(&anim_data);
2251 /* send notifier that things have changed */
2252 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
2254 return OPERATOR_FINISHED;
2257 static void ANIM_OT_channels_fcurves_enable(wmOperatorType *ot)
2260 ot->name = "Revive Disabled F-Curves";
2261 ot->idname = "ANIM_OT_channels_fcurves_enable";
2262 ot->description = "Clears 'disabled' tag from all F-Curves to get broken F-Curves working again";
2265 ot->exec = animchannels_enable_exec;
2266 ot->poll = animchannels_enable_poll;
2269 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2272 /* ****************** Find / Set Filter Operator ******************** */
2274 /* XXX: make this generic? */
2275 static bool animchannels_find_poll(bContext *C)
2277 ScrArea *sa = CTX_wm_area(C);
2282 /* animation editor with dopesheet */
2283 return ELEM(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA);
2286 /* find_invoke() - Get initial channels */
2287 static int animchannels_find_invoke(bContext *C, wmOperator *op, const wmEvent *evt)
2291 /* get editor data */
2292 if (ANIM_animdata_get_context(C, &ac) == 0)
2293 return OPERATOR_CANCELLED;
2295 /* set initial filter text, and enable filter */
2296 RNA_string_set(op->ptr, "query", ac.ads->searchstr);
2298 /* defer to popup */
2299 return WM_operator_props_popup(C, op, evt);
2302 /* find_exec() - Called to set the value */
2303 static int animchannels_find_exec(bContext *C, wmOperator *op)
2307 /* get editor data */
2308 if (ANIM_animdata_get_context(C, &ac) == 0)
2309 return OPERATOR_CANCELLED;
2311 /* update filter text */
2312 RNA_string_get(op->ptr, "query", ac.ads->searchstr);
2315 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
2317 return OPERATOR_FINISHED;
2320 static void ANIM_OT_channels_find(wmOperatorType *ot)
2323 ot->name = "Find Channels";
2324 ot->idname = "ANIM_OT_channels_find";
2325 ot->description = "Filter the set of channels shown to only include those with matching names";
2328 ot->invoke = animchannels_find_invoke;
2329 ot->exec = animchannels_find_exec;
2330 ot->poll = animchannels_find_poll;
2333 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2336 ot->prop = RNA_def_string(ot->srna, "query", "Query", sizeof(((bDopeSheet *)NULL)->searchstr), "", "Text to search for in channel names");
2339 /* ********************** Select All Operator *********************** */
2341 static int animchannels_deselectall_exec(bContext *C, wmOperator *op)
2345 /* get editor data */
2346 if (ANIM_animdata_get_context(C, &ac) == 0)
2347 return OPERATOR_CANCELLED;
2349 /* 'standard' behavior - check if selected, then apply relevant selection */
2350 const int action = RNA_enum_get(op->ptr, "action");
2353 ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, true, ACHANNEL_SETFLAG_ADD);
2356 ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, false, ACHANNEL_SETFLAG_ADD);
2359 ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, false, ACHANNEL_SETFLAG_CLEAR);
2362 ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, false, ACHANNEL_SETFLAG_INVERT);
2369 /* send notifier that things have changed */
2370 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL);
2372 return OPERATOR_FINISHED;
2375 static void ANIM_OT_channels_select_all(wmOperatorType *ot)
2378 ot->name = "Select All";
2379 ot->idname = "ANIM_OT_channels_select_all";
2380 ot->description = "Toggle selection of all animation channels";
2383 ot->exec = animchannels_deselectall_exec;
2384 ot->poll = animedit_poll_channels_nla_tweakmode_off;
2387 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2390 WM_operator_properties_select_all(ot);
2393 /* ******************** Box Select Operator *********************** */
2395 static void box_select_anim_channels(bAnimContext *ac, rcti *rect, short selectmode)
2397 ListBase anim_data = {NULL, NULL};
2401 SpaceNla *snla = (SpaceNla *)ac->sl;
2402 View2D *v2d = &ac->ar->v2d;
2406 /* set initial y extents */
2407 if (ac->datatype == ANIMCONT_NLA) {
2408 ymin = (float)(-NLACHANNEL_HEIGHT(snla));
2413 ymax = (float)(-ACHANNEL_HEIGHT(ac));
2416 /* convert border-region to view coordinates */
2417 UI_view2d_region_to_view(v2d, rect->xmin, rect->ymin + 2, &rectf.xmin, &rectf.ymin);
2418 UI_view2d_region_to_view(v2d, rect->xmax, rect->ymax - 2, &rectf.xmax, &rectf.ymax);
2421 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
2422 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
2424 /* loop over data, doing box select */
2425 for (ale = anim_data.first; ale; ale = ale->next) {
2426 if (ac->datatype == ANIMCONT_NLA)
2427 ymin = ymax - NLACHANNEL_STEP(snla);
2429 ymin = ymax - ACHANNEL_STEP(ac);
2431 /* if channel is within border-select region, alter it */
2432 if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))) {
2433 /* set selection flags only */
2434 ANIM_channel_setting_set(ac, ale, ACHANNEL_SETTING_SELECT, selectmode);
2436 /* type specific actions */
2437 switch (ale->type) {
2438 case ANIMTYPE_GROUP:
2440 bActionGroup *agrp = (bActionGroup *)ale->data;
2441 select_pchan_for_action_group(ac, agrp, ale);
2442 /* always clear active flag after doing this */
2443 agrp->flag &= ~AGRP_ACTIVE;
2446 case ANIMTYPE_NLATRACK:
2448 NlaTrack *nlt = (NlaTrack *)ale->data;
2450 /* for now, it's easier just to do this here manually, as defining a new type
2451 * currently adds complications when doing other stuff
2453 ACHANNEL_SET_FLAG(nlt, selectmode, NLATRACK_SELECTED);
2459 /* set minimum extent to be the maximum of the next channel */
2464 ANIM_animdata_freelist(&anim_data);
2467 /* ------------------- */
2469 static int animchannels_box_select_exec(bContext *C, wmOperator *op)
2473 short selectmode = 0;
2474 const bool select = !RNA_boolean_get(op->ptr, "deselect");
2475 const bool extend = RNA_boolean_get(op->ptr, "extend");
2477 /* get editor data */
2478 if (ANIM_animdata_get_context(C, &ac) == 0)
2479 return OPERATOR_CANCELLED;
2481 /* get settings from operator */
2482 WM_operator_properties_border_to_rcti(op, &rect);
2485 ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, true, ACHANNEL_SETFLAG_CLEAR);
2489 selectmode = ACHANNEL_SETFLAG_ADD;
2492 selectmode = ACHANNEL_SETFLAG_CLEAR;
2495 /* apply box_select animation channels */
2496 box_select_anim_channels(&ac, &rect, selectmode);
2498 /* send notifier that things have changed */
2499 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL);
2501 return OPERATOR_FINISHED;
2504 static void ANIM_OT_channels_select_box(wmOperatorType *ot)
2507 ot->name = "Box Select";
2508 ot->idname = "ANIM_OT_channels_select_box";
2509 ot->description = "Select all animation channels within the specified region";
2512 ot->invoke = WM_gesture_box_invoke;
2513 ot->exec = animchannels_box_select_exec;
2514 ot->modal = WM_gesture_box_modal;
2515 ot->cancel = WM_gesture_box_cancel;
2517 ot->poll = animedit_poll_channels_nla_tweakmode_off;
2520 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2523 WM_operator_properties_gesture_box_select(ot);
2526 /* ******************* Rename Operator ***************************** */
2527 /* Allow renaming some channels by clicking on them */
2529 static bool rename_anim_channels(bAnimContext *ac, int channel_index)
2531 ListBase anim_data = {NULL, NULL};
2532 const bAnimChannelType *acf;
2535 bool success = false;
2537 /* get the channel that was clicked on */
2538 /* filter channels */
2539 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
2540 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
2542 /* get channel from index */
2543 ale = BLI_findlink(&anim_data, channel_index);
2545 /* channel not found */
2546 if (G.debug & G_DEBUG)
2547 printf("Error: animation channel (index = %d) not found in rename_anim_channels()\n", channel_index);
2549 ANIM_animdata_freelist(&anim_data);
2553 /* check that channel can be renamed */
2554 acf = ANIM_channel_get_typeinfo(ale);
2555 if (acf && acf->name_prop) {
2559 /* ok if we can get name property to edit from this channel */
2560 if (acf->name_prop(ale, &ptr, &prop)) {
2561 /* actually showing the rename textfield is done on redraw,
2562 * so here we just store the index of this channel in the
2563 * dopesheet data, which will get utilized when drawing the
2566 * +1 factor is for backwards compat issues
2569 ac->ads->renameIndex = channel_index + 1;
2575 /* free temp data and tag for refresh */
2576 ANIM_animdata_freelist(&anim_data);
2577 ED_region_tag_redraw(ac->ar);
2581 static int animchannels_channel_get(bAnimContext *ac, const int mval[2])
2588 /* get useful pointers from animation context data */
2592 /* figure out which channel user clicked in
2593 * Note: although channels technically start at (y = ACHANNEL_FIRST), we need to adjust by half a channel's height
2594 * so that the tops of channels get caught ok. Since ACHANNEL_FIRST is really ACHANNEL_HEIGHT, we simply use
2595 * ACHANNEL_HEIGHT_HALF.
2597 UI_view2d_region_to_view(v2d, mval[0], mval[1], &x, &y);
2599 if (ac->datatype == ANIMCONT_NLA) {
2600 SpaceNla *snla = (SpaceNla *)ac->sl;
2601 UI_view2d_listview_view_to_cell(v2d, NLACHANNEL_NAMEWIDTH, NLACHANNEL_STEP(snla), 0, (float)NLACHANNEL_HEIGHT_HALF(snla), x, y, NULL, &channel_index);
2604 UI_view2d_listview_view_to_cell(v2d, ACHANNEL_NAMEWIDTH, ACHANNEL_STEP(ac), 0, (float)ACHANNEL_HEIGHT_HALF(ac), x, y, NULL, &channel_index);
2607 return channel_index;
2610 static int animchannels_rename_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
2615 /* get editor data */
2616 if (ANIM_animdata_get_context(C, &ac) == 0)
2617 return OPERATOR_CANCELLED;
2619 channel_index = animchannels_channel_get(&ac, event->mval);
2622 if (rename_anim_channels(&ac, channel_index))
2623 return OPERATOR_FINISHED;
2625 /* allow event to be handled by selectall operator */
2626 return OPERATOR_PASS_THROUGH;
2629 static void ANIM_OT_channels_rename(wmOperatorType *ot)
2632 ot->name = "Rename Channels";
2633 ot->idname = "ANIM_OT_channels_rename";
2634 ot->description = "Rename animation channel under mouse";
2637 ot->invoke = animchannels_rename_invoke;
2638 ot->poll = animedit_poll_channels_active;
2641 /* ******************** Mouse-Click Operator *********************** */
2642 /* Handle selection changes due to clicking on channels. Settings will get caught by UI code... */
2644 static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index, short selectmode)
2646 ListBase anim_data = {NULL, NULL};
2649 int notifierFlags = 0;
2651 /* get the channel that was clicked on */
2652 /* filter channels */
2653 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
2654 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
2656 /* get channel from index */
2657 ale = BLI_findlink(&anim_data, channel_index);
2659 /* channel not found */
2660 if (G.debug & G_DEBUG)
2661 printf("Error: animation channel (index = %d) not found in mouse_anim_channels()\n", channel_index);
2663 ANIM_animdata_freelist(&anim_data);
2667 /* selectmode -1 is a special case for ActionGroups only, which selects all of the channels underneath it only... */
2668 /* TODO: should this feature be extended to work with other channel types too? */
2669 if ((selectmode == -1) && (ale->type != ANIMTYPE_GROUP)) {
2670 /* normal channels should not behave normally in this case */
2671 ANIM_animdata_freelist(&anim_data);
2675 /* action to take depends on what channel we've got */
2676 /* WARNING: must keep this in sync with the equivalent function in nla_channels.c */
2677 switch (ale->type) {
2678 case ANIMTYPE_SCENE:
2680 Scene *sce = (Scene *)ale->data;
2681 AnimData *adt = sce->adt;
2683 /* set selection status */
2684 if (selectmode == SELECT_INVERT) {
2686 sce->flag ^= SCE_DS_SELECTED;
2687 if (adt) adt->flag ^= ADT_UI_SELECTED;
2690 sce->flag |= SCE_DS_SELECTED;
2691 if (adt) adt->flag |= ADT_UI_SELECTED;
2694 notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2697 case ANIMTYPE_OBJECT:
2700 bDopeSheet *ads = (bDopeSheet *)ac->data;
2701 Scene *sce = (Scene *)ads->source;
2703 ViewLayer *view_layer = ac->view_layer;
2704 Base *base = (Base *)ale->data;
2705 Object *ob = base->object;
2706 AnimData *adt = ob->adt;
2708 /* set selection status */
2709 if (base->flag & BASE_SELECTABLE) {
2710 if (selectmode == SELECT_INVERT) {
2712 ED_object_base_select(base, BA_INVERT);
2713 BKE_scene_object_base_flag_sync_from_base(base);
2715 if (adt) adt->flag ^= ADT_UI_SELECTED;
2721 /* TODO: should this deselect all other types of channels too? */
2722 for (b = view_layer->object_bases.first; b; b = b->next) {
2723 ED_object_base_select(b, BA_DESELECT);
2724 BKE_scene_object_base_flag_sync_from_base(b);
2725 if (b->object->adt) b->object->adt->flag &= ~(ADT_UI_SELECTED | ADT_UI_ACTIVE);
2728 /* select object now */
2729 ED_object_base_select(base, BA_SELECT);
2730 BKE_scene_object_base_flag_sync_from_base(base);
2731 if (adt) adt->flag |= ADT_UI_SELECTED;
2734 /* change active object - regardless of whether it is now selected [T37883] */
2735 ED_object_base_activate(C, base); /* adds notifier */
2737 if ((adt) && (adt->flag & ADT_UI_SELECTED))
2738 adt->flag |= ADT_UI_ACTIVE;
2740 /* ensure we exit editmode on whatever object was active before to avoid getting stuck there - T48747 */
2741 if (ob != CTX_data_edit_object(C)) {
2742 ED_object_editmode_exit(C, EM_FREEDATA | EM_WAITCURSOR);
2745 notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2749 case ANIMTYPE_FILLACTD: /* Action Expander */
2750 case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
2751 case ANIMTYPE_DSLAM:
2752 case ANIMTYPE_DSCAM:
2753 case ANIMTYPE_DSCACHEFILE:
2754 case ANIMTYPE_DSCUR:
2755 case ANIMTYPE_DSSKEY:
2756 case ANIMTYPE_DSWOR:
2757 case ANIMTYPE_DSPART:
2758 case ANIMTYPE_DSMBALL:
2759 case ANIMTYPE_DSARM:
2760 case ANIMTYPE_DSMESH:
2761 case ANIMTYPE_DSNTREE:
2762 case ANIMTYPE_DSTEX:
2763 case ANIMTYPE_DSLAT:
2764 case ANIMTYPE_DSLINESTYLE:
2765 case ANIMTYPE_DSSPK:
2766 case ANIMTYPE_DSGPENCIL:
2767 case ANIMTYPE_DSMCLIP:
2769 /* sanity checking... */
2771 /* select/deselect */
2772 if (selectmode == SELECT_INVERT) {
2773 /* inverse selection status of this AnimData block only */
2774 ale->adt->flag ^= ADT_UI_SELECTED;
2777 /* select AnimData block by itself */
2778 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
2779 ale->adt->flag |= ADT_UI_SELECTED;
2783 if ((ale->adt) && (ale->adt->flag & ADT_UI_SELECTED))
2784 ale->adt->flag |= ADT_UI_ACTIVE;
2787 notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2790 case ANIMTYPE_GROUP:
2792 bActionGroup *agrp = (bActionGroup *)ale->data;
2795 bPoseChannel *pchan = NULL;
2798 /* Armatures-Specific Feature:
2799 * Since groups are used to collect F-Curves of the same Bone by default
2800 * (via Keying Sets) so that they can be managed better, we try to make
2801 * things here easier for animators by mapping group selection to bone
2804 * Only do this if "Only Selected" dopesheet filter is not active, or else it
2805 * becomes too unpredictable/tricky to manage
2807 if ((ac->ads->filterflag & ADS_FILTER_ONLYSEL) == 0) {
2808 if ((ale->id) && (GS(ale->id->name) == ID_OB)) {
2809 ob = (Object *)ale->id;
2811 if (ob->type == OB_ARMATURE) {
2812 /* Assume for now that any group with corresponding name is what we want
2813 * (i.e. for an armature whose location is animated, things would break
2814 * if the user were to add a bone named "Location").
2816 * TODO: check the first F-Curve or so to be sure...
2818 pchan = BKE_pose_channel_find_name(ob->pose, agrp->name);
2823 /* select/deselect group */
2824 if (selectmode == SELECT_INVERT) {
2825 /* inverse selection status of this group only */
2826 agrp->flag ^= AGRP_SELECTED;
2828 else if (selectmode == -1) {
2829 /* select all in group (and deselect everything else) */
2832 /* deselect all other channels */
2833 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
2834 if (pchan) ED_pose_deselect_all(ob, SEL_DESELECT, false);
2836 /* only select channels in group and group itself */
2837 for (fcu = agrp->channels.first; fcu && fcu->grp == agrp; fcu = fcu->next)
2838 fcu->flag |= FCURVE_SELECTED;
2839 agrp->flag |= AGRP_SELECTED;
2842 /* select group by itself */
2843 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
2844 if (pchan) ED_pose_deselect_all(ob, SEL_DESELECT, false);
2846 agrp->flag |= AGRP_SELECTED;
2849 /* if group is selected now, make group the 'active' one in the visible list */
2850 if (agrp->flag & AGRP_SELECTED) {
2851 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, agrp, ANIMTYPE_GROUP);
2852 if (pchan) ED_pose_bone_select(ob, pchan, true);
2855 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, NULL, ANIMTYPE_GROUP);
2856 if (pchan) ED_pose_bone_select(ob, pchan, false);
2859 notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2862 case ANIMTYPE_FCURVE:
2863 case ANIMTYPE_NLACURVE:
2865 FCurve *fcu = (FCurve *)ale->data;
2867 /* select/deselect */
2868 if (selectmode == SELECT_INVERT) {
2869 /* inverse selection status of this F-Curve only */
2870 fcu->flag ^= FCURVE_SELECTED;
2873 /* select F-Curve by itself */
2874 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
2875 fcu->flag |= FCURVE_SELECTED;
2878 /* if F-Curve is selected now, make F-Curve the 'active' one in the visible list */
2879 if (fcu->flag & FCURVE_SELECTED)
2880 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, fcu, ale->type);
2882 notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2885 case ANIMTYPE_SHAPEKEY:
2887 KeyBlock *kb = (KeyBlock *)ale->data;
2889 /* select/deselect */
2890 if (selectmode == SELECT_INVERT) {
2891 /* inverse selection status of this ShapeKey only */
2892 kb->flag ^= KEYBLOCK_SEL;
2895 /* select ShapeKey by itself */
2896 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
2897 kb->flag |= KEYBLOCK_SEL;
2900 notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2903 case ANIMTYPE_NLACONTROLS:
2905 AnimData *adt = (AnimData *)ale->data;
2908 * - Although the triangle widget already allows this, since there's nothing else that can be done here now,
2909 * let's just use it for easier expand/collapse for now
2911 adt->flag ^= ADT_NLA_SKEYS_COLLAPSED;
2913 notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
2916 case ANIMTYPE_GPDATABLOCK:
2918 bGPdata *gpd = (bGPdata *)ale->data;
2921 * - although the triangle widget already allows this, the whole channel can also be used for this purpose
2923 gpd->flag ^= GP_DATA_EXPAND;
2925 notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
2928 case ANIMTYPE_GPLAYER:
2930 bGPdata *gpd = (bGPdata *)ale->id;
2931 bGPDlayer *gpl = (bGPDlayer *)ale->data;
2933 /* select/deselect */
2934 if (selectmode == SELECT_INVERT) {
2935 /* invert selection status of this layer only */
2936 gpl->flag ^= GP_LAYER_SELECT;
2939 /* select layer by itself */
2940 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
2941 gpl->flag |= GP_LAYER_SELECT;
2944 /* change active layer, if this is selected (since we must always have an active layer) */
2945 if (gpl->flag & GP_LAYER_SELECT) {
2946 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, gpl, ANIMTYPE_GPLAYER);
2947 /* update other layer status */
2948 BKE_gpencil_layer_setactive(gpd, gpl);
2951 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); /* Grease Pencil updates */
2952 notifierFlags |= (ND_ANIMCHAN | NA_EDITED); /* Animation Editors updates */
2955 case ANIMTYPE_MASKDATABLOCK:
2957 Mask *mask = (Mask *)ale->data;
2960 * - although the triangle widget already allows this, the whole channel can also be used for this purpose
2962 mask->flag ^= MASK_ANIMF_EXPAND;
2964 notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
2967 case ANIMTYPE_MASKLAYER:
2969 MaskLayer *masklay = (MaskLayer *)ale->data;
2971 /* select/deselect */
2972 if (selectmode == SELECT_INVERT) {
2973 /* invert selection status of this layer only */
2974 masklay->flag ^= MASK_LAYERFLAG_SELECT;
2977 /* select layer by itself */
2978 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
2979 masklay->flag |= MASK_LAYERFLAG_SELECT;
2982 notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
2986 if (G.debug & G_DEBUG)
2987 printf("Error: Invalid channel type in mouse_anim_channels()\n");