1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 *
16 * The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung
17 * All rights reserved.
18 */
19
20 /** \file
21 * \ingroup edanimation
22 */
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include "MEM_guardedalloc.h"
29
30 #include "BLI_blenlib.h"
31 #include "BLI_listbase.h"
32 #include "BLI_utildefines.h"
33
34 #include "DNA_anim_types.h"
35 #include "DNA_gpencil_types.h"
36 #include "DNA_key_types.h"
37 #include "DNA_mask_types.h"
38 #include "DNA_object_types.h"
39 #include "DNA_scene_types.h"
40
41 #include "RNA_access.h"
42 #include "RNA_define.h"
43
44 #include "BKE_action.h"
45 #include "BKE_anim_data.h"
46 #include "BKE_context.h"
47 #include "BKE_fcurve.h"
48 #include "BKE_global.h"
49 #include "BKE_gpencil.h"
50 #include "BKE_lib_id.h"
51 #include "BKE_mask.h"
52 #include "BKE_scene.h"
53
54 #include "DEG_depsgraph.h"
55 #include "DEG_depsgraph_build.h"
56
57 #include "UI_interface.h"
58 #include "UI_view2d.h"
59
60 #include "ED_anim_api.h"
61 #include "ED_armature.h"
62 #include "ED_keyframes_edit.h" /* XXX move the select modes out of there! */
63 #include "ED_object.h"
64 #include "ED_screen.h"
65 #include "ED_select_utils.h"
66
67 #include "WM_api.h"
68 #include "WM_types.h"
69
70 /* ************************************************************************** */
71 /* CHANNELS API - Exposed API */
72
73 /* -------------------------- Selection ------------------------------------- */
74
75 /* Set the given animation-channel as the active one for the active context */
76 /* TODO: extend for animdata types... */
ANIM_set_active_channel(bAnimContext * ac,void * data,eAnimCont_Types datatype,eAnimFilter_Flags filter,void * channel_data,eAnim_ChannelType channel_type)77 void ANIM_set_active_channel(bAnimContext *ac,
78 void *data,
79 eAnimCont_Types datatype,
80 eAnimFilter_Flags filter,
81 void *channel_data,
82 eAnim_ChannelType channel_type)
83 {
84 ListBase anim_data = {NULL, NULL};
85 bAnimListElem *ale;
86
87 /* try to build list of filtered items */
88 ANIM_animdata_filter(ac, &anim_data, filter, data, datatype);
89 if (BLI_listbase_is_empty(&anim_data)) {
90 return;
91 }
92
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) {
97 continue;
98 }
99
100 /* flag to set depends on type */
101 switch (ale->type) {
102 case ANIMTYPE_GROUP: {
103 bActionGroup *agrp = (bActionGroup *)ale->data;
104
105 ACHANNEL_SET_FLAG(agrp, ACHANNEL_SETFLAG_CLEAR, AGRP_ACTIVE);
106 break;
107 }
108 case ANIMTYPE_FCURVE:
109 case ANIMTYPE_NLACURVE: {
110 FCurve *fcu = (FCurve *)ale->data;
111
112 ACHANNEL_SET_FLAG(fcu, ACHANNEL_SETFLAG_CLEAR, FCURVE_ACTIVE);
113 break;
114 }
115 case ANIMTYPE_NLATRACK: {
116 NlaTrack *nlt = (NlaTrack *)ale->data;
117
118 ACHANNEL_SET_FLAG(nlt, ACHANNEL_SETFLAG_CLEAR, NLATRACK_ACTIVE);
119 break;
120 }
121 case ANIMTYPE_FILLACTD: /* Action Expander */
122 case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
123 case ANIMTYPE_DSLAM:
124 case ANIMTYPE_DSCAM:
125 case ANIMTYPE_DSCACHEFILE:
126 case ANIMTYPE_DSCUR:
127 case ANIMTYPE_DSSKEY:
128 case ANIMTYPE_DSWOR:
129 case ANIMTYPE_DSPART:
130 case ANIMTYPE_DSMBALL:
131 case ANIMTYPE_DSARM:
132 case ANIMTYPE_DSMESH:
133 case ANIMTYPE_DSTEX:
134 case ANIMTYPE_DSLAT:
135 case ANIMTYPE_DSLINESTYLE:
136 case ANIMTYPE_DSSPK:
137 case ANIMTYPE_DSGPENCIL:
138 case ANIMTYPE_DSMCLIP:
139 case ANIMTYPE_DSHAIR:
140 case ANIMTYPE_DSPOINTCLOUD:
141 case ANIMTYPE_DSVOLUME:
142 case ANIMTYPE_DSSIMULATION: {
143 /* need to verify that this data is valid for now */
144 if (ale->adt) {
145 ACHANNEL_SET_FLAG(ale->adt, ACHANNEL_SETFLAG_CLEAR, ADT_UI_ACTIVE);
146 }
147 break;
148 }
149 case ANIMTYPE_GPLAYER: {
150 bGPDlayer *gpl = (bGPDlayer *)ale->data;
151
152 ACHANNEL_SET_FLAG(gpl, ACHANNEL_SETFLAG_CLEAR, GP_LAYER_ACTIVE);
153 break;
154 }
155 }
156 }
157
158 /* set active flag */
159 if (channel_data) {
160 switch (channel_type) {
161 case ANIMTYPE_GROUP: {
162 bActionGroup *agrp = (bActionGroup *)channel_data;
163 agrp->flag |= AGRP_ACTIVE;
164 break;
165 }
166 case ANIMTYPE_FCURVE:
167 case ANIMTYPE_NLACURVE: {
168 FCurve *fcu = (FCurve *)channel_data;
169 fcu->flag |= FCURVE_ACTIVE;
170 break;
171 }
172 case ANIMTYPE_NLATRACK: {
173 NlaTrack *nlt = (NlaTrack *)channel_data;
174 nlt->flag |= NLATRACK_ACTIVE;
175 break;
176 }
177 case ANIMTYPE_FILLACTD: /* Action Expander */
178 case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
179 case ANIMTYPE_DSLAM:
180 case ANIMTYPE_DSCAM:
181 case ANIMTYPE_DSCACHEFILE:
182 case ANIMTYPE_DSCUR:
183 case ANIMTYPE_DSSKEY:
184 case ANIMTYPE_DSWOR:
185 case ANIMTYPE_DSPART:
186 case ANIMTYPE_DSMBALL:
187 case ANIMTYPE_DSARM:
188 case ANIMTYPE_DSMESH:
189 case ANIMTYPE_DSLAT:
190 case ANIMTYPE_DSLINESTYLE:
191 case ANIMTYPE_DSSPK:
192 case ANIMTYPE_DSNTREE:
193 case ANIMTYPE_DSTEX:
194 case ANIMTYPE_DSGPENCIL:
195 case ANIMTYPE_DSMCLIP:
196 case ANIMTYPE_DSHAIR:
197 case ANIMTYPE_DSPOINTCLOUD:
198 case ANIMTYPE_DSVOLUME:
199 case ANIMTYPE_DSSIMULATION: {
200 /* need to verify that this data is valid for now */
201 if (ale && ale->adt) {
202 ale->adt->flag |= ADT_UI_ACTIVE;
203 }
204 break;
205 }
206
207 case ANIMTYPE_GPLAYER: {
208 bGPDlayer *gpl = (bGPDlayer *)channel_data;
209 gpl->flag |= GP_LAYER_ACTIVE;
210 break;
211 }
212
213 /* unhandled currently, but may be interesting */
214 case ANIMTYPE_MASKLAYER:
215 case ANIMTYPE_SHAPEKEY:
216 case ANIMTYPE_NLAACTION:
217 break;
218
219 /* other types */
220 default:
221 break;
222 }
223 }
224
225 /* clean up */
226 ANIM_animdata_freelist(&anim_data);
227 }
228
select_pchan_for_action_group(bAnimContext * ac,bActionGroup * agrp,bAnimListElem * ale)229 static void select_pchan_for_action_group(bAnimContext *ac, bActionGroup *agrp, bAnimListElem *ale)
230 {
231 /* Armatures-Specific Feature:
232 * See mouse_anim_channels() -> ANIMTYPE_GROUP case for more details (T38737)
233 */
234 if ((ac->ads->filterflag & ADS_FILTER_ONLYSEL) == 0) {
235 if ((ale->id) && (GS(ale->id->name) == ID_OB)) {
236 Object *ob = (Object *)ale->id;
237 if (ob->type == OB_ARMATURE) {
238 /* Assume for now that any group with corresponding name is what we want
239 * (i.e. for an armature whose location is animated, things would break
240 * if the user were to add a bone named "Location").
241 *
242 * TODO: check the first F-Curve or so to be sure...
243 */
244 bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, agrp->name);
245 if (agrp->flag & AGRP_SELECTED) {
246 ED_pose_bone_select(ob, pchan, true);
247 }
248 else {
249 ED_pose_bone_select(ob, pchan, false);
250 }
251 }
252 }
253 }
254 }
255
anim_channels_for_selection(bAnimContext * ac)256 static ListBase /* bAnimListElem */ anim_channels_for_selection(bAnimContext *ac)
257 {
258 ListBase anim_data = {NULL, NULL};
259
260 /* filter data */
261 /* NOTE: no list visible, otherwise, we get dangling */
262 const int filter = ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS;
263 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
264
265 return anim_data;
266 }
267
anim_channels_selection_flag_for_toggle(const ListBase anim_data)268 static eAnimChannels_SetFlag anim_channels_selection_flag_for_toggle(const ListBase anim_data)
269 {
270 /* See if we should be selecting or deselecting. */
271 for (bAnimListElem *ale = anim_data.first; ale; ale = ale->next) {
272 switch (ale->type) {
273 case ANIMTYPE_SCENE:
274 if (ale->flag & SCE_DS_SELECTED) {
275 return ACHANNEL_SETFLAG_CLEAR;
276 }
277 break;
278 case ANIMTYPE_OBJECT:
279 #if 0 /* for now, do not take object selection into account, since it gets too annoying */
280 if (ale->flag & SELECT) {
281 return ACHANNEL_SETFLAG_CLEAR;
282 }
283 #endif
284 break;
285 case ANIMTYPE_GROUP:
286 if (ale->flag & AGRP_SELECTED) {
287 return ACHANNEL_SETFLAG_CLEAR;
288 }
289 break;
290 case ANIMTYPE_FCURVE:
291 case ANIMTYPE_NLACURVE:
292 if (ale->flag & FCURVE_SELECTED) {
293 return ACHANNEL_SETFLAG_CLEAR;
294 }
295 break;
296 case ANIMTYPE_SHAPEKEY:
297 if (ale->flag & KEYBLOCK_SEL) {
298 return ACHANNEL_SETFLAG_CLEAR;
299 }
300 break;
301 case ANIMTYPE_NLATRACK:
302 if (ale->flag & NLATRACK_SELECTED) {
303 return ACHANNEL_SETFLAG_CLEAR;
304 }
305 break;
306
307 case ANIMTYPE_FILLACTD: /* Action Expander */
308 case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
309 case ANIMTYPE_DSLAM:
310 case ANIMTYPE_DSCAM:
311 case ANIMTYPE_DSCACHEFILE:
312 case ANIMTYPE_DSCUR:
313 case ANIMTYPE_DSSKEY:
314 case ANIMTYPE_DSWOR:
315 case ANIMTYPE_DSPART:
316 case ANIMTYPE_DSMBALL:
317 case ANIMTYPE_DSARM:
318 case ANIMTYPE_DSMESH:
319 case ANIMTYPE_DSNTREE:
320 case ANIMTYPE_DSTEX:
321 case ANIMTYPE_DSLAT:
322 case ANIMTYPE_DSLINESTYLE:
323 case ANIMTYPE_DSSPK:
324 case ANIMTYPE_DSGPENCIL:
325 case ANIMTYPE_DSMCLIP:
326 case ANIMTYPE_DSHAIR:
327 case ANIMTYPE_DSPOINTCLOUD:
328 case ANIMTYPE_DSVOLUME:
329 case ANIMTYPE_DSSIMULATION: {
330 if ((ale->adt) && (ale->adt->flag & ADT_UI_SELECTED)) {
331 return ACHANNEL_SETFLAG_CLEAR;
332 }
333 break;
334 }
335 case ANIMTYPE_GPLAYER:
336 if (ale->flag & GP_LAYER_SELECT) {
337 return ACHANNEL_SETFLAG_CLEAR;
338 }
339 break;
340 case ANIMTYPE_MASKLAYER:
341 if (ale->flag & MASK_LAYERFLAG_SELECT) {
342 return ACHANNEL_SETFLAG_CLEAR;
343 }
344 break;
345 }
346 }
347
348 return ACHANNEL_SETFLAG_ADD;
349 }
350
anim_channels_select_set(bAnimContext * ac,const ListBase anim_data,eAnimChannels_SetFlag sel)351 static void anim_channels_select_set(bAnimContext *ac,
352 const ListBase anim_data,
353 eAnimChannels_SetFlag sel)
354 {
355 bAnimListElem *ale;
356
357 for (ale = anim_data.first; ale; ale = ale->next) {
358 switch (ale->type) {
359 case ANIMTYPE_SCENE: {
360 Scene *scene = (Scene *)ale->data;
361
362 ACHANNEL_SET_FLAG(scene, sel, SCE_DS_SELECTED);
363
364 if (scene->adt) {
365 ACHANNEL_SET_FLAG(scene, sel, ADT_UI_SELECTED);
366 }
367 break;
368 }
369 case ANIMTYPE_OBJECT: {
370 #if 0 /* for now, do not take object selection into account, since it gets too annoying */
371 Base *base = (Base *)ale->data;
372 Object *ob = base->object;
373
374 ACHANNEL_SET_FLAG(base, sel, SELECT);
375 ACHANNEL_SET_FLAG(ob, sel, SELECT);
376
377 if (ob->adt) {
378 ACHANNEL_SET_FLAG(ob, sel, ADT_UI_SELECTED);
379 }
380 #endif
381 break;
382 }
383 case ANIMTYPE_GROUP: {
384 bActionGroup *agrp = (bActionGroup *)ale->data;
385 ACHANNEL_SET_FLAG(agrp, sel, AGRP_SELECTED);
386 select_pchan_for_action_group(ac, agrp, ale);
387 agrp->flag &= ~AGRP_ACTIVE;
388 break;
389 }
390 case ANIMTYPE_FCURVE:
391 case ANIMTYPE_NLACURVE: {
392 FCurve *fcu = (FCurve *)ale->data;
393
394 ACHANNEL_SET_FLAG(fcu, sel, FCURVE_SELECTED);
395 fcu->flag &= ~FCURVE_ACTIVE;
396 break;
397 }
398 case ANIMTYPE_SHAPEKEY: {
399 KeyBlock *kb = (KeyBlock *)ale->data;
400
401 ACHANNEL_SET_FLAG(kb, sel, KEYBLOCK_SEL);
402 break;
403 }
404 case ANIMTYPE_NLATRACK: {
405 NlaTrack *nlt = (NlaTrack *)ale->data;
406
407 ACHANNEL_SET_FLAG(nlt, sel, NLATRACK_SELECTED);
408 nlt->flag &= ~NLATRACK_ACTIVE;
409 break;
410 }
411 case ANIMTYPE_FILLACTD: /* Action Expander */
412 case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
413 case ANIMTYPE_DSLAM:
414 case ANIMTYPE_DSCAM:
415 case ANIMTYPE_DSCACHEFILE:
416 case ANIMTYPE_DSCUR:
417 case ANIMTYPE_DSSKEY:
418 case ANIMTYPE_DSWOR:
419 case ANIMTYPE_DSPART:
420 case ANIMTYPE_DSMBALL:
421 case ANIMTYPE_DSARM:
422 case ANIMTYPE_DSMESH:
423 case ANIMTYPE_DSNTREE:
424 case ANIMTYPE_DSTEX:
425 case ANIMTYPE_DSLAT:
426 case ANIMTYPE_DSLINESTYLE:
427 case ANIMTYPE_DSSPK:
428 case ANIMTYPE_DSGPENCIL:
429 case ANIMTYPE_DSMCLIP:
430 case ANIMTYPE_DSHAIR:
431 case ANIMTYPE_DSPOINTCLOUD:
432 case ANIMTYPE_DSVOLUME:
433 case ANIMTYPE_DSSIMULATION: {
434 /* need to verify that this data is valid for now */
435 if (ale->adt) {
436 ACHANNEL_SET_FLAG(ale->adt, sel, ADT_UI_SELECTED);
437 ale->adt->flag &= ~ADT_UI_ACTIVE;
438 }
439 break;
440 }
441 case ANIMTYPE_GPLAYER: {
442 bGPDlayer *gpl = (bGPDlayer *)ale->data;
443
444 ACHANNEL_SET_FLAG(gpl, sel, GP_LAYER_SELECT);
445 break;
446 }
447 case ANIMTYPE_MASKLAYER: {
448 MaskLayer *masklay = (MaskLayer *)ale->data;
449
450 ACHANNEL_SET_FLAG(masklay, sel, MASK_LAYERFLAG_SELECT);
451 break;
452 }
453 }
454 }
455 }
456
457 /* Set selection state of all animation channels in the context. */
ANIM_anim_channels_select_set(bAnimContext * ac,eAnimChannels_SetFlag sel)458 void ANIM_anim_channels_select_set(bAnimContext *ac, eAnimChannels_SetFlag sel)
459 {
460 ListBase anim_data = anim_channels_for_selection(ac);
461 anim_channels_select_set(ac, anim_data, sel);
462 ANIM_animdata_freelist(&anim_data);
463 }
464
465 /* Toggle selection state of all animation channels in the context. */
ANIM_anim_channels_select_toggle(bAnimContext * ac)466 void ANIM_anim_channels_select_toggle(bAnimContext *ac)
467 {
468 ListBase anim_data = anim_channels_for_selection(ac);
469 const eAnimChannels_SetFlag sel = anim_channels_selection_flag_for_toggle(anim_data);
470 anim_channels_select_set(ac, anim_data, sel);
471 ANIM_animdata_freelist(&anim_data);
472 }
473
474 /* ---------------------------- Graph Editor ------------------------------------- */
475
476 /* Copy a certain channel setting to parents of the modified channel. */
anim_flush_channel_setting_up(bAnimContext * ac,const eAnimChannel_Settings setting,const eAnimChannels_SetFlag mode,bAnimListElem * const match,const int matchLevel)477 static void anim_flush_channel_setting_up(bAnimContext *ac,
478 const eAnimChannel_Settings setting,
479 const eAnimChannels_SetFlag mode,
480 bAnimListElem *const match,
481 const int matchLevel)
482 {
483 /* flush up?
484 *
485 * For Visibility:
486 * - only flush up if the current state is now enabled (positive 'on' state is default)
487 * (otherwise, it's too much work to force the parents to be inactive too)
488 *
489 * For everything else:
490 * - only flush up if the current state is now disabled (negative 'off' state is default)
491 * (otherwise, it's too much work to force the parents to be active too)
492 */
493 if (setting == ACHANNEL_SETTING_VISIBLE) {
494 if (mode == ACHANNEL_SETFLAG_CLEAR) {
495 return;
496 }
497 }
498 else {
499 if (mode != ACHANNEL_SETFLAG_CLEAR) {
500 return;
501 }
502 }
503
504 /* Go backwards in the list, until the highest-ranking element
505 * (by indention has been covered). */
506 int prevLevel = matchLevel;
507 for (bAnimListElem *ale = match->prev; ale; ale = ale->prev) {
508 const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale);
509
510 /* if no channel info was found, skip, since this type might not have any useful info */
511 if (acf == NULL) {
512 continue;
513 }
514
515 /* Get the level of the current channel traversed
516 * - we define the level as simply being the offset for the start of the channel
517 */
518 const int level = (acf->get_offset) ? acf->get_offset(ac, ale) : 0;
519
520 if (level == prevLevel) {
521 /* Don't influence siblings. */
522 continue;
523 }
524
525 if (level > prevLevel) {
526 /* If previous level was a base-level (i.e. 0 offset / root of one hierarchy), stop here. */
527 if (prevLevel == 0) {
528 return;
529 }
530
531 /* Otherwise, this level weaves into another sibling hierarchy to the previous one just
532 * finished, so skip until we get to the parent of this level. */
533 continue;
534 }
535
536 /* The level is 'less than' (i.e. more important) the level we're matching but also 'less
537 * than' the level just tried (i.e. only the 1st group above grouped F-Curves, when toggling
538 * visibility of F-Curves, gets flushed, which should happen if we don't let prevLevel get
539 * updated below once the first 1st group is found). */
540 ANIM_channel_setting_set(ac, ale, setting, mode);
541
542 /* store this level as the 'old' level now */
543 prevLevel = level;
544 }
545 }
546
547 /* Copy a certain channel setting to children of the modified channel. */
anim_flush_channel_setting_down(bAnimContext * ac,const eAnimChannel_Settings setting,const eAnimChannels_SetFlag mode,bAnimListElem * const match,const int matchLevel)548 static void anim_flush_channel_setting_down(bAnimContext *ac,
549 const eAnimChannel_Settings setting,
550 const eAnimChannels_SetFlag mode,
551 bAnimListElem *const match,
552 const int matchLevel)
553 {
554 /* go forwards in the list, until the lowest-ranking element (by indention has been covered) */
555 for (bAnimListElem *ale = match->next; ale; ale = ale->next) {
556 const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale);
557
558 /* if no channel info was found, skip, since this type might not have any useful info */
559 if (acf == NULL) {
560 continue;
561 }
562
563 /* get the level of the current channel traversed
564 * - we define the level as simply being the offset for the start of the channel
565 */
566 const int level = (acf->get_offset) ? acf->get_offset(ac, ale) : 0;
567
568 /* if the level is 'greater than' (i.e. less important) the channel that was changed,
569 * flush the new status...
570 */
571 if (level > matchLevel) {
572 ANIM_channel_setting_set(ac, ale, setting, mode);
573 /* however, if the level is 'less than or equal to' the channel that was changed,
574 * (i.e. the current channel is as important if not more important than the changed
575 * channel) then we should stop, since we've found the last one of the children we should
576 * flush
577 */
578 }
579 else {
580 break;
581 }
582 }
583 }
584
585 /* Flush visibility (for Graph Editor) changes up/down hierarchy for changes in the given setting
586 * - anim_data: list of the all the anim channels that can be chosen
587 * -> filtered using ANIMFILTER_CHANNELS only, since if we took VISIBLE too,
588 * then the channels under closed expanders get ignored...
589 * - ale_setting: the anim channel (not in the anim_data list directly, though occurring there)
590 * with the new state of the setting that we want flushed up/down the hierarchy
591 * - setting: type of setting to set
592 * - on: whether the visibility setting has been enabled or disabled
593 */
ANIM_flush_setting_anim_channels(bAnimContext * ac,ListBase * anim_data,bAnimListElem * ale_setting,eAnimChannel_Settings setting,eAnimChannels_SetFlag mode)594 void ANIM_flush_setting_anim_channels(bAnimContext *ac,
595 ListBase *anim_data,
596 bAnimListElem *ale_setting,
597 eAnimChannel_Settings setting,
598 eAnimChannels_SetFlag mode)
599 {
600 bAnimListElem *ale, *match = NULL;
601 int matchLevel = 0;
602
603 /* sanity check */
604 if (ELEM(NULL, anim_data, anim_data->first)) {
605 return;
606 }
607
608 if (setting == ACHANNEL_SETTING_ALWAYS_VISIBLE) {
609 return;
610 }
611
612 /* find the channel that got changed */
613 for (ale = anim_data->first; ale; ale = ale->next) {
614 /* compare data, and type as main way of identifying the channel */
615 if ((ale->data == ale_setting->data) && (ale->type == ale_setting->type)) {
616 /* We also have to check the ID, this is assigned to,
617 * since a block may have multiple users. */
618 /* TODO: is the owner-data more revealing? */
619 if (ale->id == ale_setting->id) {
620 match = ale;
621 break;
622 }
623 }
624 }
625 if (match == NULL) {
626 printf("ERROR: no channel matching the one changed was found\n");
627 return;
628 }
629
630 {
631 const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale_setting);
632 if (acf == NULL) {
633 printf("ERROR: no channel info for the changed channel\n");
634 return;
635 }
636
637 /* get the level of the channel that was affected
638 * - we define the level as simply being the offset for the start of the channel
639 */
640 matchLevel = (acf->get_offset) ? acf->get_offset(ac, ale_setting) : 0;
641 }
642
643 anim_flush_channel_setting_up(ac, setting, mode, match, matchLevel);
644 anim_flush_channel_setting_down(ac, setting, mode, match, matchLevel);
645 }
646
647 /* -------------------------- F-Curves ------------------------------------- */
648
649 /* Delete the given F-Curve from its AnimData block */
ANIM_fcurve_delete_from_animdata(bAnimContext * ac,AnimData * adt,FCurve * fcu)650 void ANIM_fcurve_delete_from_animdata(bAnimContext *ac, AnimData *adt, FCurve *fcu)
651 {
652 /* - if no AnimData, we've got nowhere to remove the F-Curve from
653 * (this doesn't guarantee that the F-Curve is in there, but at least we tried
654 * - if no F-Curve, there is nothing to remove
655 */
656 if (ELEM(NULL, adt, fcu)) {
657 return;
658 }
659
660 /* remove from whatever list it came from
661 * - Action Group
662 * - Action
663 * - Drivers
664 * - TODO... some others?
665 */
666 if ((ac) && (ac->datatype == ANIMCONT_DRIVERS)) {
667 /* driver F-Curve */
668 BLI_remlink(&adt->drivers, fcu);
669 }
670 else if (adt->action) {
671 bAction *act = adt->action;
672
673 /* remove from group or action, whichever one "owns" the F-Curve */
674 if (fcu->grp) {
675 bActionGroup *agrp = fcu->grp;
676
677 /* remove F-Curve from group+action */
678 action_groups_remove_channel(act, fcu);
679
680 /* if group has no more channels, remove it too,
681 * otherwise can have many dangling groups T33541.
682 */
683 if (BLI_listbase_is_empty(&agrp->channels)) {
684 BLI_freelinkN(&act->groups, agrp);
685 }
686 }
687 else {
688 BLI_remlink(&act->curves, fcu);
689 }
690
691 /* if action has no more F-Curves as a result of this, unlink it from
692 * AnimData if it did not come from a NLA Strip being tweaked.
693 *
694 * This is done so that we don't have dangling Object+Action entries in
695 * channel list that are empty, and linger around long after the data they
696 * are for has disappeared (and probably won't come back).
697 */
698 ANIM_remove_empty_action_from_animdata(adt);
699 }
700
701 /* free the F-Curve itself */
702 BKE_fcurve_free(fcu);
703 }
704
705 /* If the action has no F-Curves, unlink it from AnimData if it did not
706 * come from a NLA Strip being tweaked. */
ANIM_remove_empty_action_from_animdata(struct AnimData * adt)707 bool ANIM_remove_empty_action_from_animdata(struct AnimData *adt)
708 {
709 if (adt->action != NULL) {
710 bAction *act = adt->action;
711
712 if (BLI_listbase_is_empty(&act->curves) && (adt->flag & ADT_NLA_EDIT_ON) == 0) {
713 id_us_min(&act->id);
714 adt->action = NULL;
715 return true;
716 }
717 }
718
719 return false;
720 }
721
722 /* ************************************************************************** */
723 /* OPERATORS */
724
725 /* ****************** Operator Utilities ********************************** */
726
727 /* poll callback for being in an Animation Editor channels list region */
animedit_poll_channels_active(bContext * C)728 static bool animedit_poll_channels_active(bContext *C)
729 {
730 ScrArea *area = CTX_wm_area(C);
731
732 /* channels region test */
733 /* TODO: could enhance with actually testing if channels region? */
734 if (ELEM(NULL, area, CTX_wm_region(C))) {
735 return 0;
736 }
737 /* animation editor test */
738 if (ELEM(area->spacetype, SPACE_ACTION, SPACE_GRAPH, SPACE_NLA) == 0) {
739 return 0;
740 }
741
742 return 1;
743 }
744
745 /* poll callback for Animation Editor channels list region + not in NLA-tweakmode for NLA */
animedit_poll_channels_nla_tweakmode_off(bContext * C)746 static bool animedit_poll_channels_nla_tweakmode_off(bContext *C)
747 {
748 ScrArea *area = CTX_wm_area(C);
749 Scene *scene = CTX_data_scene(C);
750
751 /* channels region test */
752 /* TODO: could enhance with actually testing if channels region? */
753 if (ELEM(NULL, area, CTX_wm_region(C))) {
754 return 0;
755 }
756 /* animation editor test */
757 if (ELEM(area->spacetype, SPACE_ACTION, SPACE_GRAPH, SPACE_NLA) == 0) {
758 return 0;
759 }
760
761 /* NLA TweakMode test */
762 if (area->spacetype == SPACE_NLA) {
763 if ((scene == NULL) || (scene->flag & SCE_NLA_EDIT_ON)) {
764 return 0;
765 }
766 }
767
768 return 1;
769 }
770
771 /* ****************** Rearrange Channels Operator ******************* */
772
773 /* constants for channel rearranging */
774 /* WARNING: don't change existing ones without modifying rearrange func accordingly */
775 typedef enum eRearrangeAnimChan_Mode {
776 REARRANGE_ANIMCHAN_TOP = -2,
777 REARRANGE_ANIMCHAN_UP = -1,
778 REARRANGE_ANIMCHAN_DOWN = 1,
779 REARRANGE_ANIMCHAN_BOTTOM = 2,
780 } eRearrangeAnimChan_Mode;
781
782 /* defines for rearranging channels */
783 static const EnumPropertyItem prop_animchannel_rearrange_types[] = {
784 {REARRANGE_ANIMCHAN_TOP, "TOP", 0, "To Top", ""},
785 {REARRANGE_ANIMCHAN_UP, "UP", 0, "Up", ""},
786 {REARRANGE_ANIMCHAN_DOWN, "DOWN", 0, "Down", ""},
787 {REARRANGE_ANIMCHAN_BOTTOM, "BOTTOM", 0, "To Bottom", ""},
788 {0, NULL, 0, NULL, NULL},
789 };
790
791 /* Reordering "Islands" Defines ----------------------------------- */
792
793 /* Island definition - just a listbase container */
794 typedef struct tReorderChannelIsland {
795 struct tReorderChannelIsland *next, *prev;
796
797 ListBase channels; /* channels within this region with the same state */
798 int flag; /* eReorderIslandFlag */
799 } tReorderChannelIsland;
800
801 /* flags for channel reordering islands */
802 typedef enum eReorderIslandFlag {
803 REORDER_ISLAND_SELECTED = (1 << 0), /* island is selected */
804 REORDER_ISLAND_UNTOUCHABLE = (1 << 1), /* island should be ignored */
805 REORDER_ISLAND_MOVED = (1 << 2), /* island has already been moved */
806 REORDER_ISLAND_HIDDEN = (1 << 3), /* island is not visible */
807 } eReorderIslandFlag;
808
809 /* Rearrange Methods --------------------------------------------- */
810
rearrange_island_ok(tReorderChannelIsland * island)811 static bool rearrange_island_ok(tReorderChannelIsland *island)
812 {
813 /* island must not be untouchable */
814 if (island->flag & REORDER_ISLAND_UNTOUCHABLE) {
815 return 0;
816 }
817
818 /* island should be selected to be moved */
819 return (island->flag & REORDER_ISLAND_SELECTED) && !(island->flag & REORDER_ISLAND_MOVED);
820 }
821
822 /* ............................. */
823
rearrange_island_top(ListBase * list,tReorderChannelIsland * island)824 static bool rearrange_island_top(ListBase *list, tReorderChannelIsland *island)
825 {
826 if (rearrange_island_ok(island)) {
827 /* remove from current position */
828 BLI_remlink(list, island);
829
830 /* make it first element */
831 BLI_insertlinkbefore(list, list->first, island);
832
833 return 1;
834 }
835
836 return 0;
837 }
838
rearrange_island_up(ListBase * list,tReorderChannelIsland * island)839 static bool rearrange_island_up(ListBase *list, tReorderChannelIsland *island)
840 {
841 if (rearrange_island_ok(island)) {
842 /* moving up = moving before the previous island, otherwise we're in the same place */
843 tReorderChannelIsland *prev = island->prev;
844
845 /* Skip hidden islands! */
846 while (prev && prev->flag & REORDER_ISLAND_HIDDEN) {
847 prev = prev->prev;
848 }
849
850 if (prev) {
851 /* remove from current position */
852 BLI_remlink(list, island);
853
854 /* push it up */
855 BLI_insertlinkbefore(list, prev, island);
856
857 return 1;
858 }
859 }
860
861 return 0;
862 }
863
rearrange_island_down(ListBase * list,tReorderChannelIsland * island)864 static bool rearrange_island_down(ListBase *list, tReorderChannelIsland *island)
865 {
866 if (rearrange_island_ok(island)) {
867 /* moving down = moving after the next island, otherwise we're in the same place */
868 tReorderChannelIsland *next = island->next;
869
870 /* Skip hidden islands! */
871 while (next && next->flag & REORDER_ISLAND_HIDDEN) {
872 next = next->next;
873 }
874
875 if (next) {
876 /* can only move past if next is not untouchable (i.e. nothing can go after it) */
877 if ((next->flag & REORDER_ISLAND_UNTOUCHABLE) == 0) {
878 /* remove from current position */
879 BLI_remlink(list, island);
880
881 /* push it down */
882 BLI_insertlinkafter(list, next, island);
883
884 return true;
885 }
886 }
887 /* else: no next channel, so we're at the bottom already, so can't move */
888 }
889
890 return false;
891 }
892
rearrange_island_bottom(ListBase * list,tReorderChannelIsland * island)893 static bool rearrange_island_bottom(ListBase *list, tReorderChannelIsland *island)
894 {
895 if (rearrange_island_ok(island)) {
896 tReorderChannelIsland *last = list->last;
897
898 /* remove island from current position */
899 BLI_remlink(list, island);
900
901 /* add before or after the last channel? */
902 if ((last->flag & REORDER_ISLAND_UNTOUCHABLE) == 0) {
903 /* can add after it */
904 BLI_addtail(list, island);
905 }
906 else {
907 /* can at most go just before it, since last cannot be moved */
908 BLI_insertlinkbefore(list, last, island);
909 }
910
911 return true;
912 }
913
914 return false;
915 }
916
917 /* ............................. */
918
919 /**
920 * typedef for channel rearranging function
921 *
922 * \param list: List of tReorderChannelIsland's that channels belong to
923 * \param island: Island to be moved
924 * \return Whether operation was a success
925 */
926 typedef bool (*AnimChanRearrangeFp)(ListBase *list, tReorderChannelIsland *island);
927
928 /* get rearranging function, given 'rearrange' mode */
rearrange_get_mode_func(eRearrangeAnimChan_Mode mode)929 static AnimChanRearrangeFp rearrange_get_mode_func(eRearrangeAnimChan_Mode mode)
930 {
931 switch (mode) {
932 case REARRANGE_ANIMCHAN_TOP:
933 return rearrange_island_top;
934 case REARRANGE_ANIMCHAN_UP:
935 return rearrange_island_up;
936 case REARRANGE_ANIMCHAN_DOWN:
937 return rearrange_island_down;
938 case REARRANGE_ANIMCHAN_BOTTOM:
939 return rearrange_island_bottom;
940 default:
941 return NULL;
942 }
943 }
944
945 /* get rearranging function, given 'rearrange' mode (grease pencil is inverted) */
rearrange_gpencil_get_mode_func(eRearrangeAnimChan_Mode mode)946 static AnimChanRearrangeFp rearrange_gpencil_get_mode_func(eRearrangeAnimChan_Mode mode)
947 {
948 switch (mode) {
949 case REARRANGE_ANIMCHAN_TOP:
950 return rearrange_island_bottom;
951 case REARRANGE_ANIMCHAN_UP:
952 return rearrange_island_down;
953 case REARRANGE_ANIMCHAN_DOWN:
954 return rearrange_island_up;
955 case REARRANGE_ANIMCHAN_BOTTOM:
956 return rearrange_island_top;
957 default:
958 return NULL;
959 }
960 }
961
962 /* Rearrange Islands Generics ------------------------------------- */
963
964 /* add channel into list of islands */
rearrange_animchannel_add_to_islands(ListBase * islands,ListBase * srcList,Link * channel,eAnim_ChannelType type,const bool is_hidden)965 static void rearrange_animchannel_add_to_islands(ListBase *islands,
966 ListBase *srcList,
967 Link *channel,
968 eAnim_ChannelType type,
969 const bool is_hidden)
970 {
971 /* always try to add to last island if possible */
972 tReorderChannelIsland *island = islands->last;
973 bool is_sel = false, is_untouchable = false;
974
975 /* get flags - selected and untouchable from the channel */
976 switch (type) {
977 case ANIMTYPE_GROUP: {
978 bActionGroup *agrp = (bActionGroup *)channel;
979
980 is_sel = SEL_AGRP(agrp);
981 is_untouchable = (agrp->flag & AGRP_TEMP) != 0;
982 break;
983 }
984 case ANIMTYPE_FCURVE:
985 case ANIMTYPE_NLACURVE: {
986 FCurve *fcu = (FCurve *)channel;
987
988 is_sel = SEL_FCU(fcu);
989 break;
990 }
991 case ANIMTYPE_NLATRACK: {
992 NlaTrack *nlt = (NlaTrack *)channel;
993
994 is_sel = SEL_NLT(nlt);
995 break;
996 }
997 case ANIMTYPE_GPLAYER: {
998 bGPDlayer *gpl = (bGPDlayer *)channel;
999
1000 is_sel = SEL_GPL(gpl);
1001 break;
1002 }
1003 default:
1004 printf(
1005 "rearrange_animchannel_add_to_islands(): don't know how to handle channels of type %u\n",
1006 type);
1007 return;
1008 }
1009
1010 /* do we need to add to a new island? */
1011 if (/* 1) no islands yet */
1012 (island == NULL) ||
1013 /* 2) unselected islands have single channels only - to allow up/down movement */
1014 ((island->flag & REORDER_ISLAND_SELECTED) == 0) ||
1015 /* 3) if channel is unselected, stop existing island
1016 * (it was either wrong sel status, or full already) */
1017 (is_sel == 0) ||
1018 /* 4) hidden status changes */
1019 ((island->flag & REORDER_ISLAND_HIDDEN) != is_hidden)) {
1020 /* create a new island now */
1021 island = MEM_callocN(sizeof(tReorderChannelIsland), "tReorderChannelIsland");
1022 BLI_addtail(islands, island);
1023
1024 if (is_sel) {
1025 island->flag |= REORDER_ISLAND_SELECTED;
1026 }
1027 if (is_untouchable) {
1028 island->flag |= REORDER_ISLAND_UNTOUCHABLE;
1029 }
1030 if (is_hidden) {
1031 island->flag |= REORDER_ISLAND_HIDDEN;
1032 }
1033 }
1034
1035 /* add channel to island - need to remove it from its existing list first though */
1036 BLI_remlink(srcList, channel);
1037 BLI_addtail(&island->channels, channel);
1038 }
1039
1040 /* flatten islands out into a single list again */
rearrange_animchannel_flatten_islands(ListBase * islands,ListBase * srcList)1041 static void rearrange_animchannel_flatten_islands(ListBase *islands, ListBase *srcList)
1042 {
1043 tReorderChannelIsland *island, *isn = NULL;
1044
1045 /* make sure srcList is empty now */
1046 BLI_assert(BLI_listbase_is_empty(srcList));
1047
1048 /* go through merging islands */
1049 for (island = islands->first; island; island = isn) {
1050 isn = island->next;
1051
1052 /* merge island channels back to main list, then delete the island */
1053 BLI_movelisttolist(srcList, &island->channels);
1054 BLI_freelinkN(islands, island);
1055 }
1056 }
1057
1058 /* ............................. */
1059
1060 /* get a list of all bAnimListElem's of a certain type which are currently visible */
rearrange_animchannels_filter_visible(ListBase * anim_data_visible,bAnimContext * ac,eAnim_ChannelType type)1061 static void rearrange_animchannels_filter_visible(ListBase *anim_data_visible,
1062 bAnimContext *ac,
1063 eAnim_ChannelType type)
1064 {
1065 ListBase anim_data = {NULL, NULL};
1066 bAnimListElem *ale, *ale_next;
1067 int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
1068
1069 /* get all visible channels */
1070 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1071
1072 /* now, only keep the ones that are of the types we are interested in */
1073 for (ale = anim_data.first; ale; ale = ale_next) {
1074 ale_next = ale->next;
1075
1076 if (ale->type != type) {
1077 BLI_freelinkN(&anim_data, ale);
1078 }
1079 }
1080
1081 /* return cleaned up list */
1082 *anim_data_visible = anim_data;
1083 }
1084
1085 /* performing rearranging of channels using islands */
rearrange_animchannel_islands(ListBase * list,AnimChanRearrangeFp rearrange_func,eRearrangeAnimChan_Mode mode,eAnim_ChannelType type,ListBase * anim_data_visible)1086 static bool rearrange_animchannel_islands(ListBase *list,
1087 AnimChanRearrangeFp rearrange_func,
1088 eRearrangeAnimChan_Mode mode,
1089 eAnim_ChannelType type,
1090 ListBase *anim_data_visible)
1091 {
1092 ListBase islands = {NULL, NULL};
1093 Link *channel, *chanNext = NULL;
1094 bool done = false;
1095
1096 /* don't waste effort on an empty list */
1097 if (BLI_listbase_is_empty(list)) {
1098 return 0;
1099 }
1100
1101 /* group channels into islands */
1102 for (channel = list->first; channel; channel = chanNext) {
1103 /* find out whether this channel is present in anim_data_visible or not! */
1104 const bool is_hidden =
1105 (BLI_findptr(anim_data_visible, channel, offsetof(bAnimListElem, data)) == NULL);
1106 chanNext = channel->next;
1107 rearrange_animchannel_add_to_islands(&islands, list, channel, type, is_hidden);
1108 }
1109
1110 /* Perform moving of selected islands now, but only if there is more than one of them
1111 * so that something will happen:
1112 *
1113 * - Scanning of the list is performed in the opposite direction
1114 * to the direction we're moving things,
1115 * so that we shouldn't need to encounter items we've moved already.
1116 */
1117 if (islands.first != islands.last) {
1118 tReorderChannelIsland *first = (mode > 0) ? islands.last : islands.first;
1119 tReorderChannelIsland *island, *isn = NULL;
1120
1121 for (island = first; island; island = isn) {
1122 isn = (mode > 0) ? island->prev : island->next;
1123
1124 /* perform rearranging */
1125 if (rearrange_func(&islands, island)) {
1126 island->flag |= REORDER_ISLAND_MOVED;
1127 done = true;
1128 }
1129 }
1130 }
1131
1132 /* ungroup islands */
1133 rearrange_animchannel_flatten_islands(&islands, list);
1134
1135 /* did we do anything? */
1136 return done;
1137 }
1138
1139 /* NLA Specific Stuff ----------------------------------------------------- */
1140
1141 /* Change the order NLA Tracks within NLA Stack
1142 * ! NLA tracks are displayed in opposite order, so directions need care
1143 * mode: REARRANGE_ANIMCHAN_*
1144 */
rearrange_nla_channels(bAnimContext * ac,AnimData * adt,eRearrangeAnimChan_Mode mode)1145 static void rearrange_nla_channels(bAnimContext *ac, AnimData *adt, eRearrangeAnimChan_Mode mode)
1146 {
1147 AnimChanRearrangeFp rearrange_func;
1148 ListBase anim_data_visible = {NULL, NULL};
1149
1150 /* hack: invert mode so that functions will work in right order */
1151 mode *= -1;
1152
1153 /* get rearranging function */
1154 rearrange_func = rearrange_get_mode_func(mode);
1155 if (rearrange_func == NULL) {
1156 return;
1157 }
1158
1159 /* Filter visible data. */
1160 rearrange_animchannels_filter_visible(&anim_data_visible, ac, ANIMTYPE_NLATRACK);
1161
1162 /* perform rearranging on tracks list */
1163 rearrange_animchannel_islands(
1164 &adt->nla_tracks, rearrange_func, mode, ANIMTYPE_NLATRACK, &anim_data_visible);
1165
1166 /* free temp data */
1167 BLI_freelistN(&anim_data_visible);
1168 }
1169
1170 /* Drivers Specific Stuff ------------------------------------------------- */
1171
1172 /* Change the order drivers within AnimData block
1173 * mode: REARRANGE_ANIMCHAN_*
1174 */
rearrange_driver_channels(bAnimContext * ac,AnimData * adt,eRearrangeAnimChan_Mode mode)1175 static void rearrange_driver_channels(bAnimContext *ac,
1176 AnimData *adt,
1177 eRearrangeAnimChan_Mode mode)
1178 {
1179 /* get rearranging function */
1180 AnimChanRearrangeFp rearrange_func = rearrange_get_mode_func(mode);
1181 ListBase anim_data_visible = {NULL, NULL};
1182
1183 if (rearrange_func == NULL) {
1184 return;
1185 }
1186
1187 /* only consider drivers if they're accessible */
1188 if (EXPANDED_DRVD(adt) == 0) {
1189 return;
1190 }
1191
1192 /* Filter visible data. */
1193 rearrange_animchannels_filter_visible(&anim_data_visible, ac, ANIMTYPE_FCURVE);
1194
1195 /* perform rearranging on drivers list (drivers are really just F-Curves) */
1196 rearrange_animchannel_islands(
1197 &adt->drivers, rearrange_func, mode, ANIMTYPE_FCURVE, &anim_data_visible);
1198
1199 /* free temp data */
1200 BLI_freelistN(&anim_data_visible);
1201 }
1202
1203 /* Action Specific Stuff ------------------------------------------------- */
1204
1205 /* make sure all action-channels belong to a group (and clear action's list) */
split_groups_action_temp(bAction * act,bActionGroup * tgrp)1206 static void split_groups_action_temp(bAction *act, bActionGroup *tgrp)
1207 {
1208 bActionGroup *agrp;
1209 FCurve *fcu;
1210
1211 if (act == NULL) {
1212 return;
1213 }
1214
1215 /* Separate F-Curves into lists per group */
1216 for (agrp = act->groups.first; agrp; agrp = agrp->next) {
1217 if (agrp->channels.first) {
1218 fcu = agrp->channels.last;
1219 act->curves.first = fcu->next;
1220
1221 fcu = agrp->channels.first;
1222 fcu->prev = NULL;
1223
1224 fcu = agrp->channels.last;
1225 fcu->next = NULL;
1226 }
1227 }
1228
1229 /* Initialize memory for temp-group */
1230 memset(tgrp, 0, sizeof(bActionGroup));
1231 tgrp->flag |= (AGRP_EXPANDED | AGRP_TEMP);
1232 BLI_strncpy(tgrp->name, "#TempGroup", sizeof(tgrp->name));
1233
1234 /* Move any action-channels not already moved, to the temp group */
1235 if (act->curves.first) {
1236 /* start of list */
1237 fcu = act->curves.first;
1238 fcu->prev = NULL;
1239 tgrp->channels.first = fcu;
1240 act->curves.first = NULL;
1241
1242 /* end of list */
1243 fcu = act->curves.last;
1244 fcu->next = NULL;
1245 tgrp->channels.last = fcu;
1246 act->curves.last = NULL;
1247
1248 /* ensure that all of these get their group set to this temp group
1249 * (so that visibility filtering works)
1250 */
1251 for (fcu = tgrp->channels.first; fcu; fcu = fcu->next) {
1252 fcu->grp = tgrp;
1253 }
1254 }
1255
1256 /* Add temp-group to list */
1257 BLI_addtail(&act->groups, tgrp);
1258 }
1259
1260 /* link lists of channels that groups have */
join_groups_action_temp(bAction * act)1261 static void join_groups_action_temp(bAction *act)
1262 {
1263 bActionGroup *agrp;
1264
1265 for (agrp = act->groups.first; agrp; agrp = agrp->next) {
1266 ListBase tempGroup;
1267
1268 /* add list of channels to action's channels */
1269 tempGroup = agrp->channels;
1270 BLI_movelisttolist(&act->curves, &agrp->channels);
1271 agrp->channels = tempGroup;
1272
1273 /* clear moved flag */
1274 agrp->flag &= ~AGRP_MOVED;
1275
1276 /* if group was temporary one:
1277 * - unassign all FCurves which were temporarily added to it
1278 * - remove from list (but don't free as it's on the stack!)
1279 */
1280 if (agrp->flag & AGRP_TEMP) {
1281 FCurve *fcu;
1282
1283 for (fcu = agrp->channels.first; fcu; fcu = fcu->next) {
1284 fcu->grp = NULL;
1285 }
1286
1287 BLI_remlink(&act->groups, agrp);
1288 break;
1289 }
1290 }
1291 }
1292
1293 /* Change the order of anim-channels within action
1294 * mode: REARRANGE_ANIMCHAN_*
1295 */
rearrange_action_channels(bAnimContext * ac,bAction * act,eRearrangeAnimChan_Mode mode)1296 static void rearrange_action_channels(bAnimContext *ac, bAction *act, eRearrangeAnimChan_Mode mode)
1297 {
1298 bActionGroup tgrp;
1299 ListBase anim_data_visible = {NULL, NULL};
1300 bool do_channels;
1301
1302 /* get rearranging function */
1303 AnimChanRearrangeFp rearrange_func = rearrange_get_mode_func(mode);
1304
1305 if (rearrange_func == NULL) {
1306 return;
1307 }
1308
1309 /* make sure we're only operating with groups (vs a mixture of groups+curves) */
1310 split_groups_action_temp(act, &tgrp);
1311
1312 /* Filter visible data. */
1313 rearrange_animchannels_filter_visible(&anim_data_visible, ac, ANIMTYPE_GROUP);
1314
1315 /* Rearrange groups first:
1316 * - The group's channels will only get considered
1317 * if nothing happened when rearranging the groups
1318 * i.e. the rearrange function returned 0.
1319 */
1320 do_channels = (rearrange_animchannel_islands(
1321 &act->groups, rearrange_func, mode, ANIMTYPE_GROUP, &anim_data_visible) == 0);
1322
1323 /* free temp data */
1324 BLI_freelistN(&anim_data_visible);
1325
1326 if (do_channels) {
1327 bActionGroup *agrp;
1328
1329 /* Filter visible data. */
1330 rearrange_animchannels_filter_visible(&anim_data_visible, ac, ANIMTYPE_FCURVE);
1331
1332 for (agrp = act->groups.first; agrp; agrp = agrp->next) {
1333 /* only consider F-Curves if they're visible (group expanded) */
1334 if (EXPANDED_AGRP(ac, agrp)) {
1335 rearrange_animchannel_islands(
1336 &agrp->channels, rearrange_func, mode, ANIMTYPE_FCURVE, &anim_data_visible);
1337 }
1338 }
1339
1340 /* free temp data */
1341 BLI_freelistN(&anim_data_visible);
1342 }
1343
1344 /* assemble lists into one list (and clear moved tags) */
1345 join_groups_action_temp(act);
1346 }
1347
1348 /* ------------------- */
1349
rearrange_nla_control_channels(bAnimContext * ac,AnimData * adt,eRearrangeAnimChan_Mode mode)1350 static void rearrange_nla_control_channels(bAnimContext *ac,
1351 AnimData *adt,
1352 eRearrangeAnimChan_Mode mode)
1353 {
1354 ListBase anim_data_visible = {NULL, NULL};
1355
1356 NlaTrack *nlt;
1357 NlaStrip *strip;
1358
1359 /* get rearranging function */
1360 AnimChanRearrangeFp rearrange_func = rearrange_get_mode_func(mode);
1361
1362 if (rearrange_func == NULL) {
1363 return;
1364 }
1365
1366 /* skip if these curves aren't being shown */
1367 if (adt->flag & ADT_NLA_SKEYS_COLLAPSED) {
1368 return;
1369 }
1370
1371 /* Filter visible data. */
1372 rearrange_animchannels_filter_visible(&anim_data_visible, ac, ANIMTYPE_NLACURVE);
1373
1374 /* we cannot rearrange between strips, but within each strip, we can rearrange those curves */
1375 for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next) {
1376 for (strip = nlt->strips.first; strip; strip = strip->next) {
1377 rearrange_animchannel_islands(
1378 &strip->fcurves, rearrange_func, mode, ANIMTYPE_NLACURVE, &anim_data_visible);
1379 }
1380 }
1381
1382 /* free temp data */
1383 BLI_freelistN(&anim_data_visible);
1384 }
1385
1386 /* ------------------- */
1387
rearrange_gpencil_channels(bAnimContext * ac,eRearrangeAnimChan_Mode mode)1388 static void rearrange_gpencil_channels(bAnimContext *ac, eRearrangeAnimChan_Mode mode)
1389 {
1390 ListBase anim_data = {NULL, NULL};
1391 bAnimListElem *ale;
1392 int filter;
1393
1394 /* get rearranging function */
1395 AnimChanRearrangeFp rearrange_func = rearrange_gpencil_get_mode_func(mode);
1396
1397 if (rearrange_func == NULL) {
1398 return;
1399 }
1400
1401 /* get Grease Pencil datablocks */
1402 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA);
1403 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1404
1405 for (ale = anim_data.first; ale; ale = ale->next) {
1406 ListBase anim_data_visible = {NULL, NULL};
1407 bGPdata *gpd = ale->data;
1408
1409 /* only consider layers if this datablock is open */
1410 BLI_assert(ale->type == ANIMTYPE_GPDATABLOCK);
1411 if ((gpd->flag & GP_DATA_EXPAND) == 0) {
1412 continue;
1413 }
1414
1415 /* Filter visible data. */
1416 rearrange_animchannels_filter_visible(&anim_data_visible, ac, ANIMTYPE_GPLAYER);
1417
1418 /* rearrange datablock's layers */
1419 rearrange_animchannel_islands(
1420 &gpd->layers, rearrange_func, mode, ANIMTYPE_GPLAYER, &anim_data_visible);
1421
1422 /* free visible layers data */
1423 BLI_freelistN(&anim_data_visible);
1424
1425 /* Tag to recalc geometry */
1426 DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
1427 }
1428
1429 /* free GPD channel data */
1430 ANIM_animdata_freelist(&anim_data);
1431
1432 WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1433 }
1434
1435 /* ------------------- */
1436
animchannels_rearrange_exec(bContext * C,wmOperator * op)1437 static int animchannels_rearrange_exec(bContext *C, wmOperator *op)
1438 {
1439 bAnimContext ac;
1440 eRearrangeAnimChan_Mode mode;
1441
1442 /* get editor data */
1443 if (ANIM_animdata_get_context(C, &ac) == 0) {
1444 return OPERATOR_CANCELLED;
1445 }
1446
1447 /* get mode */
1448 mode = RNA_enum_get(op->ptr, "direction");
1449
1450 /* method to move channels depends on the editor */
1451 if (ac.datatype == ANIMCONT_GPENCIL) {
1452 /* Grease Pencil channels */
1453 rearrange_gpencil_channels(&ac, mode);
1454 }
1455 else if (ac.datatype == ANIMCONT_MASK) {
1456 /* Grease Pencil channels */
1457 printf("Mask does not supported for moving yet\n");
1458 }
1459 else if (ac.datatype == ANIMCONT_ACTION) {
1460 /* Directly rearrange action's channels */
1461 rearrange_action_channels(&ac, ac.data, mode);
1462 }
1463 else {
1464 ListBase anim_data = {NULL, NULL};
1465 bAnimListElem *ale;
1466 int filter;
1467
1468 /* get animdata blocks */
1469 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA);
1470 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1471
1472 for (ale = anim_data.first; ale; ale = ale->next) {
1473 AnimData *adt = ale->data;
1474
1475 switch (ac.datatype) {
1476 case ANIMCONT_NLA: /* NLA-tracks only */
1477 rearrange_nla_channels(&ac, adt, mode);
1478 DEG_id_tag_update(ale->id, ID_RECALC_ANIMATION);
1479 break;
1480
1481 case ANIMCONT_DRIVERS: /* Drivers list only */
1482 rearrange_driver_channels(&ac, adt, mode);
1483 break;
1484
1485 case ANIMCONT_ACTION: /* Single Action only... */
1486 case ANIMCONT_SHAPEKEY: /* DOUBLE CHECK ME... */
1487 {
1488 if (adt->action) {
1489 rearrange_action_channels(&ac, adt->action, mode);
1490 }
1491 else if (G.debug & G_DEBUG) {
1492 printf("Animdata has no action\n");
1493 }
1494 break;
1495 }
1496
1497 default: /* DopeSheet/Graph Editor - Some Actions + NLA Control Curves */
1498 {
1499 /* NLA Control Curves */
1500 if (adt->nla_tracks.first) {
1501 rearrange_nla_control_channels(&ac, adt, mode);
1502 }
1503
1504 /* Action */
1505 if (adt->action) {
1506 rearrange_action_channels(&ac, adt->action, mode);
1507 }
1508 else if (G.debug & G_DEBUG) {
1509 printf("Animdata has no action\n");
1510 }
1511 break;
1512 }
1513 }
1514 }
1515
1516 /* free temp data */
1517 ANIM_animdata_freelist(&anim_data);
1518 }
1519
1520 /* send notifier that things have changed */
1521 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1522
1523 return OPERATOR_FINISHED;
1524 }
1525
ANIM_OT_channels_move(wmOperatorType * ot)1526 static void ANIM_OT_channels_move(wmOperatorType *ot)
1527 {
1528 /* identifiers */
1529 ot->name = "Move Channels";
1530 ot->idname = "ANIM_OT_channels_move";
1531 ot->description = "Rearrange selected animation channels";
1532
1533 /* api callbacks */
1534 ot->exec = animchannels_rearrange_exec;
1535 ot->poll = animedit_poll_channels_nla_tweakmode_off;
1536
1537 /* flags */
1538 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1539
1540 /* props */
1541 ot->prop = RNA_def_enum(ot->srna,
1542 "direction",
1543 prop_animchannel_rearrange_types,
1544 REARRANGE_ANIMCHAN_DOWN,
1545 "Direction",
1546 "");
1547 }
1548
1549 /* ******************** Group Channel Operator ************************ */
1550
animchannels_grouping_poll(bContext * C)1551 static bool animchannels_grouping_poll(bContext *C)
1552 {
1553 ScrArea *area = CTX_wm_area(C);
1554 SpaceLink *sl;
1555
1556 /* channels region test */
1557 /* TODO: could enhance with actually testing if channels region? */
1558 if (ELEM(NULL, area, CTX_wm_region(C))) {
1559 return 0;
1560 }
1561
1562 /* animation editor test - must be suitable modes only */
1563 sl = CTX_wm_space_data(C);
1564
1565 switch (area->spacetype) {
1566 /* supported... */
1567 case SPACE_ACTION: {
1568 SpaceAction *saction = (SpaceAction *)sl;
1569
1570 /* dopesheet and action only - all others are for other datatypes or have no groups */
1571 if (ELEM(saction->mode, SACTCONT_ACTION, SACTCONT_DOPESHEET) == 0) {
1572 return 0;
1573 }
1574
1575 break;
1576 }
1577 case SPACE_GRAPH: {
1578 SpaceGraph *sipo = (SpaceGraph *)sl;
1579
1580 /* drivers can't have groups... */
1581 if (sipo->mode != SIPO_MODE_ANIMATION) {
1582 return 0;
1583 }
1584
1585 break;
1586 }
1587 /* unsupported... */
1588 default:
1589 return 0;
1590 }
1591
1592 return 1;
1593 }
1594
1595 /* ----------------------------------------------------------- */
1596
animchannels_group_channels(bAnimContext * ac,bAnimListElem * adt_ref,const char name[])1597 static void animchannels_group_channels(bAnimContext *ac,
1598 bAnimListElem *adt_ref,
1599 const char name[])
1600 {
1601 AnimData *adt = adt_ref->adt;
1602 bAction *act = adt->action;
1603
1604 if (act) {
1605 ListBase anim_data = {NULL, NULL};
1606 int filter;
1607
1608 /* find selected F-Curves to re-group */
1609 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL);
1610 ANIM_animdata_filter(ac, &anim_data, filter, adt_ref, ANIMCONT_CHANNEL);
1611
1612 if (anim_data.first) {
1613 bActionGroup *agrp;
1614 bAnimListElem *ale;
1615
1616 /* create new group, which should now be part of the action */
1617 agrp = action_groups_add_new(act, name);
1618 BLI_assert(agrp != NULL);
1619
1620 /* transfer selected F-Curves across to new group */
1621 for (ale = anim_data.first; ale; ale = ale->next) {
1622 FCurve *fcu = (FCurve *)ale->data;
1623 bActionGroup *grp = fcu->grp;
1624
1625 /* remove F-Curve from group, then group too if it is now empty */
1626 action_groups_remove_channel(act, fcu);
1627
1628 if ((grp) && BLI_listbase_is_empty(&grp->channels)) {
1629 BLI_freelinkN(&act->groups, grp);
1630 }
1631
1632 /* add F-Curve to group */
1633 action_groups_add_channel(act, agrp, fcu);
1634 }
1635 }
1636
1637 /* cleanup */
1638 ANIM_animdata_freelist(&anim_data);
1639 }
1640 }
1641
animchannels_group_exec(bContext * C,wmOperator * op)1642 static int animchannels_group_exec(bContext *C, wmOperator *op)
1643 {
1644 bAnimContext ac;
1645 char name[MAX_NAME];
1646
1647 /* get editor data */
1648 if (ANIM_animdata_get_context(C, &ac) == 0) {
1649 return OPERATOR_CANCELLED;
1650 }
1651
1652 /* get name for new group */
1653 RNA_string_get(op->ptr, "name", name);
1654
1655 /* XXX: name for group should never be empty... */
1656 if (name[0]) {
1657 ListBase anim_data = {NULL, NULL};
1658 bAnimListElem *ale;
1659 int filter;
1660
1661 /* handle each animdata block separately, so that the regrouping doesn't flow into blocks */
1662 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA |
1663 ANIMFILTER_NODUPLIS);
1664 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1665
1666 for (ale = anim_data.first; ale; ale = ale->next) {
1667 animchannels_group_channels(&ac, ale, name);
1668 }
1669
1670 /* free temp data */
1671 ANIM_animdata_freelist(&anim_data);
1672
1673 /* updatss */
1674 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1675 }
1676
1677 return OPERATOR_FINISHED;
1678 }
1679
ANIM_OT_channels_group(wmOperatorType * ot)1680 static void ANIM_OT_channels_group(wmOperatorType *ot)
1681 {
1682 /* identifiers */
1683 ot->name = "Group Channels";
1684 ot->idname = "ANIM_OT_channels_group";
1685 ot->description = "Add selected F-Curves to a new group";
1686
1687 /* callbacks */
1688 ot->invoke = WM_operator_props_popup;
1689 ot->exec = animchannels_group_exec;
1690 ot->poll = animchannels_grouping_poll;
1691
1692 /* flags */
1693 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1694
1695 /* props */
1696 ot->prop = RNA_def_string(ot->srna,
1697 "name",
1698 "New Group",
1699 sizeof(((bActionGroup *)NULL)->name),
1700 "Name",
1701 "Name of newly created group");
1702 /* XXX: still not too sure about this - keeping same text is confusing... */
1703 // RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE);
1704 }
1705
1706 /* ----------------------------------------------------------- */
1707
animchannels_ungroup_exec(bContext * C,wmOperator * UNUSED (op))1708 static int animchannels_ungroup_exec(bContext *C, wmOperator *UNUSED(op))
1709 {
1710 bAnimContext ac;
1711
1712 ListBase anim_data = {NULL, NULL};
1713 bAnimListElem *ale;
1714 int filter;
1715
1716 /* get editor data */
1717 if (ANIM_animdata_get_context(C, &ac) == 0) {
1718 return OPERATOR_CANCELLED;
1719 }
1720
1721 /* just selected F-Curves... */
1722 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL |
1723 ANIMFILTER_NODUPLIS);
1724 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1725
1726 for (ale = anim_data.first; ale; ale = ale->next) {
1727 /* find action for this F-Curve... */
1728 if (ale->adt && ale->adt->action) {
1729 FCurve *fcu = (FCurve *)ale->data;
1730 bAction *act = ale->adt->action;
1731
1732 /* only proceed to remove if F-Curve is in a group... */
1733 if (fcu->grp) {
1734 bActionGroup *agrp = fcu->grp;
1735
1736 /* remove F-Curve from group and add at tail (ungrouped) */
1737 action_groups_remove_channel(act, fcu);
1738 BLI_addtail(&act->curves, fcu);
1739
1740 /* delete group if it is now empty */
1741 if (BLI_listbase_is_empty(&agrp->channels)) {
1742 BLI_freelinkN(&act->groups, agrp);
1743 }
1744 }
1745 }
1746 }
1747
1748 /* cleanup */
1749 ANIM_animdata_freelist(&anim_data);
1750
1751 /* updates */
1752 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1753
1754 return OPERATOR_FINISHED;
1755 }
1756
ANIM_OT_channels_ungroup(wmOperatorType * ot)1757 static void ANIM_OT_channels_ungroup(wmOperatorType *ot)
1758 {
1759 /* identifiers */
1760 ot->name = "Ungroup Channels";
1761 ot->idname = "ANIM_OT_channels_ungroup";
1762 ot->description = "Remove selected F-Curves from their current groups";
1763
1764 /* callbacks */
1765 ot->exec = animchannels_ungroup_exec;
1766 ot->poll = animchannels_grouping_poll;
1767
1768 /* flags */
1769 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1770 }
1771
1772 /* ******************** Delete Channel Operator *********************** */
1773
tag_update_animation_element(bAnimListElem * ale)1774 static void tag_update_animation_element(bAnimListElem *ale)
1775 {
1776 ID *id = ale->id;
1777 AnimData *adt = BKE_animdata_from_id(id);
1778 /* TODO(sergey): Technically, if the animation element is being deleted
1779 * from a driver we don't have to tag action. This is something we can check
1780 * for in the future. For now just do most reliable tag which was always happening. */
1781 if (adt != NULL) {
1782 DEG_id_tag_update(id, ID_RECALC_ANIMATION);
1783 if (adt->action != NULL) {
1784 DEG_id_tag_update(&adt->action->id, ID_RECALC_ANIMATION);
1785 }
1786 }
1787 /* Deals with NLA and drivers.
1788 * Doesn't cause overhead for action updates, since object will receive
1789 * animation update after dependency graph flushes update from action to
1790 * all its users. */
1791 DEG_id_tag_update(id, ID_RECALC_ANIMATION);
1792 }
1793
animchannels_delete_exec(bContext * C,wmOperator * UNUSED (op))1794 static int animchannels_delete_exec(bContext *C, wmOperator *UNUSED(op))
1795 {
1796 bAnimContext ac;
1797 ListBase anim_data = {NULL, NULL};
1798 bAnimListElem *ale;
1799 int filter;
1800
1801 /* get editor data */
1802 if (ANIM_animdata_get_context(C, &ac) == 0) {
1803 return OPERATOR_CANCELLED;
1804 }
1805
1806 /* cannot delete in shapekey */
1807 if (ac.datatype == ANIMCONT_SHAPEKEY) {
1808 return OPERATOR_CANCELLED;
1809 }
1810
1811 /* do groups only first (unless in Drivers mode, where there are none) */
1812 if (ac.datatype != ANIMCONT_DRIVERS) {
1813 /* filter data */
1814 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL |
1815 ANIMFILTER_LIST_CHANNELS | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
1816 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1817
1818 /* delete selected groups and their associated channels */
1819 for (ale = anim_data.first; ale; ale = ale->next) {
1820 /* only groups - don't check other types yet, since they may no-longer exist */
1821 if (ale->type == ANIMTYPE_GROUP) {
1822 bActionGroup *agrp = (bActionGroup *)ale->data;
1823 AnimData *adt = ale->adt;
1824 FCurve *fcu, *fcn;
1825
1826 /* skip this group if no AnimData available, as we can't safely remove the F-Curves */
1827 if (adt == NULL) {
1828 continue;
1829 }
1830
1831 /* delete all of the Group's F-Curves, but no others */
1832 for (fcu = agrp->channels.first; fcu && fcu->grp == agrp; fcu = fcn) {
1833 fcn = fcu->next;
1834
1835 /* remove from group and action, then free */
1836 action_groups_remove_channel(adt->action, fcu);
1837 BKE_fcurve_free(fcu);
1838 }
1839
1840 /* free the group itself */
1841 if (adt->action) {
1842 BLI_freelinkN(&adt->action->groups, agrp);
1843 DEG_id_tag_update_ex(CTX_data_main(C), &adt->action->id, ID_RECALC_ANIMATION);
1844 }
1845 else {
1846 MEM_freeN(agrp);
1847 }
1848 }
1849 }
1850
1851 /* cleanup */
1852 ANIM_animdata_freelist(&anim_data);
1853 }
1854
1855 /* filter data */
1856 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL |
1857 ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
1858 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1859
1860 /* delete selected data channels */
1861 for (ale = anim_data.first; ale; ale = ale->next) {
1862 switch (ale->type) {
1863 case ANIMTYPE_FCURVE: {
1864 /* F-Curves if we can identify its parent */
1865 AnimData *adt = ale->adt;
1866 FCurve *fcu = (FCurve *)ale->data;
1867
1868 /* try to free F-Curve */
1869 ANIM_fcurve_delete_from_animdata(&ac, adt, fcu);
1870 tag_update_animation_element(ale);
1871 break;
1872 }
1873 case ANIMTYPE_NLACURVE: {
1874 /* NLA Control Curve - Deleting it should disable the corresponding setting... */
1875 NlaStrip *strip = (NlaStrip *)ale->owner;
1876 FCurve *fcu = (FCurve *)ale->data;
1877
1878 if (STREQ(fcu->rna_path, "strip_time")) {
1879 strip->flag &= ~NLASTRIP_FLAG_USR_TIME;
1880 }
1881 else if (STREQ(fcu->rna_path, "influence")) {
1882 strip->flag &= ~NLASTRIP_FLAG_USR_INFLUENCE;
1883 }
1884 else {
1885 printf("ERROR: Trying to delete NLA Control Curve for unknown property '%s'\n",
1886 fcu->rna_path);
1887 }
1888
1889 /* unlink and free the F-Curve */
1890 BLI_remlink(&strip->fcurves, fcu);
1891 BKE_fcurve_free(fcu);
1892 tag_update_animation_element(ale);
1893 break;
1894 }
1895 case ANIMTYPE_GPLAYER: {
1896 /* Grease Pencil layer */
1897 bGPdata *gpd = (bGPdata *)ale->id;
1898 bGPDlayer *gpl = (bGPDlayer *)ale->data;
1899
1900 /* try to delete the layer's data and the layer itself */
1901 BKE_gpencil_layer_delete(gpd, gpl);
1902 ale->update = ANIM_UPDATE_DEPS;
1903 break;
1904 }
1905 case ANIMTYPE_MASKLAYER: {
1906 /* Mask layer */
1907 Mask *mask = (Mask *)ale->id;
1908 MaskLayer *masklay = (MaskLayer *)ale->data;
1909
1910 /* try to delete the layer's data and the layer itself */
1911 BKE_mask_layer_remove(mask, masklay);
1912 break;
1913 }
1914 }
1915 }
1916
1917 ANIM_animdata_update(&ac, &anim_data);
1918 ANIM_animdata_freelist(&anim_data);
1919
1920 /* send notifier that things have changed */
1921 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1922 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_REMOVED, NULL);
1923 DEG_relations_tag_update(CTX_data_main(C));
1924
1925 return OPERATOR_FINISHED;
1926 }
1927
ANIM_OT_channels_delete(wmOperatorType * ot)1928 static void ANIM_OT_channels_delete(wmOperatorType *ot)
1929 {
1930 /* identifiers */
1931 ot->name = "Delete Channels";
1932 ot->idname = "ANIM_OT_channels_delete";
1933 ot->description = "Delete all selected animation channels";
1934
1935 /* api callbacks */
1936 ot->exec = animchannels_delete_exec;
1937 ot->poll = animedit_poll_channels_active;
1938
1939 /* flags */
1940 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1941 }
1942
1943 /* ********************** Set Flags Operator *********************** */
1944
1945 /* defines for setting animation-channel flags */
1946 static const EnumPropertyItem prop_animchannel_setflag_types[] = {
1947 {ACHANNEL_SETFLAG_TOGGLE, "TOGGLE", 0, "Toggle", ""},
1948 {ACHANNEL_SETFLAG_CLEAR, "DISABLE", 0, "Disable", ""},
1949 {ACHANNEL_SETFLAG_ADD, "ENABLE", 0, "Enable", ""},
1950 {ACHANNEL_SETFLAG_INVERT, "INVERT", 0, "Invert", ""},
1951 {0, NULL, 0, NULL, NULL},
1952 };
1953
1954 /* defines for set animation-channel settings */
1955 /* TODO: could add some more types, but those are really quite dependent on the mode... */
1956 static const EnumPropertyItem prop_animchannel_settings_types[] = {
1957 {ACHANNEL_SETTING_PROTECT, "PROTECT", 0, "Protect", ""},
1958 {ACHANNEL_SETTING_MUTE, "MUTE", 0, "Mute", ""},
1959 {0, NULL, 0, NULL, NULL},
1960 };
1961
1962 /* ------------------- */
1963
1964 /**
1965 * Set/clear a particular flag (setting) for all selected + visible channels
1966 * \param setting: the setting to modify.
1967 * \param mode: eAnimChannels_SetFlag.
1968 * \param onlysel: only selected channels get the flag set.
1969 *
1970 * TODO: enable a setting which turns flushing on/off?.
1971 */
setflag_anim_channels(bAnimContext * ac,eAnimChannel_Settings setting,eAnimChannels_SetFlag mode,bool onlysel,bool flush)1972 static void setflag_anim_channels(bAnimContext *ac,
1973 eAnimChannel_Settings setting,
1974 eAnimChannels_SetFlag mode,
1975 bool onlysel,
1976 bool flush)
1977 {
1978 ListBase anim_data = {NULL, NULL};
1979 ListBase all_data = {NULL, NULL};
1980 bAnimListElem *ale;
1981 int filter;
1982
1983 /* filter data that we need if flush is on */
1984 if (flush) {
1985 /* get list of all channels that selection may need to be flushed to
1986 * - hierarchy visibility needs to be ignored so that settings can get flushed
1987 * "down" inside closed containers
1988 */
1989 filter = ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS;
1990 ANIM_animdata_filter(ac, &all_data, filter, ac->data, ac->datatype);
1991 }
1992
1993 /* filter data that we're working on
1994 * - hierarchy matters if we're doing this from the channels region
1995 * since we only want to apply this to channels we can "see",
1996 * and have these affect their relatives
1997 * - but for Graph Editor, this gets used also from main region
1998 * where hierarchy doesn't apply T21276.
1999 */
2000 if ((ac->spacetype == SPACE_GRAPH) && (ac->regiontype != RGN_TYPE_CHANNELS)) {
2001 /* graph editor (case 2) */
2002 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_CURVE_VISIBLE |
2003 ANIMFILTER_NODUPLIS);
2004 }
2005 else {
2006 /* standard case */
2007 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS |
2008 ANIMFILTER_NODUPLIS);
2009 }
2010 if (onlysel) {
2011 filter |= ANIMFILTER_SEL;
2012 }
2013 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
2014
2015 /* if toggling, check if disable or enable */
2016 if (mode == ACHANNEL_SETFLAG_TOGGLE) {
2017 /* default to turn all on, unless we encounter one that's on... */
2018 mode = ACHANNEL_SETFLAG_ADD;
2019
2020 /* see if we should turn off instead... */
2021 for (ale = anim_data.first; ale; ale = ale->next) {
2022 /* set the setting in the appropriate way (if available) */
2023 if (ANIM_channel_setting_get(ac, ale, setting) > 0) {
2024 mode = ACHANNEL_SETFLAG_CLEAR;
2025 break;
2026 }
2027 }
2028 }
2029
2030 /* apply the setting */
2031 for (ale = anim_data.first; ale; ale = ale->next) {
2032 /* skip channel if setting is not available */
2033 if (ANIM_channel_setting_get(ac, ale, setting) == -1) {
2034 continue;
2035 }
2036
2037 /* set the setting in the appropriate way */
2038 ANIM_channel_setting_set(ac, ale, setting, mode);
2039 tag_update_animation_element(ale);
2040
2041 /* if flush status... */
2042 if (flush) {
2043 ANIM_flush_setting_anim_channels(ac, &all_data, ale, setting, mode);
2044 }
2045 }
2046
2047 ANIM_animdata_freelist(&anim_data);
2048 BLI_freelistN(&all_data);
2049 }
2050
2051 /* ------------------- */
2052
animchannels_setflag_exec(bContext * C,wmOperator * op)2053 static int animchannels_setflag_exec(bContext *C, wmOperator *op)
2054 {
2055 bAnimContext ac;
2056 eAnimChannel_Settings setting;
2057 eAnimChannels_SetFlag mode;
2058 bool flush = true;
2059
2060 /* get editor data */
2061 if (ANIM_animdata_get_context(C, &ac) == 0) {
2062 return OPERATOR_CANCELLED;
2063 }
2064
2065 /* mode (eAnimChannels_SetFlag), setting (eAnimChannel_Settings) */
2066 mode = RNA_enum_get(op->ptr, "mode");
2067 setting = RNA_enum_get(op->ptr, "type");
2068
2069 /* check if setting is flushable */
2070 if (setting == ACHANNEL_SETTING_EXPAND) {
2071 flush = false;
2072 }
2073
2074 /* modify setting
2075 * - only selected channels are affected
2076 */
2077 setflag_anim_channels(&ac, setting, mode, true, flush);
2078
2079 /* send notifier that things have changed */
2080 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
2081
2082 return OPERATOR_FINISHED;
2083 }
2084
2085 /* duplicate of 'ANIM_OT_channels_setting_toggle' for menu title only, weak! */
ANIM_OT_channels_setting_enable(wmOperatorType * ot)2086 static void ANIM_OT_channels_setting_enable(wmOperatorType *ot)
2087 {
2088 PropertyRNA *prop;
2089
2090 /* identifiers */
2091 ot->name = "Enable Channel Setting";
2092 ot->idname = "ANIM_OT_channels_setting_enable";
2093 ot->description = "Enable specified setting on all selected animation channels";
2094
2095 /* api callbacks */
2096 ot->invoke = WM_menu_invoke;
2097 ot->exec = animchannels_setflag_exec;
2098 ot->poll = animedit_poll_channels_active;
2099
2100 /* flags */
2101 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2102
2103 /* props */
2104 /* flag-setting mode */
2105 prop = RNA_def_enum(
2106 ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_ADD, "Mode", "");
2107 RNA_def_property_flag(prop, PROP_HIDDEN);
2108 /* setting to set */
2109 ot->prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
2110 }
2111 /* duplicate of 'ANIM_OT_channels_setting_toggle' for menu title only, weak! */
ANIM_OT_channels_setting_disable(wmOperatorType * ot)2112 static void ANIM_OT_channels_setting_disable(wmOperatorType *ot)
2113 {
2114 PropertyRNA *prop;
2115
2116 /* identifiers */
2117 ot->name = "Disable Channel Setting";
2118 ot->idname = "ANIM_OT_channels_setting_disable";
2119 ot->description = "Disable specified setting on all selected animation channels";
2120
2121 /* api callbacks */
2122 ot->invoke = WM_menu_invoke;
2123 ot->exec = animchannels_setflag_exec;
2124 ot->poll = animedit_poll_channels_active;
2125
2126 /* flags */
2127 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2128
2129 /* props */
2130 /* flag-setting mode */
2131 prop = RNA_def_enum(
2132 ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_CLEAR, "Mode", "");
2133 RNA_def_property_flag(prop, PROP_HIDDEN); /* internal hack - don't expose */
2134 /* setting to set */
2135 ot->prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
2136 }
2137
ANIM_OT_channels_setting_toggle(wmOperatorType * ot)2138 static void ANIM_OT_channels_setting_toggle(wmOperatorType *ot)
2139 {
2140 PropertyRNA *prop;
2141
2142 /* identifiers */
2143 ot->name = "Toggle Channel Setting";
2144 ot->idname = "ANIM_OT_channels_setting_toggle";
2145 ot->description = "Toggle specified setting on all selected animation channels";
2146
2147 /* api callbacks */
2148 ot->invoke = WM_menu_invoke;
2149 ot->exec = animchannels_setflag_exec;
2150 ot->poll = animedit_poll_channels_active;
2151
2152 /* flags */
2153 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2154
2155 /* props */
2156 /* flag-setting mode */
2157 prop = RNA_def_enum(
2158 ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_TOGGLE, "Mode", "");
2159 RNA_def_property_flag(prop, PROP_HIDDEN); /* internal hack - don't expose */
2160 /* setting to set */
2161 ot->prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
2162 }
2163
ANIM_OT_channels_editable_toggle(wmOperatorType * ot)2164 static void ANIM_OT_channels_editable_toggle(wmOperatorType *ot)
2165 {
2166 PropertyRNA *prop;
2167
2168 /* identifiers */
2169 ot->name = "Toggle Channel Editability";
2170 ot->idname = "ANIM_OT_channels_editable_toggle";
2171 ot->description = "Toggle editability of selected channels";
2172
2173 /* api callbacks */
2174 ot->exec = animchannels_setflag_exec;
2175 ot->poll = animedit_poll_channels_active;
2176
2177 /* flags */
2178 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2179
2180 /* props */
2181 /* flag-setting mode */
2182 RNA_def_enum(
2183 ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_TOGGLE, "Mode", "");
2184 /* setting to set */
2185 prop = RNA_def_enum(
2186 ot->srna, "type", prop_animchannel_settings_types, ACHANNEL_SETTING_PROTECT, "Type", "");
2187 RNA_def_property_flag(prop, PROP_HIDDEN); /* internal hack - don't expose */
2188 }
2189
2190 /* ********************** Expand Channels Operator *********************** */
2191
animchannels_expand_exec(bContext * C,wmOperator * op)2192 static int animchannels_expand_exec(bContext *C, wmOperator *op)
2193 {
2194 bAnimContext ac;
2195 bool onlysel = true;
2196
2197 /* get editor data */
2198 if (ANIM_animdata_get_context(C, &ac) == 0) {
2199 return OPERATOR_CANCELLED;
2200 }
2201
2202 /* only affect selected channels? */
2203 if (RNA_boolean_get(op->ptr, "all")) {
2204 onlysel = false;
2205 }
2206
2207 /* modify setting */
2208 setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_ADD, onlysel, false);
2209
2210 /* send notifier that things have changed */
2211 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
2212
2213 return OPERATOR_FINISHED;
2214 }
2215
ANIM_OT_channels_expand(wmOperatorType * ot)2216 static void ANIM_OT_channels_expand(wmOperatorType *ot)
2217 {
2218 /* identifiers */
2219 ot->name = "Expand Channels";
2220 ot->idname = "ANIM_OT_channels_expand";
2221 ot->description = "Expand (i.e. open) all selected expandable animation channels";
2222
2223 /* api callbacks */
2224 ot->exec = animchannels_expand_exec;
2225 ot->poll = animedit_poll_channels_active;
2226
2227 /* flags */
2228 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2229
2230 /* props */
2231 ot->prop = RNA_def_boolean(
2232 ot->srna, "all", 1, "All", "Expand all channels (not just selected ones)");
2233 }
2234
2235 /* ********************** Collapse Channels Operator *********************** */
2236
animchannels_collapse_exec(bContext * C,wmOperator * op)2237 static int animchannels_collapse_exec(bContext *C, wmOperator *op)
2238 {
2239 bAnimContext ac;
2240 bool onlysel = true;
2241
2242 /* get editor data */
2243 if (ANIM_animdata_get_context(C, &ac) == 0) {
2244 return OPERATOR_CANCELLED;
2245 }
2246
2247 /* only affect selected channels? */
2248 if (RNA_boolean_get(op->ptr, "all")) {
2249 onlysel = false;
2250 }
2251
2252 /* modify setting */
2253 setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_CLEAR, onlysel, false);
2254
2255 /* send notifier that things have changed */
2256 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
2257
2258 return OPERATOR_FINISHED;
2259 }
2260
ANIM_OT_channels_collapse(wmOperatorType * ot)2261 static void ANIM_OT_channels_collapse(wmOperatorType *ot)
2262 {
2263 /* identifiers */
2264 ot->name = "Collapse Channels";
2265 ot->idname = "ANIM_OT_channels_collapse";
2266 ot->description = "Collapse (i.e. close) all selected expandable animation channels";
2267
2268 /* api callbacks */
2269 ot->exec = animchannels_collapse_exec;
2270 ot->poll = animedit_poll_channels_active;
2271
2272 /* flags */
2273 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2274
2275 /* props */
2276 ot->prop = RNA_def_boolean(
2277 ot->srna, "all", true, "All", "Collapse all channels (not just selected ones)");
2278 }
2279
2280 /* ************ Remove All "Empty" AnimData Blocks Operator ********* */
2281 /* We define "empty" AnimData blocks here as those which have all 3 of criteria:
2282 * 1) No active action OR that active actions are empty
2283 * Assuming that all legitimate entries will have an action,
2284 * and that empty actions
2285 * 2) No NLA Tracks + NLA Strips
2286 * Assuming that users haven't set up any of these as "placeholders"
2287 * for convenience sake, and that most that exist were either unintentional
2288 * or are no longer wanted
2289 * 3) No drivers
2290 */
2291
animchannels_clean_empty_exec(bContext * C,wmOperator * UNUSED (op))2292 static int animchannels_clean_empty_exec(bContext *C, wmOperator *UNUSED(op))
2293 {
2294 bAnimContext ac;
2295
2296 ListBase anim_data = {NULL, NULL};
2297 bAnimListElem *ale;
2298 int filter;
2299
2300 /* get editor data */
2301 if (ANIM_animdata_get_context(C, &ac) == 0) {
2302 return OPERATOR_CANCELLED;
2303 }
2304
2305 /* get animdata blocks */
2306 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA |
2307 ANIMFILTER_NODUPLIS);
2308 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
2309
2310 for (ale = anim_data.first; ale; ale = ale->next) {
2311 ID *id = ale->id;
2312 AnimData *adt = ale->data;
2313
2314 bool action_empty = false;
2315 bool nla_empty = false;
2316 bool drivers_empty = false;
2317
2318 /* sanity checks */
2319 BLI_assert((id != NULL) && (adt != NULL));
2320
2321 /* check if this is "empty" and can be deleted */
2322 /* (For now, there are only these 3 criteria) */
2323
2324 /* 1) Active Action is missing or empty */
2325 if (ELEM(NULL, adt->action, adt->action->curves.first)) {
2326 action_empty = true;
2327 }
2328 else {
2329 /* TODO: check for keyframe + fmodifier data on these too */
2330 }
2331
2332 /* 2) No NLA Tracks and/or NLA Strips */
2333 if (adt->nla_tracks.first == NULL) {
2334 nla_empty = true;
2335 }
2336 else {
2337 NlaTrack *nlt;
2338
2339 /* empty tracks? */
2340 for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next) {
2341 if (nlt->strips.first) {
2342 /* stop searching, as we found one that actually had stuff we don't want lost
2343 * NOTE: nla_empty gets reset to false, as a previous track may have been empty
2344 */
2345 nla_empty = false;
2346 break;
2347 }
2348 if (nlt->strips.first == NULL) {
2349 /* this track is empty, but another one may still have stuff in it, so can't break yet */
2350 nla_empty = true;
2351 }
2352 }
2353 }
2354
2355 /* 3) Drivers */
2356 drivers_empty = (adt->drivers.first == NULL);
2357
2358 /* remove AnimData? */
2359 if (action_empty && nla_empty && drivers_empty) {
2360 BKE_animdata_free(id, true);
2361 }
2362 }
2363
2364 /* free temp data */
2365 ANIM_animdata_freelist(&anim_data);
2366
2367 /* send notifier that things have changed */
2368 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
2369
2370 return OPERATOR_FINISHED;
2371 }
2372
ANIM_OT_channels_clean_empty(wmOperatorType * ot)2373 static void ANIM_OT_channels_clean_empty(wmOperatorType *ot)
2374 {
2375 /* identifiers */
2376 ot->name = "Remove Empty Animation Data";
2377 ot->idname = "ANIM_OT_channels_clean_empty";
2378 ot->description = "Delete all empty animation data containers from visible data-blocks";
2379
2380 /* api callbacks */
2381 ot->exec = animchannels_clean_empty_exec;
2382 ot->poll = animedit_poll_channels_nla_tweakmode_off;
2383
2384 /* flags */
2385 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2386 }
2387
2388 /* ******************* Reenable Disabled Operator ******************* */
2389
animchannels_enable_poll(bContext * C)2390 static bool animchannels_enable_poll(bContext *C)
2391 {
2392 ScrArea *area = CTX_wm_area(C);
2393
2394 /* channels region test */
2395 /* TODO: could enhance with actually testing if channels region? */
2396 if (ELEM(NULL, area, CTX_wm_region(C))) {
2397 return 0;
2398 }
2399
2400 /* animation editor test - Action/Dopesheet/etc. and Graph only */
2401 if (ELEM(area->spacetype, SPACE_ACTION, SPACE_GRAPH) == 0) {
2402 return 0;
2403 }
2404
2405 return 1;
2406 }
2407
animchannels_enable_exec(bContext * C,wmOperator * UNUSED (op))2408 static int animchannels_enable_exec(bContext *C, wmOperator *UNUSED(op))
2409 {
2410 bAnimContext ac;
2411
2412 ListBase anim_data = {NULL, NULL};
2413 bAnimListElem *ale;
2414 int filter;
2415
2416 /* get editor data */
2417 if (ANIM_animdata_get_context(C, &ac) == 0) {
2418 return OPERATOR_CANCELLED;
2419 }
2420
2421 /* filter data */
2422 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_NODUPLIS);
2423 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
2424
2425 /* loop through filtered data and clean curves */
2426 for (ale = anim_data.first; ale; ale = ale->next) {
2427 FCurve *fcu = (FCurve *)ale->data;
2428
2429 /* remove disabled flags from F-Curves */
2430 fcu->flag &= ~FCURVE_DISABLED;
2431
2432 /* for drivers, let's do the same too */
2433 if (fcu->driver) {
2434 fcu->driver->flag &= ~DRIVER_FLAG_INVALID;
2435 }
2436
2437 /* tag everything for updates - in particular, this is needed to get drivers working again */
2438 ale->update |= ANIM_UPDATE_DEPS;
2439 }
2440
2441 ANIM_animdata_update(&ac, &anim_data);
2442 ANIM_animdata_freelist(&anim_data);
2443
2444 /* send notifier that things have changed */
2445 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
2446
2447 return OPERATOR_FINISHED;
2448 }
2449
ANIM_OT_channels_fcurves_enable(wmOperatorType * ot)2450 static void ANIM_OT_channels_fcurves_enable(wmOperatorType *ot)
2451 {
2452 /* identifiers */
2453 ot->name = "Revive Disabled F-Curves";
2454 ot->idname = "ANIM_OT_channels_fcurves_enable";
2455 ot->description = "Clears 'disabled' tag from all F-Curves to get broken F-Curves working again";
2456
2457 /* api callbacks */
2458 ot->exec = animchannels_enable_exec;
2459 ot->poll = animchannels_enable_poll;
2460
2461 /* flags */
2462 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2463 }
2464
2465 /* ****************** Find / Set Filter Operator ******************** */
2466
2467 /* XXX: make this generic? */
animchannels_find_poll(bContext * C)2468 static bool animchannels_find_poll(bContext *C)
2469 {
2470 ScrArea *area = CTX_wm_area(C);
2471
2472 if (area == NULL) {
2473 return 0;
2474 }
2475
2476 /* animation editor with dopesheet */
2477 return ELEM(area->spacetype, SPACE_ACTION, SPACE_GRAPH, SPACE_NLA);
2478 }
2479
2480 /* find_invoke() - Get initial channels */
animchannels_find_invoke(bContext * C,wmOperator * op,const wmEvent * evt)2481 static int animchannels_find_invoke(bContext *C, wmOperator *op, const wmEvent *evt)
2482 {
2483 bAnimContext ac;
2484
2485 /* get editor data */
2486 if (ANIM_animdata_get_context(C, &ac) == 0) {
2487 return OPERATOR_CANCELLED;
2488 }
2489
2490 /* set initial filter text, and enable filter */
2491 RNA_string_set(op->ptr, "query", ac.ads->searchstr);
2492
2493 /* defer to popup */
2494 return WM_operator_props_popup(C, op, evt);
2495 }
2496
2497 /* find_exec() - Called to set the value */
animchannels_find_exec(bContext * C,wmOperator * op)2498 static int animchannels_find_exec(bContext *C, wmOperator *op)
2499 {
2500 bAnimContext ac;
2501
2502 /* get editor data */
2503 if (ANIM_animdata_get_context(C, &ac) == 0) {
2504 return OPERATOR_CANCELLED;
2505 }
2506
2507 /* update filter text */
2508 RNA_string_get(op->ptr, "query", ac.ads->searchstr);
2509
2510 /* redraw */
2511 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
2512
2513 return OPERATOR_FINISHED;
2514 }
2515
ANIM_OT_channels_find(wmOperatorType * ot)2516 static void ANIM_OT_channels_find(wmOperatorType *ot)
2517 {
2518 /* identifiers */
2519 ot->name = "Find Channels";
2520 ot->idname = "ANIM_OT_channels_find";
2521 ot->description = "Filter the set of channels shown to only include those with matching names";
2522
2523 /* callbacks */
2524 ot->invoke = animchannels_find_invoke;
2525 ot->exec = animchannels_find_exec;
2526 ot->poll = animchannels_find_poll;
2527
2528 /* flags */
2529 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2530
2531 /* properties */
2532 ot->prop = RNA_def_string(ot->srna,
2533 "query",
2534 "Query",
2535 sizeof(((bDopeSheet *)NULL)->searchstr),
2536 "",
2537 "Text to search for in channel names");
2538 }
2539
2540 /* ********************** Select All Operator *********************** */
2541
animchannels_selectall_exec(bContext * C,wmOperator * op)2542 static int animchannels_selectall_exec(bContext *C, wmOperator *op)
2543 {
2544 bAnimContext ac;
2545
2546 /* get editor data */
2547 if (ANIM_animdata_get_context(C, &ac) == 0) {
2548 return OPERATOR_CANCELLED;
2549 }
2550
2551 /* 'standard' behavior - check if selected, then apply relevant selection */
2552 const int action = RNA_enum_get(op->ptr, "action");
2553 switch (action) {
2554 case SEL_TOGGLE:
2555 ANIM_anim_channels_select_toggle(&ac);
2556 break;
2557 case SEL_SELECT:
2558 ANIM_anim_channels_select_set(&ac, ACHANNEL_SETFLAG_ADD);
2559 break;
2560 case SEL_DESELECT:
2561 ANIM_anim_channels_select_set(&ac, ACHANNEL_SETFLAG_CLEAR);
2562 break;
2563 case SEL_INVERT:
2564 ANIM_anim_channels_select_set(&ac, ACHANNEL_SETFLAG_INVERT);
2565 break;
2566 default:
2567 BLI_assert(0);
2568 break;
2569 }
2570
2571 /* send notifier that things have changed */
2572 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL);
2573
2574 return OPERATOR_FINISHED;
2575 }
2576
ANIM_OT_channels_select_all(wmOperatorType * ot)2577 static void ANIM_OT_channels_select_all(wmOperatorType *ot)
2578 {
2579 /* identifiers */
2580 ot->name = "Select All";
2581 ot->idname = "ANIM_OT_channels_select_all";
2582 ot->description = "Toggle selection of all animation channels";
2583
2584 /* api callbacks */
2585 ot->exec = animchannels_selectall_exec;
2586 ot->poll = animedit_poll_channels_nla_tweakmode_off;
2587
2588 /* flags */
2589 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2590
2591 /* properties */
2592 WM_operator_properties_select_all(ot);
2593 }
2594
2595 /* ******************** Box Select Operator *********************** */
2596
box_select_anim_channels(bAnimContext * ac,rcti * rect,short selectmode)2597 static void box_select_anim_channels(bAnimContext *ac, rcti *rect, short selectmode)
2598 {
2599 ListBase anim_data = {NULL, NULL};
2600 bAnimListElem *ale;
2601 int filter;
2602
2603 SpaceNla *snla = (SpaceNla *)ac->sl;
2604 View2D *v2d = &ac->region->v2d;
2605 rctf rectf;
2606
2607 /* convert border-region to view coordinates */
2608 UI_view2d_region_to_view(v2d, rect->xmin, rect->ymin + 2, &rectf.xmin, &rectf.ymin);
2609 UI_view2d_region_to_view(v2d, rect->xmax, rect->ymax - 2, &rectf.xmax, &rectf.ymax);
2610
2611 /* filter data */
2612 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
2613 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
2614
2615 float ymax;
2616 if (ac->datatype == ANIMCONT_NLA) {
2617 ymax = NLACHANNEL_FIRST_TOP(ac);
2618 }
2619 else {
2620 ymax = ACHANNEL_FIRST_TOP(ac);
2621 }
2622
2623 /* loop over data, doing box select */
2624 for (ale = anim_data.first; ale; ale = ale->next) {
2625 float ymin;
2626 if (ac->datatype == ANIMCONT_NLA) {
2627 ymin = ymax - NLACHANNEL_STEP(snla);
2628 }
2629 else {
2630 ymin = ymax - ACHANNEL_STEP(ac);
2631 }
2632
2633 /* if channel is within border-select region, alter it */
2634 if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))) {
2635 /* set selection flags only */
2636 ANIM_channel_setting_set(ac, ale, ACHANNEL_SETTING_SELECT, selectmode);
2637
2638 /* type specific actions */
2639 switch (ale->type) {
2640 case ANIMTYPE_GROUP: {
2641 bActionGroup *agrp = (bActionGroup *)ale->data;
2642 select_pchan_for_action_group(ac, agrp, ale);
2643 /* always clear active flag after doing this */
2644 agrp->flag &= ~AGRP_ACTIVE;
2645 break;
2646 }
2647 case ANIMTYPE_NLATRACK: {
2648 NlaTrack *nlt = (NlaTrack *)ale->data;
2649
2650 /* for now, it's easier just to do this here manually, as defining a new type
2651 * currently adds complications when doing other stuff
2652 */
2653 ACHANNEL_SET_FLAG(nlt, selectmode, NLATRACK_SELECTED);
2654 break;
2655 }
2656 }
2657 }
2658
2659 /* set minimum extent to be the maximum of the next channel */
2660 ymax = ymin;
2661 }
2662
2663 /* cleanup */
2664 ANIM_animdata_freelist(&anim_data);
2665 }
2666
2667 /* ------------------- */
2668
animchannels_box_select_exec(bContext * C,wmOperator * op)2669 static int animchannels_box_select_exec(bContext *C, wmOperator *op)
2670 {
2671 bAnimContext ac;
2672 rcti rect;
2673 short selectmode = 0;
2674 const bool select = !RNA_boolean_get(op->ptr, "deselect");
2675 const bool extend = RNA_boolean_get(op->ptr, "extend");
2676
2677 /* get editor data */
2678 if (ANIM_animdata_get_context(C, &ac) == 0) {
2679 return OPERATOR_CANCELLED;
2680 }
2681
2682 /* get settings from operator */
2683 WM_operator_properties_border_to_rcti(op, &rect);
2684
2685 if (!extend) {
2686 ANIM_anim_channels_select_set(&ac, ACHANNEL_SETFLAG_CLEAR);
2687 }
2688
2689 if (select) {
2690 selectmode = ACHANNEL_SETFLAG_ADD;
2691 }
2692 else {
2693 selectmode = ACHANNEL_SETFLAG_CLEAR;
2694 }
2695
2696 /* apply box_select animation channels */
2697 box_select_anim_channels(&ac, &rect, selectmode);
2698
2699 /* send notifier that things have changed */
2700 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL);
2701
2702 return OPERATOR_FINISHED;
2703 }
2704
ANIM_OT_channels_select_box(wmOperatorType * ot)2705 static void ANIM_OT_channels_select_box(wmOperatorType *ot)
2706 {
2707 /* identifiers */
2708 ot->name = "Box Select";
2709 ot->idname = "ANIM_OT_channels_select_box";
2710 ot->description = "Select all animation channels within the specified region";
2711
2712 /* api callbacks */
2713 ot->invoke = WM_gesture_box_invoke;
2714 ot->exec = animchannels_box_select_exec;
2715 ot->modal = WM_gesture_box_modal;
2716 ot->cancel = WM_gesture_box_cancel;
2717
2718 ot->poll = animedit_poll_channels_nla_tweakmode_off;
2719
2720 /* flags */
2721 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2722
2723 /* rna */
2724 WM_operator_properties_gesture_box_select(ot);
2725 }
2726
2727 /* ******************* Rename Operator ***************************** */
2728 /* Allow renaming some channels by clicking on them */
2729
rename_anim_channels(bAnimContext * ac,int channel_index)2730 static bool rename_anim_channels(bAnimContext *ac, int channel_index)
2731 {
2732 ListBase anim_data = {NULL, NULL};
2733 const bAnimChannelType *acf;
2734 bAnimListElem *ale;
2735 int filter;
2736 bool success = false;
2737
2738 /* get the channel that was clicked on */
2739 /* filter channels */
2740 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
2741 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
2742
2743 /* get channel from index */
2744 ale = BLI_findlink(&anim_data, channel_index);
2745 if (ale == NULL) {
2746 /* channel not found */
2747 if (G.debug & G_DEBUG) {
2748 printf("Error: animation channel (index = %d) not found in rename_anim_channels()\n",
2749 channel_index);
2750 }
2751
2752 ANIM_animdata_freelist(&anim_data);
2753 return false;
2754 }
2755
2756 /* don't allow renaming linked channels */
2757 if ((ale->fcurve_owner_id != NULL && ID_IS_LINKED(ale->fcurve_owner_id)) ||
2758 (ale->id != NULL && ID_IS_LINKED(ale->id))) {
2759 ANIM_animdata_freelist(&anim_data);
2760 return false;
2761 }
2762
2763 /* check that channel can be renamed */
2764 acf = ANIM_channel_get_typeinfo(ale);
2765 if (acf && acf->name_prop) {
2766 PointerRNA ptr;
2767 PropertyRNA *prop;
2768
2769 /* ok if we can get name property to edit from this channel */
2770 if (acf->name_prop(ale, &ptr, &prop)) {
2771 /* Actually showing the rename text-field is done on redraw,
2772 * so here we just store the index of this channel in the
2773 * dope-sheet data, which will get utilized when drawing the channel.
2774 *
2775 * +1 factor is for backwards compatibility issues. */
2776 if (ac->ads) {
2777 ac->ads->renameIndex = channel_index + 1;
2778 success = true;
2779 }
2780 }
2781 }
2782
2783 /* free temp data and tag for refresh */
2784 ANIM_animdata_freelist(&anim_data);
2785 ED_region_tag_redraw(ac->region);
2786 return success;
2787 }
2788
animchannels_channel_get(bAnimContext * ac,const int mval[2])2789 static int animchannels_channel_get(bAnimContext *ac, const int mval[2])
2790 {
2791 ARegion *region;
2792 View2D *v2d;
2793 int channel_index;
2794 float x, y;
2795
2796 /* get useful pointers from animation context data */
2797 region = ac->region;
2798 v2d = ®ion->v2d;
2799
2800 /* Figure out which channel user clicked in. */
2801 UI_view2d_region_to_view(v2d, mval[0], mval[1], &x, &y);
2802
2803 if (ac->datatype == ANIMCONT_NLA) {
2804 SpaceNla *snla = (SpaceNla *)ac->sl;
2805 UI_view2d_listview_view_to_cell(NLACHANNEL_NAMEWIDTH,
2806 NLACHANNEL_STEP(snla),
2807 0,
2808 NLACHANNEL_FIRST_TOP(ac),
2809 x,
2810 y,
2811 NULL,
2812 &channel_index);
2813 }
2814 else {
2815 UI_view2d_listview_view_to_cell(ACHANNEL_NAMEWIDTH,
2816 ACHANNEL_STEP(ac),
2817 0,
2818 ACHANNEL_FIRST_TOP(ac),
2819 x,
2820 y,
2821 NULL,
2822 &channel_index);
2823 }
2824
2825 return channel_index;
2826 }
2827
animchannels_rename_invoke(bContext * C,wmOperator * UNUSED (op),const wmEvent * event)2828 static int animchannels_rename_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
2829 {
2830 bAnimContext ac;
2831 int channel_index;
2832
2833 /* get editor data */
2834 if (ANIM_animdata_get_context(C, &ac) == 0) {
2835 return OPERATOR_CANCELLED;
2836 }
2837
2838 channel_index = animchannels_channel_get(&ac, event->mval);
2839
2840 /* handle click */
2841 if (rename_anim_channels(&ac, channel_index)) {
2842 return OPERATOR_FINISHED;
2843 }
2844
2845 /* allow event to be handled by selectall operator */
2846 return OPERATOR_PASS_THROUGH;
2847 }
2848
ANIM_OT_channels_rename(wmOperatorType * ot)2849 static void ANIM_OT_channels_rename(wmOperatorType *ot)
2850 {
2851 /* identifiers */
2852 ot->name = "Rename Channels";
2853 ot->idname = "ANIM_OT_channels_rename";
2854 ot->description = "Rename animation channel under mouse";
2855
2856 /* api callbacks */
2857 ot->invoke = animchannels_rename_invoke;
2858 ot->poll = animedit_poll_channels_active;
2859 }
2860
2861 /* ******************** Mouse-Click Operator *********************** */
2862 /* Handle selection changes due to clicking on channels. Settings will get caught by UI code... */
2863
click_select_channel_scene(bAnimListElem * ale,const short selectmode)2864 static int click_select_channel_scene(bAnimListElem *ale,
2865 const short /* eEditKeyframes_Select or -1 */ selectmode)
2866 {
2867 Scene *sce = (Scene *)ale->data;
2868 AnimData *adt = sce->adt;
2869
2870 /* set selection status */
2871 if (selectmode == SELECT_INVERT) {
2872 /* swap select */
2873 sce->flag ^= SCE_DS_SELECTED;
2874 if (adt) {
2875 adt->flag ^= ADT_UI_SELECTED;
2876 }
2877 }
2878 else {
2879 sce->flag |= SCE_DS_SELECTED;
2880 if (adt) {
2881 adt->flag |= ADT_UI_SELECTED;
2882 }
2883 }
2884 return (ND_ANIMCHAN | NA_SELECTED);
2885 }
2886
click_select_channel_object(bContext * C,bAnimContext * ac,bAnimListElem * ale,const short selectmode)2887 static int click_select_channel_object(bContext *C,
2888 bAnimContext *ac,
2889 bAnimListElem *ale,
2890 const short /* eEditKeyframes_Select or -1 */ selectmode)
2891 {
2892 ViewLayer *view_layer = ac->view_layer;
2893 Base *base = (Base *)ale->data;
2894 Object *ob = base->object;
2895 AnimData *adt = ob->adt;
2896
2897 if ((base->flag & BASE_SELECTABLE) == 0) {
2898 return 0;
2899 }
2900
2901 if (selectmode == SELECT_INVERT) {
2902 /* swap select */
2903 ED_object_base_select(base, BA_INVERT);
2904
2905 if (adt) {
2906 adt->flag ^= ADT_UI_SELECTED;
2907 }
2908 }
2909 else {
2910 Base *b;
2911
2912 /* deselect all */
2913 /* TODO: should this deselect all other types of channels too? */
2914 for (b = view_layer->object_bases.first; b; b = b->next) {
2915 ED_object_base_select(b, BA_DESELECT);
2916 if (b->object->adt) {
2917 b->object->adt->flag &= ~(ADT_UI_SELECTED | ADT_UI_ACTIVE);
2918 }
2919 }
2920
2921 /* select object now */
2922 ED_object_base_select(base, BA_SELECT);
2923 if (adt) {
2924 adt->flag |= ADT_UI_SELECTED;
2925 }
2926 }
2927
2928 /* change active object - regardless of whether it is now selected [T37883] */
2929 ED_object_base_activate(C, base); /* adds notifier */
2930
2931 if ((adt) && (adt->flag & ADT_UI_SELECTED)) {
2932 adt->flag |= ADT_UI_ACTIVE;
2933 }
2934
2935 /* Ensure we exit editmode on whatever object was active before
2936 * to avoid getting stuck there - T48747. */
2937 if (ob != CTX_data_edit_object(C)) {
2938 ED_object_editmode_exit(C, EM_FREEDATA);
2939 }
2940 return (ND_ANIMCHAN | NA_SELECTED);
2941 }
2942
click_select_channel_dummy(bAnimContext * ac,bAnimListElem * ale,const short selectmode)2943 static int click_select_channel_dummy(bAnimContext *ac,
2944 bAnimListElem *ale,
2945 const short /* eEditKeyframes_Select or -1 */ selectmode)
2946 {
2947 if (ale->adt == NULL) {
2948 return 0;
2949 }
2950
2951 /* select/deselect */
2952 if (selectmode == SELECT_INVERT) {
2953 /* inverse selection status of this AnimData block only */
2954 ale->adt->flag ^= ADT_UI_SELECTED;
2955 }
2956 else {
2957 /* select AnimData block by itself */
2958 ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_CLEAR);
2959 ale->adt->flag |= ADT_UI_SELECTED;
2960 }
2961
2962 /* set active? */
2963 if (ale->adt->flag & ADT_UI_SELECTED) {
2964 ale->adt->flag |= ADT_UI_ACTIVE;
2965 }
2966
2967 return (ND_ANIMCHAN | NA_SELECTED);
2968 }
2969
click_select_channel_group(bAnimContext * ac,bAnimListElem * ale,const short selectmode,const int filter)2970 static int click_select_channel_group(bAnimContext *ac,
2971 bAnimListElem *ale,
2972 const short /* eEditKeyframes_Select or -1 */ selectmode,
2973 const int filter)
2974 {
2975 bActionGroup *agrp = (bActionGroup *)ale->data;
2976 Object *ob = NULL;
2977 bPoseChannel *pchan = NULL;
2978
2979 /* Armatures-Specific Feature:
2980 * Since groups are used to collect F-Curves of the same Bone by default
2981 * (via Keying Sets) so that they can be managed better, we try to make
2982 * things here easier for animators by mapping group selection to bone
2983 * selection.
2984 *
2985 * Only do this if "Only Selected" dopesheet filter is not active, or else it
2986 * becomes too unpredictable/tricky to manage
2987 */
2988 if ((ac->ads->filterflag & ADS_FILTER_ONLYSEL) == 0) {
2989 if ((ale->id) && (GS(ale->id->name) == ID_OB)) {
2990 ob = (Object *)ale->id;
2991
2992 if (ob->type == OB_ARMATURE) {
2993 /* Assume for now that any group with corresponding name is what we want
2994 * (i.e. for an armature whose location is animated, things would break
2995 * if the user were to add a bone named "Location").
2996 *
2997 * TODO: check the first F-Curve or so to be sure...
2998 */
2999 pchan = BKE_pose_channel_find_name(ob->pose, agrp->name);
3000 }
3001 }
3002 }
3003
3004 /* select/deselect group */
3005 if (selectmode == SELECT_INVERT) {
3006 /* inverse selection status of this group only */
3007 agrp->flag ^= AGRP_SELECTED;
3008 }
3009 else if (selectmode == -1) {
3010 /* select all in group (and deselect everything else) */
3011 FCurve *fcu;
3012
3013 /* deselect all other channels */
3014 ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_CLEAR);
3015 if (pchan) {
3016 ED_pose_deselect_all(ob, SEL_DESELECT, false);
3017 }
3018
3019 /* only select channels in group and group itself */
3020 for (fcu = agrp->channels.first; fcu && fcu->grp == agrp; fcu = fcu->next) {
3021 fcu->flag |= FCURVE_SELECTED;
3022 }
3023 agrp->flag |= AGRP_SELECTED;
3024 }
3025 else {
3026 /* select group by itself */
3027 ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_CLEAR);
3028 if (pchan) {
3029 ED_pose_deselect_all(ob, SEL_DESELECT, false);
3030 }
3031
3032 agrp->flag |= AGRP_SELECTED;
3033 }
3034
3035 /* if group is selected now, make group the 'active' one in the visible list */
3036 if (agrp->flag & AGRP_SELECTED) {
3037 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, agrp, ANIMTYPE_GROUP);
3038 if (pchan) {
3039 ED_pose_bone_select(ob, pchan, true);
3040 }
3041 }
3042 else {
3043 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, NULL, ANIMTYPE_GROUP);
3044 if (pchan) {
3045 ED_pose_bone_select(ob, pchan, false);
3046 }
3047 }
3048
3049 return (ND_ANIMCHAN | NA_SELECTED);
3050 }
3051
click_select_channel_fcurve(bAnimContext * ac,bAnimListElem * ale,const short selectmode,const int filter)3052 static int click_select_channel_fcurve(bAnimContext *ac,
3053 bAnimListElem *ale,
3054 const short /* eEditKeyframes_Select or -1 */ selectmode,
3055 const int filter)
3056 {
3057 FCurve *fcu = (FCurve *)ale->data;
3058
3059 /* select/deselect */
3060 if (selectmode == SELECT_INVERT) {
3061 /* inverse selection status of this F-Curve only */
3062 fcu->flag ^= FCURVE_SELECTED;
3063 }
3064 else {
3065 /* select F-Curve by itself */
3066 ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_CLEAR);
3067 fcu->flag |= FCURVE_SELECTED;
3068 }
3069
3070 /* if F-Curve is selected now, make F-Curve the 'active' one in the visible list */
3071 if (fcu->flag & FCURVE_SELECTED) {
3072 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, fcu, ale->type);
3073 }
3074
3075 return (ND_ANIMCHAN | NA_SELECTED);
3076 }
3077
click_select_channel_shapekey(bAnimContext * ac,bAnimListElem * ale,const short selectmode)3078 static int click_select_channel_shapekey(bAnimContext *ac,
3079 bAnimListElem *ale,
3080 const short /* eEditKeyframes_Select or -1 */ selectmode)
3081 {
3082 KeyBlock *kb = (KeyBlock *)ale->data;
3083
3084 /* select/deselect */
3085 if (selectmode == SELECT_INVERT) {
3086 /* inverse selection status of this ShapeKey only */
3087 kb->flag ^= KEYBLOCK_SEL;
3088 }
3089 else {
3090 /* select ShapeKey by itself */
3091 ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_CLEAR);
3092 kb->flag |= KEYBLOCK_SEL;
3093 }
3094
3095 return (ND_ANIMCHAN | NA_SELECTED);
3096 }
3097
click_select_channel_nlacontrols(bAnimListElem * ale)3098 static int click_select_channel_nlacontrols(bAnimListElem *ale)
3099 {
3100 AnimData *adt = (AnimData *)ale->data;
3101
3102 /* Toggle expand:
3103 * - Although the triangle widget already allows this,
3104 * since there's nothing else that can be done here now,
3105 * let's just use it for easier expand/collapse for now.
3106 */
3107 adt->flag ^= ADT_NLA_SKEYS_COLLAPSED;
3108
3109 return (ND_ANIMCHAN | NA_EDITED);
3110 }
3111
click_select_channel_gpdatablock(bAnimListElem * ale)3112 static int click_select_channel_gpdatablock(bAnimListElem *ale)
3113 {
3114 bGPdata *gpd = (bGPdata *)ale->data;
3115
3116 /* Toggle expand:
3117 * - Although the triangle widget already allows this,
3118 * the whole channel can also be used for this purpose.
3119 */
3120 gpd->flag ^= GP_DATA_EXPAND;
3121
3122 return (ND_ANIMCHAN | NA_EDITED);
3123 }
3124
click_select_channel_gplayer(bContext * C,bAnimContext * ac,bAnimListElem * ale,const short selectmode,const int filter)3125 static int click_select_channel_gplayer(bContext *C,
3126 bAnimContext *ac,
3127 bAnimListElem *ale,
3128 const short /* eEditKeyframes_Select or -1 */ selectmode,
3129 const int filter)
3130 {
3131 bGPdata *gpd = (bGPdata *)ale->id;
3132 bGPDlayer *gpl = (bGPDlayer *)ale->data;
3133
3134 /* select/deselect */
3135 if (selectmode == SELECT_INVERT) {
3136 /* invert selection status of this layer only */
3137 gpl->flag ^= GP_LAYER_SELECT;
3138 }
3139 else {
3140 /* select layer by itself */
3141 ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_CLEAR);
3142 gpl->flag |= GP_LAYER_SELECT;
3143 }
3144
3145 /* change active layer, if this is selected (since we must always have an active layer) */
3146 if (gpl->flag & GP_LAYER_SELECT) {
3147 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, gpl, ANIMTYPE_GPLAYER);
3148 /* update other layer status */
3149 BKE_gpencil_layer_active_set(gpd, gpl);
3150 BKE_gpencil_layer_autolock_set(gpd, false);
3151 DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
3152 }
3153
3154 /* Grease Pencil updates */
3155 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL);
3156 return (ND_ANIMCHAN | NA_EDITED); /* Animation Editors updates */
3157 }
3158
click_select_channel_maskdatablock(bAnimListElem * ale)3159 static int click_select_channel_maskdatablock(bAnimListElem *ale)
3160 {
3161 Mask *mask = (Mask *)ale->data;
3162
3163 /* Toggle expand
3164 * - Although the triangle widget already allows this,
3165 * the whole channel can also be used for this purpose.
3166 */
3167 mask->flag ^= MASK_ANIMF_EXPAND;
3168
3169 return (ND_ANIMCHAN | NA_EDITED);
3170 }
3171
click_select_channel_masklayer(bAnimContext * ac,bAnimListElem * ale,const short selectmode)3172 static int click_select_channel_masklayer(bAnimContext *ac,
3173 bAnimListElem *ale,
3174 const short /* eEditKeyframes_Select or -1 */ selectmode)
3175 {
3176 MaskLayer *masklay = (MaskLayer *)ale->data;
3177
3178 /* select/deselect */
3179 if (selectmode == SELECT_INVERT) {
3180 /* invert selection status of this layer only */
3181 masklay->flag ^= MASK_LAYERFLAG_SELECT;
3182 }
3183 else {
3184 /* select layer by itself */
3185 ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_CLEAR);
3186 masklay->flag |= MASK_LAYERFLAG_SELECT;
3187 }
3188
3189 return (ND_ANIMCHAN | NA_EDITED);
3190 }
3191
mouse_anim_channels(bContext * C,bAnimContext * ac,const int channel_index,const short selectmode)3192 static int mouse_anim_channels(bContext *C,
3193 bAnimContext *ac,
3194 const int channel_index,
3195 const short /* eEditKeyframes_Select or -1 */ selectmode)
3196 {
3197 ListBase anim_data = {NULL, NULL};
3198 bAnimListElem *ale;
3199 int filter;
3200 int notifierFlags = 0;
3201
3202 /* get the channel that was clicked on */
3203 /* filter channels */
3204 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
3205 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
3206
3207 /* get channel from index */
3208 ale = BLI_findlink(&anim_data, channel_index);
3209 if (ale == NULL) {
3210 /* channel not found */
3211 if (G.debug & G_DEBUG) {
3212 printf("Error: animation channel (index = %d) not found in mouse_anim_channels()\n",
3213 channel_index);
3214 }
3215
3216 ANIM_animdata_freelist(&anim_data);
3217 return 0;
3218 }
3219
3220 /* selectmode -1 is a special case for ActionGroups only,
3221 * which selects all of the channels underneath it only. */
3222 /* TODO: should this feature be extended to work with other channel types too? */
3223 if ((selectmode == -1) && (ale->type != ANIMTYPE_GROUP)) {
3224 /* normal channels should not behave normally in this case */
3225 ANIM_animdata_freelist(&anim_data);
3226 return 0;
3227 }
3228
3229 /* action to take depends on what channel we've got */
3230 /* WARNING: must keep this in sync with the equivalent function in nla_channels.c */
3231 switch (ale->type) {
3232 case ANIMTYPE_SCENE:
3233 notifierFlags |= click_select_channel_scene(ale, selectmode);
3234 break;
3235 case ANIMTYPE_OBJECT:
3236 notifierFlags |= click_select_channel_object(C, ac, ale, selectmode);
3237 break;
3238 case ANIMTYPE_FILLACTD: /* Action Expander */
3239 case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
3240 case ANIMTYPE_DSLAM:
3241 case ANIMTYPE_DSCAM:
3242 case ANIMTYPE_DSCACHEFILE:
3243 case ANIMTYPE_DSCUR:
3244 case ANIMTYPE_DSSKEY:
3245 case ANIMTYPE_DSWOR:
3246 case ANIMTYPE_DSPART:
3247 case ANIMTYPE_DSMBALL:
3248 case ANIMTYPE_DSARM:
3249 case ANIMTYPE_DSMESH:
3250 case ANIMTYPE_DSNTREE:
3251 case ANIMTYPE_DSTEX:
3252 case ANIMTYPE_DSLAT:
3253 case ANIMTYPE_DSLINESTYLE:
3254 case ANIMTYPE_DSSPK:
3255 case ANIMTYPE_DSGPENCIL:
3256 case ANIMTYPE_DSMCLIP:
3257 case ANIMTYPE_DSHAIR:
3258 case ANIMTYPE_DSPOINTCLOUD:
3259 case ANIMTYPE_DSVOLUME:
3260 case ANIMTYPE_DSSIMULATION:
3261 notifierFlags |= click_select_channel_dummy(ac, ale, selectmode);
3262 break;
3263 case ANIMTYPE_GROUP:
3264 notifierFlags |= click_select_channel_group(ac, ale, selectmode, filter);
3265 break;
3266 case ANIMTYPE_FCURVE:
3267 case ANIMTYPE_NLACURVE:
3268 notifierFlags |= click_select_channel_fcurve(ac, ale, selectmode, filter);
3269 break;
3270 case ANIMTYPE_SHAPEKEY:
3271 notifierFlags |= click_select_channel_shapekey(ac, ale, selectmode);
3272 break;
3273 case ANIMTYPE_NLACONTROLS:
3274 notifierFlags |= click_select_channel_nlacontrols(ale);
3275 break;
3276 case ANIMTYPE_GPDATABLOCK:
3277 notifierFlags |= click_select_channel_gpdatablock(ale);
3278 break;
3279 case ANIMTYPE_GPLAYER:
3280 notifierFlags |= click_select_channel_gplayer(C, ac, ale, selectmode, filter);
3281 break;
3282 case ANIMTYPE_MASKDATABLOCK:
3283 notifierFlags |= click_select_channel_maskdatablock(ale);
3284 break;
3285 case ANIMTYPE_MASKLAYER:
3286 notifierFlags |= click_select_channel_masklayer(ac, ale, selectmode);
3287 break;
3288 default:
3289 if (G.debug & G_DEBUG) {
3290 printf("Error: Invalid channel type in mouse_anim_channels()\n");
3291 }
3292 break;
3293 }
3294
3295 /* free channels */
3296 ANIM_animdata_freelist(&anim_data);
3297
3298 /* return notifier flags */
3299 return notifierFlags;
3300 }
3301
3302 /* ------------------- */
3303
3304 /* handle clicking */
animchannels_mouseclick_invoke(bContext * C,wmOperator * op,const wmEvent * event)3305 static int animchannels_mouseclick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
3306 {
3307 bAnimContext ac;
3308 ARegion *region;
3309 View2D *v2d;
3310 int channel_index;
3311 int notifierFlags = 0;
3312 short selectmode;
3313 float x, y;
3314
3315 /* get editor data */
3316 if (ANIM_animdata_get_context(C, &ac) == 0) {
3317 return OPERATOR_CANCELLED;
3318 }
3319
3320 /* get useful pointers from animation context data */
3321 region = ac.region;
3322 v2d = ®ion->v2d;
3323
3324 /* select mode is either replace (deselect all, then add) or add/extend */
3325 if (RNA_boolean_get(op->ptr, "extend")) {
3326 selectmode = SELECT_INVERT;
3327 }
3328 else if (RNA_boolean_get(op->ptr, "children_only")) {
3329 /* this is a bit of a special case for ActionGroups only...
3330 * should it be removed or extended to all instead? */
3331 selectmode = -1;
3332 }
3333 else {
3334 selectmode = SELECT_REPLACE;
3335 }
3336
3337 /* figure out which channel user clicked in */
3338 UI_view2d_region_to_view(v2d, event->mval[0], event->mval[1], &x, &y);
3339 UI_view2d_listview_view_to_cell(ACHANNEL_NAMEWIDTH,
3340 ACHANNEL_STEP(&ac),
3341 0,
3342 ACHANNEL_FIRST_TOP(&ac),
3343 x,
3344 y,
3345 NULL,
3346 &channel_index);
3347
3348 /* handle mouse-click in the relevant channel then */
3349 notifierFlags = mouse_anim_channels(C, &ac, channel_index, selectmode);
3350
3351 /* set notifier that things have changed */
3352 WM_event_add_notifier(C, NC_ANIMATION | notifierFlags, NULL);
3353
3354 return OPERATOR_FINISHED;
3355 }
3356
ANIM_OT_channels_click(wmOperatorType * ot)3357 static void ANIM_OT_channels_click(wmOperatorType *ot)
3358 {
3359 PropertyRNA *prop;
3360
3361 /* identifiers */
3362 ot->name = "Mouse Click on Channels";
3363 ot->idname = "ANIM_OT_channels_click";
3364 ot->description = "Handle mouse-clicks over animation channels";
3365
3366 /* api callbacks */
3367 ot->invoke = animchannels_mouseclick_invoke;
3368 ot->poll = animedit_poll_channels_active;
3369
3370 /* flags */
3371 ot->flag = OPTYPE_UNDO;
3372
3373 /* properties */
3374 /* NOTE: don't save settings, otherwise, can end up with some weird behavior (sticky extend) */
3375 prop = RNA_def_boolean(ot->srna, "extend", false, "Extend Select", ""); /* SHIFTKEY */
3376 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
3377
3378 prop = RNA_def_boolean(
3379 ot->srna, "children_only", false, "Select Children Only", ""); /* CTRLKEY|SHIFTKEY */
3380 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
3381 }
3382
select_anim_channel_keys(bAnimContext * ac,int channel_index,bool extend)3383 static bool select_anim_channel_keys(bAnimContext *ac, int channel_index, bool extend)
3384 {
3385 ListBase anim_data = {NULL, NULL};
3386 bAnimListElem *ale;
3387 int filter;
3388 bool success = false;
3389 FCurve *fcu;
3390 int i;
3391
3392 /* get the channel that was clicked on */
3393 /* filter channels */
3394 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
3395 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
3396
3397 /* get channel from index */
3398 ale = BLI_findlink(&anim_data, channel_index);
3399 if (ale == NULL) {
3400 /* channel not found */
3401 if (G.debug & G_DEBUG) {
3402 printf("Error: animation channel (index = %d) not found in rename_anim_channels()\n",
3403 channel_index);
3404 }
3405
3406 ANIM_animdata_freelist(&anim_data);
3407 return false;
3408 }
3409
3410 fcu = (FCurve *)ale->key_data;
3411 success = (fcu != NULL);
3412
3413 ANIM_animdata_freelist(&anim_data);
3414
3415 /* F-Curve may not have any keyframes */
3416 if (fcu && fcu->bezt) {
3417 BezTriple *bezt;
3418
3419 if (!extend) {
3420 filter = (ANIMFILTER_DATA_VISIBLE);
3421 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
3422 for (ale = anim_data.first; ale; ale = ale->next) {
3423 FCurve *fcu_inner = (FCurve *)ale->key_data;
3424
3425 if (fcu_inner != NULL && fcu_inner->bezt != NULL) {
3426 for (i = 0, bezt = fcu_inner->bezt; i < fcu_inner->totvert; i++, bezt++) {
3427 bezt->f2 = bezt->f1 = bezt->f3 = 0;
3428 }
3429 }
3430 }
3431
3432 ANIM_animdata_freelist(&anim_data);
3433 }
3434
3435 for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
3436 bezt->f2 = bezt->f1 = bezt->f3 = SELECT;
3437 }
3438 }
3439
3440 /* free temp data and tag for refresh */
3441 ED_region_tag_redraw(ac->region);
3442 return success;
3443 }
3444
animchannels_channel_select_keys_invoke(bContext * C,wmOperator * op,const wmEvent * event)3445 static int animchannels_channel_select_keys_invoke(bContext *C,
3446 wmOperator *op,
3447 const wmEvent *event)
3448 {
3449 bAnimContext ac;
3450 int channel_index;
3451 bool extend = RNA_boolean_get(op->ptr, "extend");
3452
3453 /* get editor data */
3454 if (ANIM_animdata_get_context(C, &ac) == 0) {
3455 return OPERATOR_CANCELLED;
3456 }
3457
3458 channel_index = animchannels_channel_get(&ac, event->mval);
3459
3460 /* handle click */
3461 if (select_anim_channel_keys(&ac, channel_index, extend)) {
3462 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
3463 return OPERATOR_FINISHED;
3464 }
3465
3466 /* allow event to be handled by selectall operator */
3467 return OPERATOR_PASS_THROUGH;
3468 }
3469
ANIM_OT_channel_select_keys(wmOperatorType * ot)3470 static void ANIM_OT_channel_select_keys(wmOperatorType *ot)
3471 {
3472 PropertyRNA *prop;
3473
3474 /* identifiers */
3475 ot->name = "Select Channel Keyframes";
3476 ot->idname = "ANIM_OT_channel_select_keys";
3477 ot->description = "Select all keyframes of channel under mouse";
3478
3479 /* api callbacks */
3480 ot->invoke = animchannels_channel_select_keys_invoke;
3481 ot->poll = animedit_poll_channels_active;
3482
3483 prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend selection");
3484 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
3485 }
3486
3487 /* ************************************************************************** */
3488 /* Operator Registration */
3489
ED_operatortypes_animchannels(void)3490 void ED_operatortypes_animchannels(void)
3491 {
3492 WM_operatortype_append(ANIM_OT_channels_select_all);
3493 WM_operatortype_append(ANIM_OT_channels_select_box);
3494
3495 WM_operatortype_append(ANIM_OT_channels_click);
3496 WM_operatortype_append(ANIM_OT_channel_select_keys);
3497 WM_operatortype_append(ANIM_OT_channels_rename);
3498
3499 WM_operatortype_append(ANIM_OT_channels_find);
3500
3501 WM_operatortype_append(ANIM_OT_channels_setting_enable);
3502 WM_operatortype_append(ANIM_OT_channels_setting_disable);
3503 WM_operatortype_append(ANIM_OT_channels_setting_toggle);
3504
3505 WM_operatortype_append(ANIM_OT_channels_delete);
3506
3507 /* XXX does this need to be a separate operator? */
3508 WM_operatortype_append(ANIM_OT_channels_editable_toggle);
3509
3510 WM_operatortype_append(ANIM_OT_channels_move);
3511
3512 WM_operatortype_append(ANIM_OT_channels_expand);
3513 WM_operatortype_append(ANIM_OT_channels_collapse);
3514
3515 WM_operatortype_append(ANIM_OT_channels_fcurves_enable);
3516
3517 WM_operatortype_append(ANIM_OT_channels_clean_empty);
3518
3519 WM_operatortype_append(ANIM_OT_channels_group);
3520 WM_operatortype_append(ANIM_OT_channels_ungroup);
3521 }
3522
3523 /* TODO: check on a poll callback for this, to get hotkeys into menus */
ED_keymap_animchannels(wmKeyConfig * keyconf)3524 void ED_keymap_animchannels(wmKeyConfig *keyconf)
3525 {
3526 WM_keymap_ensure(keyconf, "Animation Channels", 0, 0);
3527 }
3528
3529 /* ************************************************************************** */
3530