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) 2008 Blender Foundation
17 */
18
19 /** \file
20 * \ingroup spaction
21 */
22
23 #include <float.h>
24 #include <math.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include "MEM_guardedalloc.h"
29
30 #include "BLI_blenlib.h"
31 #include "BLI_dlrbTree.h"
32 #include "BLI_lasso_2d.h"
33 #include "BLI_utildefines.h"
34
35 #include "DNA_anim_types.h"
36 #include "DNA_gpencil_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_context.h"
45 #include "BKE_fcurve.h"
46 #include "BKE_gpencil.h"
47 #include "BKE_nla.h"
48
49 #include "UI_interface.h"
50 #include "UI_view2d.h"
51
52 #include "ED_anim_api.h"
53 #include "ED_gpencil.h"
54 #include "ED_keyframes_draw.h"
55 #include "ED_keyframes_edit.h"
56 #include "ED_markers.h"
57 #include "ED_mask.h"
58 #include "ED_screen.h"
59 #include "ED_select_utils.h"
60
61 #include "WM_api.h"
62 #include "WM_types.h"
63
64 #include "action_intern.h"
65
66 /* ************************************************************************** */
67 /* KEYFRAMES STUFF */
68
actkeys_find_list_element_at_position(bAnimContext * ac,int filter,float region_x,float region_y)69 static bAnimListElem *actkeys_find_list_element_at_position(bAnimContext *ac,
70 int filter,
71 float region_x,
72 float region_y)
73 {
74 View2D *v2d = &ac->region->v2d;
75
76 float view_x, view_y;
77 int channel_index;
78 UI_view2d_region_to_view(v2d, region_x, region_y, &view_x, &view_y);
79 UI_view2d_listview_view_to_cell(
80 0, ACHANNEL_STEP(ac), 0, ACHANNEL_FIRST_TOP(ac), view_x, view_y, NULL, &channel_index);
81
82 ListBase anim_data = {NULL, NULL};
83 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
84
85 bAnimListElem *ale = BLI_findlink(&anim_data, channel_index);
86 if (ale != NULL) {
87 BLI_remlink(&anim_data, ale);
88 ale->next = ale->prev = NULL;
89 }
90 ANIM_animdata_freelist(&anim_data);
91
92 return ale;
93 }
94
actkeys_list_element_to_keylist(bAnimContext * ac,DLRBT_Tree * anim_keys,bAnimListElem * ale)95 static void actkeys_list_element_to_keylist(bAnimContext *ac,
96 DLRBT_Tree *anim_keys,
97 bAnimListElem *ale)
98 {
99 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
100
101 bDopeSheet *ads = NULL;
102 if (ELEM(ac->datatype, ANIMCONT_DOPESHEET, ANIMCONT_TIMELINE)) {
103 ads = ac->data;
104 }
105
106 if (ale->key_data) {
107 switch (ale->datatype) {
108 case ALE_SCE: {
109 Scene *scene = (Scene *)ale->key_data;
110 scene_to_keylist(ads, scene, anim_keys, 0);
111 break;
112 }
113 case ALE_OB: {
114 Object *ob = (Object *)ale->key_data;
115 ob_to_keylist(ads, ob, anim_keys, 0);
116 break;
117 }
118 case ALE_ACT: {
119 bAction *act = (bAction *)ale->key_data;
120 action_to_keylist(adt, act, anim_keys, 0);
121 break;
122 }
123 case ALE_FCURVE: {
124 FCurve *fcu = (FCurve *)ale->key_data;
125 fcurve_to_keylist(adt, fcu, anim_keys, 0);
126 break;
127 }
128 }
129 }
130 else if (ale->type == ANIMTYPE_SUMMARY) {
131 /* dopesheet summary covers everything */
132 summary_to_keylist(ac, anim_keys, 0);
133 }
134 else if (ale->type == ANIMTYPE_GROUP) {
135 /* TODO: why don't we just give groups key_data too? */
136 bActionGroup *agrp = (bActionGroup *)ale->data;
137 agroup_to_keylist(adt, agrp, anim_keys, 0);
138 }
139 else if (ale->type == ANIMTYPE_GPLAYER) {
140 /* TODO: why don't we just give gplayers key_data too? */
141 bGPDlayer *gpl = (bGPDlayer *)ale->data;
142 gpl_to_keylist(ads, gpl, anim_keys);
143 }
144 else if (ale->type == ANIMTYPE_MASKLAYER) {
145 /* TODO: why don't we just give masklayers key_data too? */
146 MaskLayer *masklay = (MaskLayer *)ale->data;
147 mask_to_keylist(ads, masklay, anim_keys);
148 }
149 }
150
actkeys_find_key_in_list_element(bAnimContext * ac,bAnimListElem * ale,float region_x,float * r_selx,float * r_frame,bool * r_found,bool * r_is_selected)151 static void actkeys_find_key_in_list_element(bAnimContext *ac,
152 bAnimListElem *ale,
153 float region_x,
154 float *r_selx,
155 float *r_frame,
156 bool *r_found,
157 bool *r_is_selected)
158 {
159 *r_found = false;
160
161 View2D *v2d = &ac->region->v2d;
162
163 DLRBT_Tree anim_keys;
164 BLI_dlrbTree_init(&anim_keys);
165 actkeys_list_element_to_keylist(ac, &anim_keys, ale);
166
167 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
168
169 /* standard channel height (to allow for some slop) */
170 float key_hsize = ACHANNEL_HEIGHT(ac) * 0.8f;
171 /* half-size (for either side), but rounded up to nearest int (for easier targeting) */
172 key_hsize = roundf(key_hsize / 2.0f);
173
174 float xmin = UI_view2d_region_to_view_x(v2d, region_x - (int)key_hsize);
175 float xmax = UI_view2d_region_to_view_x(v2d, region_x + (int)key_hsize);
176
177 for (ActKeyColumn *ak = anim_keys.root; ak; ak = (ak->cfra < xmin) ? ak->right : ak->left) {
178 if (IN_RANGE(ak->cfra, xmin, xmax)) {
179 /* set the frame to use, and apply inverse-correction for NLA-mapping
180 * so that the frame will get selected by the selection functions without
181 * requiring to map each frame once again...
182 */
183 *r_selx = BKE_nla_tweakedit_remap(adt, ak->cfra, NLATIME_CONVERT_UNMAP);
184 *r_frame = ak->cfra;
185 *r_found = true;
186 *r_is_selected = (ak->sel & SELECT) != 0;
187 break;
188 }
189 }
190
191 /* cleanup temporary lists */
192 BLI_dlrbTree_free(&anim_keys);
193 }
194
actkeys_find_key_at_position(bAnimContext * ac,int filter,float region_x,float region_y,bAnimListElem ** r_ale,float * r_selx,float * r_frame,bool * r_found,bool * r_is_selected)195 static void actkeys_find_key_at_position(bAnimContext *ac,
196 int filter,
197 float region_x,
198 float region_y,
199 bAnimListElem **r_ale,
200 float *r_selx,
201 float *r_frame,
202 bool *r_found,
203 bool *r_is_selected)
204
205 {
206 *r_found = false;
207 *r_ale = actkeys_find_list_element_at_position(ac, filter, region_x, region_y);
208
209 if (*r_ale != NULL) {
210 actkeys_find_key_in_list_element(
211 ac, *r_ale, region_x, r_selx, r_frame, r_found, r_is_selected);
212 }
213 }
214
actkeys_is_key_at_position(bAnimContext * ac,float region_x,float region_y)215 static bool actkeys_is_key_at_position(bAnimContext *ac, float region_x, float region_y)
216 {
217 bAnimListElem *ale;
218 float selx, frame;
219 bool found;
220 bool is_selected;
221
222 int filter = ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS;
223 actkeys_find_key_at_position(
224 ac, filter, region_x, region_y, &ale, &selx, &frame, &found, &is_selected);
225
226 if (ale != NULL) {
227 MEM_freeN(ale);
228 }
229 return found;
230 }
231
232 /* ******************** Deselect All Operator ***************************** */
233 /* This operator works in one of three ways:
234 * 1) (de)select all (AKEY) - test if select all or deselect all
235 * 2) invert all (CTRL-IKEY) - invert selection of all keyframes
236 * 3) (de)select all - no testing is done; only for use internal tools as normal function...
237 */
238
239 /* Deselects keyframes in the action editor
240 * - This is called by the deselect all operator, as well as other ones!
241 *
242 * - test: check if select or deselect all
243 * - sel: how to select keyframes (SELECT_*)
244 */
deselect_action_keys(bAnimContext * ac,short test,short sel)245 static void deselect_action_keys(bAnimContext *ac, short test, short sel)
246 {
247 ListBase anim_data = {NULL, NULL};
248 bAnimListElem *ale;
249 int filter;
250
251 KeyframeEditData ked = {{NULL}};
252 KeyframeEditFunc test_cb, sel_cb;
253
254 /* determine type-based settings */
255 if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
256 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS);
257 }
258 else {
259 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/ |
260 ANIMFILTER_NODUPLIS);
261 }
262
263 /* filter data */
264 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
265
266 /* init BezTriple looping data */
267 test_cb = ANIM_editkeyframes_ok(BEZT_OK_SELECTED);
268
269 /* See if we should be selecting or deselecting */
270 if (test) {
271 for (ale = anim_data.first; ale; ale = ale->next) {
272 if (ale->type == ANIMTYPE_GPLAYER) {
273 if (ED_gpencil_layer_frame_select_check(ale->data)) {
274 sel = SELECT_SUBTRACT;
275 break;
276 }
277 }
278 else if (ale->type == ANIMTYPE_MASKLAYER) {
279 if (ED_masklayer_frame_select_check(ale->data)) {
280 sel = SELECT_SUBTRACT;
281 break;
282 }
283 }
284 else {
285 if (ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, test_cb, NULL)) {
286 sel = SELECT_SUBTRACT;
287 break;
288 }
289 }
290 }
291 }
292
293 /* convert sel to selectmode, and use that to get editor */
294 sel_cb = ANIM_editkeyframes_select(sel);
295
296 /* Now set the flags */
297 for (ale = anim_data.first; ale; ale = ale->next) {
298 if (ale->type == ANIMTYPE_GPLAYER) {
299 ED_gpencil_layer_frame_select_set(ale->data, sel);
300 ale->update |= ANIM_UPDATE_DEPS;
301 }
302 else if (ale->type == ANIMTYPE_MASKLAYER) {
303 ED_masklayer_frame_select_set(ale->data, sel);
304 }
305 else {
306 ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, sel_cb, NULL);
307 }
308 }
309
310 /* Cleanup */
311 ANIM_animdata_update(ac, &anim_data);
312 ANIM_animdata_freelist(&anim_data);
313 }
314
315 /* ------------------- */
316
actkeys_deselectall_exec(bContext * C,wmOperator * op)317 static int actkeys_deselectall_exec(bContext *C, wmOperator *op)
318 {
319 bAnimContext ac;
320
321 /* get editor data */
322 if (ANIM_animdata_get_context(C, &ac) == 0) {
323 return OPERATOR_CANCELLED;
324 }
325
326 /* 'standard' behavior - check if selected, then apply relevant selection */
327 const int action = RNA_enum_get(op->ptr, "action");
328 switch (action) {
329 case SEL_TOGGLE:
330 deselect_action_keys(&ac, 1, SELECT_ADD);
331 break;
332 case SEL_SELECT:
333 deselect_action_keys(&ac, 0, SELECT_ADD);
334 break;
335 case SEL_DESELECT:
336 deselect_action_keys(&ac, 0, SELECT_SUBTRACT);
337 break;
338 case SEL_INVERT:
339 deselect_action_keys(&ac, 0, SELECT_INVERT);
340 break;
341 default:
342 BLI_assert(0);
343 break;
344 }
345
346 /* set notifier that keyframe selection have changed */
347 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
348 if (ac.datatype == ANIMCONT_GPENCIL) {
349 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL);
350 }
351 return OPERATOR_FINISHED;
352 }
353
ACTION_OT_select_all(wmOperatorType * ot)354 void ACTION_OT_select_all(wmOperatorType *ot)
355 {
356 /* identifiers */
357 ot->name = "Select All";
358 ot->idname = "ACTION_OT_select_all";
359 ot->description = "Toggle selection of all keyframes";
360
361 /* api callbacks */
362 ot->exec = actkeys_deselectall_exec;
363 ot->poll = ED_operator_action_active;
364
365 /* flags */
366 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
367
368 /* properties */
369 WM_operator_properties_select_all(ot);
370 }
371
372 /* ******************** Box Select Operator **************************** */
373 /**
374 * This operator currently works in one of three ways:
375 * - BKEY - 1) all keyframes within region are selected #ACTKEYS_BORDERSEL_ALLKEYS.
376 * - ALT-BKEY - depending on which axis of the region was larger...
377 * - 2) x-axis, so select all frames within frame range #ACTKEYS_BORDERSEL_FRAMERANGE.
378 * - 3) y-axis, so select all frames within channels that region included
379 * #ACTKEYS_BORDERSEL_CHANNELS.
380 */
381
382 /* defines for box_select mode */
383 enum {
384 ACTKEYS_BORDERSEL_ALLKEYS = 0,
385 ACTKEYS_BORDERSEL_FRAMERANGE,
386 ACTKEYS_BORDERSEL_CHANNELS,
387 } /*eActKeys_BoxSelect_Mode*/;
388
389 typedef struct BoxSelectData {
390 bAnimContext *ac;
391 short selectmode;
392
393 KeyframeEditData ked;
394 KeyframeEditFunc ok_cb, select_cb;
395 } BoxSelectData;
396
box_select_elem(BoxSelectData * sel_data,bAnimListElem * ale,float xmin,float xmax,bool summary)397 static void box_select_elem(
398 BoxSelectData *sel_data, bAnimListElem *ale, float xmin, float xmax, bool summary)
399 {
400 bAnimContext *ac = sel_data->ac;
401
402 switch (ale->type) {
403 #if 0 /* XXX: Keyframes are not currently shown here */
404 case ANIMTYPE_GPDATABLOCK: {
405 bGPdata *gpd = ale->data;
406 bGPDlayer *gpl;
407 for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
408 ED_gpencil_layer_frames_select_box(gpl, xmin, xmax, data->selectmode);
409 }
410 ale->update |= ANIM_UPDATE_DEPS;
411 break;
412 }
413 #endif
414 case ANIMTYPE_GPLAYER: {
415 ED_gpencil_layer_frames_select_box(ale->data, xmin, xmax, sel_data->selectmode);
416 ale->update |= ANIM_UPDATE_DEPS;
417 break;
418 }
419 case ANIMTYPE_MASKDATABLOCK: {
420 Mask *mask = ale->data;
421 MaskLayer *masklay;
422 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
423 ED_masklayer_frames_select_box(masklay, xmin, xmax, sel_data->selectmode);
424 }
425 break;
426 }
427 case ANIMTYPE_MASKLAYER: {
428 ED_masklayer_frames_select_box(ale->data, xmin, xmax, sel_data->selectmode);
429 break;
430 }
431 default: {
432 if (summary) {
433 break;
434 }
435
436 if (ale->type == ANIMTYPE_SUMMARY && ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
437 ListBase anim_data = {NULL, NULL};
438 ANIM_animdata_filter(ac, &anim_data, ANIMFILTER_DATA_VISIBLE, ac->data, ac->datatype);
439
440 LISTBASE_FOREACH (bAnimListElem *, ale2, &anim_data) {
441 box_select_elem(sel_data, ale2, xmin, xmax, true);
442 }
443
444 ANIM_animdata_update(ac, &anim_data);
445 ANIM_animdata_freelist(&anim_data);
446 }
447
448 ANIM_animchannel_keyframes_loop(
449 &sel_data->ked, ac->ads, ale, sel_data->ok_cb, sel_data->select_cb, NULL);
450 }
451 }
452 }
453
box_select_action(bAnimContext * ac,const rcti rect,short mode,short selectmode)454 static void box_select_action(bAnimContext *ac, const rcti rect, short mode, short selectmode)
455 {
456 ListBase anim_data = {NULL, NULL};
457 bAnimListElem *ale;
458 int filter;
459
460 BoxSelectData sel_data = {.ac = ac, .selectmode = selectmode};
461 View2D *v2d = &ac->region->v2d;
462 rctf rectf;
463
464 /* Convert mouse coordinates to frame ranges and channel
465 * coordinates corrected for view pan/zoom. */
466 UI_view2d_region_to_view(v2d, rect.xmin, rect.ymin + 2, &rectf.xmin, &rectf.ymin);
467 UI_view2d_region_to_view(v2d, rect.xmax, rect.ymax - 2, &rectf.xmax, &rectf.ymax);
468
469 /* filter data */
470 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
471 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
472
473 /* get beztriple editing/validation funcs */
474 sel_data.select_cb = ANIM_editkeyframes_select(selectmode);
475
476 if (ELEM(mode, ACTKEYS_BORDERSEL_FRAMERANGE, ACTKEYS_BORDERSEL_ALLKEYS)) {
477 sel_data.ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAMERANGE);
478 }
479 else {
480 sel_data.ok_cb = NULL;
481 }
482
483 /* init editing data */
484 memset(&sel_data.ked, 0, sizeof(KeyframeEditData));
485
486 float ymax = ACHANNEL_FIRST_TOP(ac);
487
488 /* loop over data, doing box select */
489 for (ale = anim_data.first; ale; ale = ale->next, ymax -= ACHANNEL_STEP(ac)) {
490 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
491
492 /* get new vertical minimum extent of channel */
493 float ymin = ymax - ACHANNEL_STEP(ac);
494
495 /* set horizontal range (if applicable) */
496 if (ELEM(mode, ACTKEYS_BORDERSEL_FRAMERANGE, ACTKEYS_BORDERSEL_ALLKEYS)) {
497 /* if channel is mapped in NLA, apply correction */
498 if (adt) {
499 sel_data.ked.iterflags &= ~(KED_F1_NLA_UNMAP | KED_F2_NLA_UNMAP);
500 sel_data.ked.f1 = BKE_nla_tweakedit_remap(adt, rectf.xmin, NLATIME_CONVERT_UNMAP);
501 sel_data.ked.f2 = BKE_nla_tweakedit_remap(adt, rectf.xmax, NLATIME_CONVERT_UNMAP);
502 }
503 else {
504 sel_data.ked.iterflags |= (KED_F1_NLA_UNMAP | KED_F2_NLA_UNMAP); /* for summary tracks */
505 sel_data.ked.f1 = rectf.xmin;
506 sel_data.ked.f2 = rectf.xmax;
507 }
508 }
509
510 /* perform vertical suitability check (if applicable) */
511 if ((mode == ACTKEYS_BORDERSEL_FRAMERANGE) || !((ymax < rectf.ymin) || (ymin > rectf.ymax))) {
512 box_select_elem(&sel_data, ale, rectf.xmin, rectf.xmax, false);
513 }
514 }
515
516 /* cleanup */
517 ANIM_animdata_update(ac, &anim_data);
518 ANIM_animdata_freelist(&anim_data);
519 }
520
521 /* ------------------- */
522
actkeys_box_select_invoke(bContext * C,wmOperator * op,const wmEvent * event)523 static int actkeys_box_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
524 {
525 bAnimContext ac;
526 if (ANIM_animdata_get_context(C, &ac) == 0) {
527 return OPERATOR_CANCELLED;
528 }
529
530 bool tweak = RNA_boolean_get(op->ptr, "tweak");
531 if (tweak && actkeys_is_key_at_position(&ac, event->mval[0], event->mval[1])) {
532 return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
533 }
534
535 return WM_gesture_box_invoke(C, op, event);
536 }
537
actkeys_box_select_exec(bContext * C,wmOperator * op)538 static int actkeys_box_select_exec(bContext *C, wmOperator *op)
539 {
540 bAnimContext ac;
541 rcti rect;
542 short mode = 0;
543
544 /* get editor data */
545 if (ANIM_animdata_get_context(C, &ac) == 0) {
546 return OPERATOR_CANCELLED;
547 }
548
549 const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
550 const int selectmode = (sel_op != SEL_OP_SUB) ? SELECT_ADD : SELECT_SUBTRACT;
551 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
552 deselect_action_keys(&ac, 1, SELECT_SUBTRACT);
553 }
554
555 /* get settings from operator */
556 WM_operator_properties_border_to_rcti(op, &rect);
557
558 /* selection 'mode' depends on whether box_select region only matters on one axis */
559 if (RNA_boolean_get(op->ptr, "axis_range")) {
560 /* Mode depends on which axis of the range is larger to determine which axis to use:
561 * - checking this in region-space is fine,
562 * as it's fundamentally still going to be a different rect size.
563 * - the frame-range select option is favored over the channel one (x over y),
564 * as frame-range one is often used for tweaking timing when "blocking",
565 * while channels is not that useful...
566 */
567 if (BLI_rcti_size_x(&rect) >= BLI_rcti_size_y(&rect)) {
568 mode = ACTKEYS_BORDERSEL_FRAMERANGE;
569 }
570 else {
571 mode = ACTKEYS_BORDERSEL_CHANNELS;
572 }
573 }
574 else {
575 mode = ACTKEYS_BORDERSEL_ALLKEYS;
576 }
577
578 /* apply box_select action */
579 box_select_action(&ac, rect, mode, selectmode);
580
581 /* set notifier that keyframe selection have changed */
582 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
583 if (ac.datatype == ANIMCONT_GPENCIL) {
584 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL);
585 }
586 return OPERATOR_FINISHED;
587 }
588
ACTION_OT_select_box(wmOperatorType * ot)589 void ACTION_OT_select_box(wmOperatorType *ot)
590 {
591 /* identifiers */
592 ot->name = "Box Select";
593 ot->idname = "ACTION_OT_select_box";
594 ot->description = "Select all keyframes within the specified region";
595
596 /* api callbacks */
597 ot->invoke = actkeys_box_select_invoke;
598 ot->exec = actkeys_box_select_exec;
599 ot->modal = WM_gesture_box_modal;
600 ot->cancel = WM_gesture_box_cancel;
601
602 ot->poll = ED_operator_action_active;
603
604 /* flags */
605 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
606
607 /* rna */
608 ot->prop = RNA_def_boolean(ot->srna, "axis_range", 0, "Axis Range", "");
609
610 /* properties */
611 WM_operator_properties_gesture_box(ot);
612 WM_operator_properties_select_operation_simple(ot);
613
614 PropertyRNA *prop = RNA_def_boolean(
615 ot->srna, "tweak", 0, "Tweak", "Operator has been activated using a tweak event");
616 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
617 }
618
619 /* ******************** Region Select Operators ***************************** */
620 /* "Region Select" operators include the Lasso and Circle Select operators.
621 * These two ended up being lumped together, as it was easier in the
622 * original Graph Editor implementation of these to do it this way.
623 */
624
625 typedef struct RegionSelectData {
626 bAnimContext *ac;
627 short mode;
628 short selectmode;
629
630 KeyframeEditData ked;
631 KeyframeEditFunc ok_cb, select_cb;
632 } RegionSelectData;
633
region_select_elem(RegionSelectData * sel_data,bAnimListElem * ale,bool summary)634 static void region_select_elem(RegionSelectData *sel_data, bAnimListElem *ale, bool summary)
635 {
636 bAnimContext *ac = sel_data->ac;
637
638 switch (ale->type) {
639 #if 0 /* XXX: Keyframes are not currently shown here */
640 case ANIMTYPE_GPDATABLOCK: {
641 bGPdata *gpd = ale->data;
642 bGPDlayer *gpl;
643 for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
644 ED_gpencil_layer_frames_select_region(&rdata->ked, ale->data, rdata->mode, rdata->selectmode);
645 }
646 break;
647 }
648 #endif
649 case ANIMTYPE_GPLAYER: {
650 ED_gpencil_layer_frames_select_region(
651 &sel_data->ked, ale->data, sel_data->mode, sel_data->selectmode);
652 ale->update |= ANIM_UPDATE_DEPS;
653 break;
654 }
655 case ANIMTYPE_MASKDATABLOCK: {
656 Mask *mask = ale->data;
657 MaskLayer *masklay;
658 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
659 ED_masklayer_frames_select_region(
660 &sel_data->ked, masklay, sel_data->mode, sel_data->selectmode);
661 }
662 break;
663 }
664 case ANIMTYPE_MASKLAYER: {
665 ED_masklayer_frames_select_region(
666 &sel_data->ked, ale->data, sel_data->mode, sel_data->selectmode);
667 break;
668 }
669 default: {
670 if (summary) {
671 break;
672 }
673
674 if (ale->type == ANIMTYPE_SUMMARY && ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
675 ListBase anim_data = {NULL, NULL};
676 ANIM_animdata_filter(ac, &anim_data, ANIMFILTER_DATA_VISIBLE, ac->data, ac->datatype);
677
678 LISTBASE_FOREACH (bAnimListElem *, ale2, &anim_data) {
679 region_select_elem(sel_data, ale2, true);
680 }
681
682 ANIM_animdata_update(ac, &anim_data);
683 ANIM_animdata_freelist(&anim_data);
684 }
685
686 ANIM_animchannel_keyframes_loop(
687 &sel_data->ked, ac->ads, ale, sel_data->ok_cb, sel_data->select_cb, NULL);
688 }
689 }
690 }
691
region_select_action_keys(bAnimContext * ac,const rctf * rectf_view,short mode,short selectmode,void * data)692 static void region_select_action_keys(
693 bAnimContext *ac, const rctf *rectf_view, short mode, short selectmode, void *data)
694 {
695 ListBase anim_data = {NULL, NULL};
696 bAnimListElem *ale;
697 int filter;
698
699 RegionSelectData sel_data = {.ac = ac, .mode = mode, .selectmode = selectmode};
700 View2D *v2d = &ac->region->v2d;
701 rctf rectf, scaled_rectf;
702
703 /* Convert mouse coordinates to frame ranges and channel
704 * coordinates corrected for view pan/zoom. */
705 UI_view2d_region_to_view_rctf(v2d, rectf_view, &rectf);
706
707 /* filter data */
708 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
709 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
710
711 /* get beztriple editing/validation funcs */
712 sel_data.select_cb = ANIM_editkeyframes_select(selectmode);
713 sel_data.ok_cb = ANIM_editkeyframes_ok(mode);
714
715 /* init editing data */
716 memset(&sel_data.ked, 0, sizeof(KeyframeEditData));
717 if (mode == BEZT_OK_CHANNEL_LASSO) {
718 KeyframeEdit_LassoData *data_lasso = data;
719 data_lasso->rectf_scaled = &scaled_rectf;
720 sel_data.ked.data = data_lasso;
721 }
722 else if (mode == BEZT_OK_CHANNEL_CIRCLE) {
723 KeyframeEdit_CircleData *data_circle = data;
724 data_circle->rectf_scaled = &scaled_rectf;
725 sel_data.ked.data = data;
726 }
727 else {
728 sel_data.ked.data = &scaled_rectf;
729 }
730
731 float ymax = ACHANNEL_FIRST_TOP(ac);
732
733 /* loop over data, doing region select */
734 for (ale = anim_data.first; ale; ale = ale->next, ymax -= ACHANNEL_STEP(ac)) {
735 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
736
737 /* get new vertical minimum extent of channel */
738 float ymin = ymax - ACHANNEL_STEP(ac);
739
740 /* compute midpoint of channel (used for testing if the key is in the region or not) */
741 sel_data.ked.channel_y = (ymin + ymax) / 2.0f;
742
743 /* if channel is mapped in NLA, apply correction
744 * - Apply to the bounds being checked, not all the keyframe points,
745 * to avoid having scaling everything
746 * - Save result to the scaled_rect, which is all that these operators
747 * will read from
748 */
749 if (adt) {
750 sel_data.ked.iterflags &= ~(KED_F1_NLA_UNMAP | KED_F2_NLA_UNMAP);
751 sel_data.ked.f1 = BKE_nla_tweakedit_remap(adt, rectf.xmin, NLATIME_CONVERT_UNMAP);
752 sel_data.ked.f2 = BKE_nla_tweakedit_remap(adt, rectf.xmax, NLATIME_CONVERT_UNMAP);
753 }
754 else {
755 sel_data.ked.iterflags |= (KED_F1_NLA_UNMAP | KED_F2_NLA_UNMAP); /* for summary tracks */
756 sel_data.ked.f1 = rectf.xmin;
757 sel_data.ked.f2 = rectf.xmax;
758 }
759
760 /* Update values for scaled_rectf - which is used to compute the mapping in the callbacks
761 * NOTE: Since summary tracks need late-binding remapping, the callbacks may overwrite these
762 * with the properly remapped ked.f1/f2 values, when needed
763 */
764 scaled_rectf.xmin = sel_data.ked.f1;
765 scaled_rectf.xmax = sel_data.ked.f2;
766 scaled_rectf.ymin = ymin;
767 scaled_rectf.ymax = ymax;
768
769 /* perform vertical suitability check (if applicable) */
770 if ((mode == ACTKEYS_BORDERSEL_FRAMERANGE) || !((ymax < rectf.ymin) || (ymin > rectf.ymax))) {
771 region_select_elem(&sel_data, ale, false);
772 }
773 }
774
775 /* cleanup */
776 ANIM_animdata_update(ac, &anim_data);
777 ANIM_animdata_freelist(&anim_data);
778 }
779
780 /* ----------------------------------- */
781
actkeys_lassoselect_exec(bContext * C,wmOperator * op)782 static int actkeys_lassoselect_exec(bContext *C, wmOperator *op)
783 {
784 bAnimContext ac;
785
786 KeyframeEdit_LassoData data_lasso;
787 rcti rect;
788 rctf rect_fl;
789
790 /* get editor data */
791 if (ANIM_animdata_get_context(C, &ac) == 0) {
792 return OPERATOR_CANCELLED;
793 }
794
795 data_lasso.rectf_view = &rect_fl;
796 data_lasso.mcoords = WM_gesture_lasso_path_to_array(C, op, &data_lasso.mcoords_len);
797 if (data_lasso.mcoords == NULL) {
798 return OPERATOR_CANCELLED;
799 }
800
801 const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
802 const int selectmode = (sel_op != SEL_OP_SUB) ? SELECT_ADD : SELECT_SUBTRACT;
803 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
804 deselect_action_keys(&ac, 1, SELECT_SUBTRACT);
805 }
806
807 /* get settings from operator */
808 BLI_lasso_boundbox(&rect, data_lasso.mcoords, data_lasso.mcoords_len);
809 BLI_rctf_rcti_copy(&rect_fl, &rect);
810
811 /* apply box_select action */
812 region_select_action_keys(&ac, &rect_fl, BEZT_OK_CHANNEL_LASSO, selectmode, &data_lasso);
813
814 MEM_freeN((void *)data_lasso.mcoords);
815
816 /* send notifier that keyframe selection has changed */
817 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
818 if (ac.datatype == ANIMCONT_GPENCIL) {
819 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL);
820 }
821 return OPERATOR_FINISHED;
822 }
823
ACTION_OT_select_lasso(wmOperatorType * ot)824 void ACTION_OT_select_lasso(wmOperatorType *ot)
825 {
826 /* identifiers */
827 ot->name = "Lasso Select";
828 ot->description = "Select keyframe points using lasso selection";
829 ot->idname = "ACTION_OT_select_lasso";
830
831 /* api callbacks */
832 ot->invoke = WM_gesture_lasso_invoke;
833 ot->modal = WM_gesture_lasso_modal;
834 ot->exec = actkeys_lassoselect_exec;
835 ot->poll = ED_operator_action_active;
836 ot->cancel = WM_gesture_lasso_cancel;
837
838 /* flags */
839 ot->flag = OPTYPE_UNDO;
840
841 /* properties */
842 WM_operator_properties_gesture_lasso(ot);
843 WM_operator_properties_select_operation_simple(ot);
844 }
845
846 /* ------------------- */
847
action_circle_select_exec(bContext * C,wmOperator * op)848 static int action_circle_select_exec(bContext *C, wmOperator *op)
849 {
850 bAnimContext ac;
851
852 KeyframeEdit_CircleData data = {0};
853 rctf rect_fl;
854
855 float x = RNA_int_get(op->ptr, "x");
856 float y = RNA_int_get(op->ptr, "y");
857 float radius = RNA_int_get(op->ptr, "radius");
858
859 /* get editor data */
860 if (ANIM_animdata_get_context(C, &ac) == 0) {
861 return OPERATOR_CANCELLED;
862 }
863
864 const eSelectOp sel_op = ED_select_op_modal(RNA_enum_get(op->ptr, "mode"),
865 WM_gesture_is_modal_first(op->customdata));
866 const short selectmode = (sel_op != SEL_OP_SUB) ? SELECT_ADD : SELECT_SUBTRACT;
867 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
868 deselect_action_keys(&ac, 0, SELECT_SUBTRACT);
869 }
870
871 data.mval[0] = x;
872 data.mval[1] = y;
873 data.radius_squared = radius * radius;
874 data.rectf_view = &rect_fl;
875
876 rect_fl.xmin = x - radius;
877 rect_fl.xmax = x + radius;
878 rect_fl.ymin = y - radius;
879 rect_fl.ymax = y + radius;
880
881 /* apply region select action */
882 region_select_action_keys(&ac, &rect_fl, BEZT_OK_CHANNEL_CIRCLE, selectmode, &data);
883
884 /* send notifier that keyframe selection has changed */
885 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
886 if (ac.datatype == ANIMCONT_GPENCIL) {
887 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL);
888 }
889 return OPERATOR_FINISHED;
890 }
891
ACTION_OT_select_circle(wmOperatorType * ot)892 void ACTION_OT_select_circle(wmOperatorType *ot)
893 {
894 ot->name = "Circle Select";
895 ot->description = "Select keyframe points using circle selection";
896 ot->idname = "ACTION_OT_select_circle";
897
898 ot->invoke = WM_gesture_circle_invoke;
899 ot->modal = WM_gesture_circle_modal;
900 ot->exec = action_circle_select_exec;
901 ot->poll = ED_operator_action_active;
902 ot->cancel = WM_gesture_circle_cancel;
903
904 /* flags */
905 ot->flag = OPTYPE_UNDO;
906
907 /* properties */
908 WM_operator_properties_gesture_circle(ot);
909 WM_operator_properties_select_operation_simple(ot);
910 }
911
912 /* ******************** Column Select Operator **************************** */
913 /* This operator works in one of four ways:
914 * - 1) select all keyframes in the same frame as a selected one (KKEY)
915 * - 2) select all keyframes in the same frame as the current frame marker (CTRL-KKEY)
916 * - 3) select all keyframes in the same frame as a selected markers (SHIFT-KKEY)
917 * - 4) select all keyframes that occur between selected markers (ALT-KKEY)
918 */
919
920 /* defines for column-select mode */
921 static const EnumPropertyItem prop_column_select_types[] = {
922 {ACTKEYS_COLUMNSEL_KEYS, "KEYS", 0, "On Selected Keyframes", ""},
923 {ACTKEYS_COLUMNSEL_CFRA, "CFRA", 0, "On Current Frame", ""},
924 {ACTKEYS_COLUMNSEL_MARKERS_COLUMN, "MARKERS_COLUMN", 0, "On Selected Markers", ""},
925 {ACTKEYS_COLUMNSEL_MARKERS_BETWEEN,
926 "MARKERS_BETWEEN",
927 0,
928 "Between Min/Max Selected Markers",
929 ""},
930 {0, NULL, 0, NULL, NULL},
931 };
932
933 /* ------------------- */
934
935 /* Selects all visible keyframes between the specified markers */
936 /* TODO, this is almost an _exact_ duplicate of a function of the same name in graph_select.c
937 * should de-duplicate - campbell */
markers_selectkeys_between(bAnimContext * ac)938 static void markers_selectkeys_between(bAnimContext *ac)
939 {
940 ListBase anim_data = {NULL, NULL};
941 bAnimListElem *ale;
942 int filter;
943
944 KeyframeEditFunc ok_cb, select_cb;
945 KeyframeEditData ked = {{NULL}};
946 float min, max;
947
948 /* get extreme markers */
949 ED_markers_get_minmax(ac->markers, 1, &min, &max);
950 min -= 0.5f;
951 max += 0.5f;
952
953 /* get editing funcs + data */
954 ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAMERANGE);
955 select_cb = ANIM_editkeyframes_select(SELECT_ADD);
956
957 ked.f1 = min;
958 ked.f2 = max;
959
960 /* filter data */
961 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY */ |
962 ANIMFILTER_NODUPLIS);
963 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
964
965 /* select keys in-between */
966 for (ale = anim_data.first; ale; ale = ale->next) {
967 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
968
969 if (adt) {
970 ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1);
971 ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
972 ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
973 }
974 else if (ale->type == ANIMTYPE_GPLAYER) {
975 ED_gpencil_layer_frames_select_box(ale->data, min, max, SELECT_ADD);
976 ale->update |= ANIM_UPDATE_DEPS;
977 }
978 else if (ale->type == ANIMTYPE_MASKLAYER) {
979 ED_masklayer_frames_select_box(ale->data, min, max, SELECT_ADD);
980 }
981 else {
982 ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
983 }
984 }
985
986 /* Cleanup */
987 ANIM_animdata_update(ac, &anim_data);
988 ANIM_animdata_freelist(&anim_data);
989 }
990
991 /* Selects all visible keyframes in the same frames as the specified elements */
columnselect_action_keys(bAnimContext * ac,short mode)992 static void columnselect_action_keys(bAnimContext *ac, short mode)
993 {
994 ListBase anim_data = {NULL, NULL};
995 bAnimListElem *ale;
996 int filter;
997
998 Scene *scene = ac->scene;
999 CfraElem *ce;
1000 KeyframeEditFunc select_cb, ok_cb;
1001 KeyframeEditData ked = {{NULL}};
1002
1003 /* build list of columns */
1004 switch (mode) {
1005 case ACTKEYS_COLUMNSEL_KEYS: /* list of selected keys */
1006 if (ac->datatype == ANIMCONT_GPENCIL) {
1007 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE);
1008 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1009
1010 for (ale = anim_data.first; ale; ale = ale->next) {
1011 ED_gpencil_layer_make_cfra_list(ale->data, &ked.list, 1);
1012 }
1013 }
1014 else {
1015 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/);
1016 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1017
1018 for (ale = anim_data.first; ale; ale = ale->next) {
1019 ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, bezt_to_cfraelem, NULL);
1020 }
1021 }
1022 ANIM_animdata_freelist(&anim_data);
1023 break;
1024
1025 case ACTKEYS_COLUMNSEL_CFRA: /* current frame */
1026 /* make a single CfraElem for storing this */
1027 ce = MEM_callocN(sizeof(CfraElem), "cfraElem");
1028 BLI_addtail(&ked.list, ce);
1029
1030 ce->cfra = (float)CFRA;
1031 break;
1032
1033 case ACTKEYS_COLUMNSEL_MARKERS_COLUMN: /* list of selected markers */
1034 ED_markers_make_cfra_list(ac->markers, &ked.list, SELECT);
1035 break;
1036
1037 default: /* invalid option */
1038 return;
1039 }
1040
1041 /* set up BezTriple edit callbacks */
1042 select_cb = ANIM_editkeyframes_select(SELECT_ADD);
1043 ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAME);
1044
1045 /* loop through all of the keys and select additional keyframes
1046 * based on the keys found to be selected above
1047 */
1048 if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
1049 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE);
1050 }
1051 else {
1052 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/);
1053 }
1054 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1055
1056 for (ale = anim_data.first; ale; ale = ale->next) {
1057 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
1058
1059 /* loop over cfraelems (stored in the KeyframeEditData->list)
1060 * - we need to do this here, as we can apply fewer NLA-mapping conversions
1061 */
1062 for (ce = ked.list.first; ce; ce = ce->next) {
1063 /* set frame for validation callback to refer to */
1064 if (adt) {
1065 ked.f1 = BKE_nla_tweakedit_remap(adt, ce->cfra, NLATIME_CONVERT_UNMAP);
1066 }
1067 else {
1068 ked.f1 = ce->cfra;
1069 }
1070
1071 /* select elements with frame number matching cfraelem */
1072 if (ale->type == ANIMTYPE_GPLAYER) {
1073 ED_gpencil_select_frame(ale->data, ce->cfra, SELECT_ADD);
1074 ale->update |= ANIM_UPDATE_DEPS;
1075 }
1076 else if (ale->type == ANIMTYPE_MASKLAYER) {
1077 ED_mask_select_frame(ale->data, ce->cfra, SELECT_ADD);
1078 }
1079 else {
1080 ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
1081 }
1082 }
1083 }
1084
1085 /* free elements */
1086 BLI_freelistN(&ked.list);
1087
1088 ANIM_animdata_update(ac, &anim_data);
1089 ANIM_animdata_freelist(&anim_data);
1090 }
1091
1092 /* ------------------- */
1093
actkeys_columnselect_exec(bContext * C,wmOperator * op)1094 static int actkeys_columnselect_exec(bContext *C, wmOperator *op)
1095 {
1096 bAnimContext ac;
1097 short mode;
1098
1099 /* get editor data */
1100 if (ANIM_animdata_get_context(C, &ac) == 0) {
1101 return OPERATOR_CANCELLED;
1102 }
1103
1104 /* action to take depends on the mode */
1105 mode = RNA_enum_get(op->ptr, "mode");
1106
1107 if (mode == ACTKEYS_COLUMNSEL_MARKERS_BETWEEN) {
1108 markers_selectkeys_between(&ac);
1109 }
1110 else {
1111 columnselect_action_keys(&ac, mode);
1112 }
1113
1114 /* set notifier that keyframe selection have changed */
1115 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
1116 if (ac.datatype == ANIMCONT_GPENCIL) {
1117 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL);
1118 }
1119 return OPERATOR_FINISHED;
1120 }
1121
ACTION_OT_select_column(wmOperatorType * ot)1122 void ACTION_OT_select_column(wmOperatorType *ot)
1123 {
1124 /* identifiers */
1125 ot->name = "Select All";
1126 ot->idname = "ACTION_OT_select_column";
1127 ot->description = "Select all keyframes on the specified frame(s)";
1128
1129 /* api callbacks */
1130 ot->exec = actkeys_columnselect_exec;
1131 ot->poll = ED_operator_action_active;
1132
1133 /* flags */
1134 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1135
1136 /* props */
1137 ot->prop = RNA_def_enum(ot->srna, "mode", prop_column_select_types, 0, "Mode", "");
1138 }
1139
1140 /* ******************** Select Linked Operator *********************** */
1141
actkeys_select_linked_exec(bContext * C,wmOperator * UNUSED (op))1142 static int actkeys_select_linked_exec(bContext *C, wmOperator *UNUSED(op))
1143 {
1144 bAnimContext ac;
1145
1146 ListBase anim_data = {NULL, NULL};
1147 bAnimListElem *ale;
1148 int filter;
1149
1150 KeyframeEditFunc ok_cb = ANIM_editkeyframes_ok(BEZT_OK_SELECTED);
1151 KeyframeEditFunc sel_cb = ANIM_editkeyframes_select(SELECT_ADD);
1152
1153 /* get editor data */
1154 if (ANIM_animdata_get_context(C, &ac) == 0) {
1155 return OPERATOR_CANCELLED;
1156 }
1157
1158 /* loop through all of the keys and select additional keyframes based on these */
1159 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/ |
1160 ANIMFILTER_NODUPLIS);
1161 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1162
1163 for (ale = anim_data.first; ale; ale = ale->next) {
1164 FCurve *fcu = (FCurve *)ale->key_data;
1165
1166 /* check if anything selected? */
1167 if (ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, ok_cb, NULL)) {
1168 /* select every keyframe in this curve then */
1169 ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, sel_cb, NULL);
1170 }
1171 }
1172
1173 /* Cleanup */
1174 ANIM_animdata_freelist(&anim_data);
1175
1176 /* set notifier that keyframe selection has changed */
1177 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
1178 if (ac.datatype == ANIMCONT_GPENCIL) {
1179 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL);
1180 }
1181 return OPERATOR_FINISHED;
1182 }
1183
ACTION_OT_select_linked(wmOperatorType * ot)1184 void ACTION_OT_select_linked(wmOperatorType *ot)
1185 {
1186 /* identifiers */
1187 ot->name = "Select Linked";
1188 ot->idname = "ACTION_OT_select_linked";
1189 ot->description = "Select keyframes occurring in the same F-Curves as selected ones";
1190
1191 /* api callbacks */
1192 ot->exec = actkeys_select_linked_exec;
1193 ot->poll = ED_operator_action_active;
1194
1195 /* flags */
1196 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1197 }
1198
1199 /* ******************** Select More/Less Operators *********************** */
1200
1201 /* Common code to perform selection */
select_moreless_action_keys(bAnimContext * ac,short mode)1202 static void select_moreless_action_keys(bAnimContext *ac, short mode)
1203 {
1204 ListBase anim_data = {NULL, NULL};
1205 bAnimListElem *ale;
1206 int filter;
1207
1208 KeyframeEditData ked = {{NULL}};
1209 KeyframeEditFunc build_cb;
1210
1211 /* init selmap building data */
1212 build_cb = ANIM_editkeyframes_buildselmap(mode);
1213
1214 /* loop through all of the keys and select additional keyframes based on these */
1215 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/ |
1216 ANIMFILTER_NODUPLIS);
1217 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1218
1219 for (ale = anim_data.first; ale; ale = ale->next) {
1220
1221 /* TODO: other types. */
1222 if (ale->datatype != ALE_FCURVE) {
1223 continue;
1224 }
1225
1226 /* only continue if F-Curve has keyframes */
1227 FCurve *fcu = (FCurve *)ale->key_data;
1228 if (fcu->bezt == NULL) {
1229 continue;
1230 }
1231
1232 /* build up map of whether F-Curve's keyframes should be selected or not */
1233 ked.data = MEM_callocN(fcu->totvert, "selmap actEdit more");
1234 ANIM_fcurve_keyframes_loop(&ked, fcu, NULL, build_cb, NULL);
1235
1236 /* based on this map, adjust the selection status of the keyframes */
1237 ANIM_fcurve_keyframes_loop(&ked, fcu, NULL, bezt_selmap_flush, NULL);
1238
1239 /* free the selmap used here */
1240 MEM_freeN(ked.data);
1241 ked.data = NULL;
1242 }
1243
1244 /* Cleanup */
1245 ANIM_animdata_freelist(&anim_data);
1246 }
1247
1248 /* ----------------- */
1249
actkeys_select_more_exec(bContext * C,wmOperator * UNUSED (op))1250 static int actkeys_select_more_exec(bContext *C, wmOperator *UNUSED(op))
1251 {
1252 bAnimContext ac;
1253
1254 /* get editor data */
1255 if (ANIM_animdata_get_context(C, &ac) == 0) {
1256 return OPERATOR_CANCELLED;
1257 }
1258
1259 /* perform select changes */
1260 select_moreless_action_keys(&ac, SELMAP_MORE);
1261
1262 /* set notifier that keyframe selection has changed */
1263 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
1264 if (ac.datatype == ANIMCONT_GPENCIL) {
1265 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL);
1266 }
1267 return OPERATOR_FINISHED;
1268 }
1269
ACTION_OT_select_more(wmOperatorType * ot)1270 void ACTION_OT_select_more(wmOperatorType *ot)
1271 {
1272 /* identifiers */
1273 ot->name = "Select More";
1274 ot->idname = "ACTION_OT_select_more";
1275 ot->description = "Select keyframes beside already selected ones";
1276
1277 /* api callbacks */
1278 ot->exec = actkeys_select_more_exec;
1279 ot->poll = ED_operator_action_active;
1280
1281 /* flags */
1282 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1283 }
1284
1285 /* ----------------- */
1286
actkeys_select_less_exec(bContext * C,wmOperator * UNUSED (op))1287 static int actkeys_select_less_exec(bContext *C, wmOperator *UNUSED(op))
1288 {
1289 bAnimContext ac;
1290
1291 /* get editor data */
1292 if (ANIM_animdata_get_context(C, &ac) == 0) {
1293 return OPERATOR_CANCELLED;
1294 }
1295
1296 /* perform select changes */
1297 select_moreless_action_keys(&ac, SELMAP_LESS);
1298
1299 /* set notifier that keyframe selection has changed */
1300 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
1301 if (ac.datatype == ANIMCONT_GPENCIL) {
1302 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL);
1303 }
1304 return OPERATOR_FINISHED;
1305 }
1306
ACTION_OT_select_less(wmOperatorType * ot)1307 void ACTION_OT_select_less(wmOperatorType *ot)
1308 {
1309 /* identifiers */
1310 ot->name = "Select Less";
1311 ot->idname = "ACTION_OT_select_less";
1312 ot->description = "Deselect keyframes on ends of selection islands";
1313
1314 /* api callbacks */
1315 ot->exec = actkeys_select_less_exec;
1316 ot->poll = ED_operator_action_active;
1317
1318 /* flags */
1319 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1320 }
1321
1322 /* ******************** Select Left/Right Operator ************************* */
1323 /* Select keyframes left/right of the current frame indicator */
1324
1325 /* defines for left-right select tool */
1326 static const EnumPropertyItem prop_actkeys_leftright_select_types[] = {
1327 {ACTKEYS_LRSEL_TEST, "CHECK", 0, "Check if Select Left or Right", ""},
1328 {ACTKEYS_LRSEL_LEFT, "LEFT", 0, "Before current frame", ""},
1329 {ACTKEYS_LRSEL_RIGHT, "RIGHT", 0, "After current frame", ""},
1330 {0, NULL, 0, NULL, NULL},
1331 };
1332
1333 /* --------------------------------- */
1334
actkeys_select_leftright(bAnimContext * ac,short leftright,short select_mode)1335 static void actkeys_select_leftright(bAnimContext *ac, short leftright, short select_mode)
1336 {
1337 ListBase anim_data = {NULL, NULL};
1338 bAnimListElem *ale;
1339 int filter;
1340
1341 KeyframeEditFunc ok_cb, select_cb;
1342 KeyframeEditData ked = {{NULL}};
1343 Scene *scene = ac->scene;
1344
1345 /* if select mode is replace, deselect all keyframes (and channels) first */
1346 if (select_mode == SELECT_REPLACE) {
1347 select_mode = SELECT_ADD;
1348
1349 /* - deselect all other keyframes, so that just the newly selected remain
1350 * - channels aren't deselected, since we don't re-select any as a consequence
1351 */
1352 deselect_action_keys(ac, 0, SELECT_SUBTRACT);
1353 }
1354
1355 /* set callbacks and editing data */
1356 ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAMERANGE);
1357 select_cb = ANIM_editkeyframes_select(select_mode);
1358
1359 if (leftright == ACTKEYS_LRSEL_LEFT) {
1360 ked.f1 = MINAFRAMEF;
1361 ked.f2 = (float)(CFRA + 0.1f);
1362 }
1363 else {
1364 ked.f1 = (float)(CFRA - 0.1f);
1365 ked.f2 = MAXFRAMEF;
1366 }
1367
1368 /* filter data */
1369 if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
1370 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS);
1371 }
1372 else {
1373 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/ |
1374 ANIMFILTER_NODUPLIS);
1375 }
1376 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1377
1378 /* select keys */
1379 for (ale = anim_data.first; ale; ale = ale->next) {
1380 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
1381
1382 if (adt) {
1383 ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1);
1384 ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
1385 ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
1386 }
1387 else if (ale->type == ANIMTYPE_GPLAYER) {
1388 ED_gpencil_layer_frames_select_box(ale->data, ked.f1, ked.f2, select_mode);
1389 ale->update |= ANIM_UPDATE_DEPS;
1390 }
1391 else if (ale->type == ANIMTYPE_MASKLAYER) {
1392 ED_masklayer_frames_select_box(ale->data, ked.f1, ked.f2, select_mode);
1393 }
1394 else {
1395 ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
1396 }
1397 }
1398
1399 /* Sync marker support */
1400 if (select_mode == SELECT_ADD) {
1401 SpaceAction *saction = (SpaceAction *)ac->sl;
1402
1403 if ((saction) && (saction->flag & SACTION_MARKERS_MOVE)) {
1404 ListBase *markers = ED_animcontext_get_markers(ac);
1405 TimeMarker *marker;
1406
1407 for (marker = markers->first; marker; marker = marker->next) {
1408 if (((leftright == ACTKEYS_LRSEL_LEFT) && (marker->frame < CFRA)) ||
1409 ((leftright == ACTKEYS_LRSEL_RIGHT) && (marker->frame >= CFRA))) {
1410 marker->flag |= SELECT;
1411 }
1412 else {
1413 marker->flag &= ~SELECT;
1414 }
1415 }
1416 }
1417 }
1418
1419 /* Cleanup */
1420 ANIM_animdata_update(ac, &anim_data);
1421 ANIM_animdata_freelist(&anim_data);
1422 }
1423
1424 /* ----------------- */
1425
actkeys_select_leftright_exec(bContext * C,wmOperator * op)1426 static int actkeys_select_leftright_exec(bContext *C, wmOperator *op)
1427 {
1428 bAnimContext ac;
1429 short leftright = RNA_enum_get(op->ptr, "mode");
1430 short selectmode;
1431
1432 /* get editor data */
1433 if (ANIM_animdata_get_context(C, &ac) == 0) {
1434 return OPERATOR_CANCELLED;
1435 }
1436
1437 /* select mode is either replace (deselect all, then add) or add/extend */
1438 if (RNA_boolean_get(op->ptr, "extend")) {
1439 selectmode = SELECT_INVERT;
1440 }
1441 else {
1442 selectmode = SELECT_REPLACE;
1443 }
1444
1445 /* if "test" mode is set, we don't have any info to set this with */
1446 if (leftright == ACTKEYS_LRSEL_TEST) {
1447 return OPERATOR_CANCELLED;
1448 }
1449
1450 /* do the selecting now */
1451 actkeys_select_leftright(&ac, leftright, selectmode);
1452
1453 /* set notifier that keyframe selection (and channels too) have changed */
1454 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
1455 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL);
1456
1457 return OPERATOR_FINISHED;
1458 }
1459
actkeys_select_leftright_invoke(bContext * C,wmOperator * op,const wmEvent * event)1460 static int actkeys_select_leftright_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1461 {
1462 bAnimContext ac;
1463 short leftright = RNA_enum_get(op->ptr, "mode");
1464
1465 /* get editor data */
1466 if (ANIM_animdata_get_context(C, &ac) == 0) {
1467 return OPERATOR_CANCELLED;
1468 }
1469
1470 /* handle mode-based testing */
1471 if (leftright == ACTKEYS_LRSEL_TEST) {
1472 Scene *scene = ac.scene;
1473 ARegion *region = ac.region;
1474 View2D *v2d = ®ion->v2d;
1475 float x;
1476
1477 /* determine which side of the current frame mouse is on */
1478 x = UI_view2d_region_to_view_x(v2d, event->mval[0]);
1479 if (x < CFRA) {
1480 RNA_enum_set(op->ptr, "mode", ACTKEYS_LRSEL_LEFT);
1481 }
1482 else {
1483 RNA_enum_set(op->ptr, "mode", ACTKEYS_LRSEL_RIGHT);
1484 }
1485 }
1486
1487 /* perform selection */
1488 return actkeys_select_leftright_exec(C, op);
1489 }
1490
ACTION_OT_select_leftright(wmOperatorType * ot)1491 void ACTION_OT_select_leftright(wmOperatorType *ot)
1492 {
1493 PropertyRNA *prop;
1494
1495 /* identifiers */
1496 ot->name = "Select Left/Right";
1497 ot->idname = "ACTION_OT_select_leftright";
1498 ot->description = "Select keyframes to the left or the right of the current frame";
1499
1500 /* api callbacks */
1501 ot->invoke = actkeys_select_leftright_invoke;
1502 ot->exec = actkeys_select_leftright_exec;
1503 ot->poll = ED_operator_action_active;
1504
1505 /* flags */
1506 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1507
1508 /* properties */
1509 ot->prop = RNA_def_enum(
1510 ot->srna, "mode", prop_actkeys_leftright_select_types, ACTKEYS_LRSEL_TEST, "Mode", "");
1511 RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE);
1512
1513 prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", "");
1514 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1515 }
1516
1517 /* ******************** Mouse-Click Select Operator *********************** */
1518 /* This operator works in one of three ways:
1519 * - 1) keyframe under mouse - no special modifiers
1520 * - 2) all keyframes on the same side of current frame indicator as mouse - ALT modifier
1521 * - 3) column select all keyframes in frame under mouse - CTRL modifier
1522 * - 4) all keyframes in channel under mouse - CTRL+ALT modifiers
1523 *
1524 * In addition to these basic options, the SHIFT modifier can be used to toggle the
1525 * selection mode between replacing the selection (without) and inverting the selection (with).
1526 */
1527
1528 /* ------------------- */
1529
1530 /* option 1) select keyframe directly under mouse */
actkeys_mselect_single(bAnimContext * ac,bAnimListElem * ale,short select_mode,float selx)1531 static void actkeys_mselect_single(bAnimContext *ac,
1532 bAnimListElem *ale,
1533 short select_mode,
1534 float selx)
1535 {
1536 KeyframeEditData ked = {{NULL}};
1537 KeyframeEditFunc select_cb, ok_cb;
1538
1539 /* get functions for selecting keyframes */
1540 select_cb = ANIM_editkeyframes_select(select_mode);
1541 ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAME);
1542 ked.f1 = selx;
1543 ked.iterflags |= KED_F1_NLA_UNMAP;
1544
1545 /* select the nominated keyframe on the given frame */
1546 if (ale->type == ANIMTYPE_GPLAYER) {
1547 ED_gpencil_select_frame(ale->data, selx, select_mode);
1548 ale->update |= ANIM_UPDATE_DEPS;
1549 }
1550 else if (ale->type == ANIMTYPE_MASKLAYER) {
1551 ED_mask_select_frame(ale->data, selx, select_mode);
1552 }
1553 else {
1554 if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK) && (ale->type == ANIMTYPE_SUMMARY) &&
1555 (ale->datatype == ALE_ALL)) {
1556 ListBase anim_data = {NULL, NULL};
1557 int filter;
1558
1559 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY */ |
1560 ANIMFILTER_NODUPLIS);
1561 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1562
1563 for (ale = anim_data.first; ale; ale = ale->next) {
1564 if (ale->type == ANIMTYPE_GPLAYER) {
1565 ED_gpencil_select_frame(ale->data, selx, select_mode);
1566 ale->update |= ANIM_UPDATE_DEPS;
1567 }
1568 else if (ale->type == ANIMTYPE_MASKLAYER) {
1569 ED_mask_select_frame(ale->data, selx, select_mode);
1570 }
1571 }
1572
1573 ANIM_animdata_update(ac, &anim_data);
1574 ANIM_animdata_freelist(&anim_data);
1575 }
1576 else {
1577 ANIM_animchannel_keyframes_loop(&ked, ac->ads, ale, ok_cb, select_cb, NULL);
1578 }
1579 }
1580 }
1581
1582 /* Option 2) Selects all the keyframes on either side of the current frame
1583 * (depends on which side the mouse is on) */
1584 /* (see actkeys_select_leftright) */
1585
1586 /* Option 3) Selects all visible keyframes in the same frame as the mouse click */
actkeys_mselect_column(bAnimContext * ac,short select_mode,float selx)1587 static void actkeys_mselect_column(bAnimContext *ac, short select_mode, float selx)
1588 {
1589 ListBase anim_data = {NULL, NULL};
1590 bAnimListElem *ale;
1591 int filter;
1592
1593 KeyframeEditFunc select_cb, ok_cb;
1594 KeyframeEditData ked = {{NULL}};
1595
1596 /* set up BezTriple edit callbacks */
1597 select_cb = ANIM_editkeyframes_select(select_mode);
1598 ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAME);
1599
1600 /* loop through all of the keys and select additional keyframes
1601 * based on the keys found to be selected above
1602 */
1603 if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
1604 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY */ |
1605 ANIMFILTER_NODUPLIS);
1606 }
1607 else {
1608 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS);
1609 }
1610 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1611
1612 for (ale = anim_data.first; ale; ale = ale->next) {
1613 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
1614
1615 /* set frame for validation callback to refer to */
1616 if (adt) {
1617 ked.f1 = BKE_nla_tweakedit_remap(adt, selx, NLATIME_CONVERT_UNMAP);
1618 }
1619 else {
1620 ked.f1 = selx;
1621 }
1622
1623 /* select elements with frame number matching cfra */
1624 if (ale->type == ANIMTYPE_GPLAYER) {
1625 ED_gpencil_select_frame(ale->key_data, selx, select_mode);
1626 ale->update |= ANIM_UPDATE_DEPS;
1627 }
1628 else if (ale->type == ANIMTYPE_MASKLAYER) {
1629 ED_mask_select_frame(ale->key_data, selx, select_mode);
1630 }
1631 else {
1632 ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
1633 }
1634 }
1635
1636 /* free elements */
1637 BLI_freelistN(&ked.list);
1638
1639 ANIM_animdata_update(ac, &anim_data);
1640 ANIM_animdata_freelist(&anim_data);
1641 }
1642
1643 /* option 4) select all keyframes in same channel */
actkeys_mselect_channel_only(bAnimContext * ac,bAnimListElem * ale,short select_mode)1644 static void actkeys_mselect_channel_only(bAnimContext *ac, bAnimListElem *ale, short select_mode)
1645 {
1646 KeyframeEditFunc select_cb;
1647
1648 /* get functions for selecting keyframes */
1649 select_cb = ANIM_editkeyframes_select(select_mode);
1650
1651 /* select all keyframes in this channel */
1652 if (ale->type == ANIMTYPE_GPLAYER) {
1653 ED_gpencil_select_frames(ale->data, select_mode);
1654 ale->update = ANIM_UPDATE_DEPS;
1655 }
1656 else if (ale->type == ANIMTYPE_MASKLAYER) {
1657 ED_mask_select_frames(ale->data, select_mode);
1658 }
1659 else {
1660 if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK) && (ale->type == ANIMTYPE_SUMMARY) &&
1661 (ale->datatype == ALE_ALL)) {
1662 ListBase anim_data = {NULL, NULL};
1663 int filter;
1664
1665 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY */ |
1666 ANIMFILTER_NODUPLIS);
1667 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1668
1669 for (ale = anim_data.first; ale; ale = ale->next) {
1670 if (ale->type == ANIMTYPE_GPLAYER) {
1671 ED_gpencil_select_frames(ale->data, select_mode);
1672 ale->update |= ANIM_UPDATE_DEPS;
1673 }
1674 else if (ale->type == ANIMTYPE_MASKLAYER) {
1675 ED_mask_select_frames(ale->data, select_mode);
1676 }
1677 }
1678
1679 ANIM_animdata_update(ac, &anim_data);
1680 ANIM_animdata_freelist(&anim_data);
1681 }
1682 else {
1683 ANIM_animchannel_keyframes_loop(NULL, ac->ads, ale, NULL, select_cb, NULL);
1684 }
1685 }
1686 }
1687
1688 /* ------------------- */
1689
mouse_action_keys(bAnimContext * ac,const int mval[2],short select_mode,const bool deselect_all,const bool column,const bool same_channel,bool wait_to_deselect_others)1690 static int mouse_action_keys(bAnimContext *ac,
1691 const int mval[2],
1692 short select_mode,
1693 const bool deselect_all,
1694 const bool column,
1695 const bool same_channel,
1696 bool wait_to_deselect_others)
1697 {
1698 int filter = ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS;
1699
1700 bAnimListElem *ale = NULL;
1701 bool found = false;
1702 bool is_selected = false;
1703 float frame = 0.0f; /* frame of keyframe under mouse - NLA corrections not applied/included */
1704 float selx = 0.0f; /* frame of keyframe under mouse */
1705 int ret_value = OPERATOR_FINISHED;
1706
1707 actkeys_find_key_at_position(
1708 ac, filter, mval[0], mval[1], &ale, &selx, &frame, &found, &is_selected);
1709
1710 if (select_mode != SELECT_REPLACE) {
1711 wait_to_deselect_others = false;
1712 }
1713
1714 /* For replacing selection, if we have something to select, we have to clear existing selection.
1715 * The same goes if we found nothing to select, and deselect_all is true
1716 * (deselect on nothing behavior). */
1717 if ((select_mode == SELECT_REPLACE && found) || (!found && deselect_all)) {
1718 /* reset selection mode for next steps */
1719 select_mode = SELECT_ADD;
1720
1721 /* Rather than deselecting others, users may want to drag to box-select (drag from empty space)
1722 * or tweak-translate an already selected item. If these cases may apply, delay deselection. */
1723 if (wait_to_deselect_others && (!found || is_selected)) {
1724 ret_value = OPERATOR_RUNNING_MODAL;
1725 }
1726 else {
1727 /* deselect all keyframes */
1728 deselect_action_keys(ac, 0, SELECT_SUBTRACT);
1729
1730 /* highlight channel clicked on */
1731 if (ELEM(ac->datatype, ANIMCONT_ACTION, ANIMCONT_DOPESHEET, ANIMCONT_TIMELINE)) {
1732 /* deselect all other channels first */
1733 ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_CLEAR);
1734
1735 /* Highlight Action-Group or F-Curve? */
1736 if (ale != NULL && ale->data) {
1737 if (ale->type == ANIMTYPE_GROUP) {
1738 bActionGroup *agrp = ale->data;
1739
1740 agrp->flag |= AGRP_SELECTED;
1741 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, agrp, ANIMTYPE_GROUP);
1742 }
1743 else if (ELEM(ale->type, ANIMTYPE_FCURVE, ANIMTYPE_NLACURVE)) {
1744 FCurve *fcu = ale->data;
1745
1746 fcu->flag |= FCURVE_SELECTED;
1747 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, fcu, ale->type);
1748 }
1749 }
1750 }
1751 else if (ac->datatype == ANIMCONT_GPENCIL) {
1752 /* deselect all other channels first */
1753 ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_CLEAR);
1754
1755 /* Highlight GPencil Layer */
1756 if (ale != NULL && ale->data != NULL && ale->type == ANIMTYPE_GPLAYER) {
1757 bGPdata *gpd = (bGPdata *)ale->id;
1758 bGPDlayer *gpl = ale->data;
1759
1760 gpl->flag |= GP_LAYER_SELECT;
1761 /* Update other layer status. */
1762 if (BKE_gpencil_layer_active_get(gpd) != gpl) {
1763 BKE_gpencil_layer_active_set(gpd, gpl);
1764 BKE_gpencil_layer_autolock_set(gpd, false);
1765 WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1766 }
1767 }
1768 }
1769 else if (ac->datatype == ANIMCONT_MASK) {
1770 /* deselect all other channels first */
1771 ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_CLEAR);
1772
1773 if (ale != NULL && ale->data != NULL && ale->type == ANIMTYPE_MASKLAYER) {
1774 MaskLayer *masklay = ale->data;
1775
1776 masklay->flag |= MASK_LAYERFLAG_SELECT;
1777 }
1778 }
1779 }
1780 }
1781
1782 /* only select keyframes if we clicked on a valid channel and hit something */
1783 if (ale != NULL) {
1784 if (found) {
1785 /* apply selection to keyframes */
1786 if (column) {
1787 /* select all keyframes in the same frame as the one we hit on the active channel
1788 * [T41077]: "frame" not "selx" here (i.e. no NLA corrections yet) as the code here
1789 * does that itself again as it needs to work on multiple data-blocks.
1790 */
1791 actkeys_mselect_column(ac, select_mode, frame);
1792 }
1793 else if (same_channel) {
1794 /* select all keyframes in the active channel */
1795 actkeys_mselect_channel_only(ac, ale, select_mode);
1796 }
1797 else {
1798 /* select the nominated keyframe on the given frame */
1799 actkeys_mselect_single(ac, ale, select_mode, selx);
1800 }
1801 }
1802
1803 /* flush tagged updates
1804 * NOTE: We temporarily add this channel back to the list so that this can happen
1805 */
1806 ListBase anim_data = {ale, ale};
1807 ANIM_animdata_update(ac, &anim_data);
1808
1809 /* free this channel */
1810 MEM_freeN(ale);
1811 }
1812
1813 return ret_value;
1814 }
1815
1816 /* handle clicking */
actkeys_clickselect_exec(bContext * C,wmOperator * op)1817 static int actkeys_clickselect_exec(bContext *C, wmOperator *op)
1818 {
1819 bAnimContext ac;
1820 int ret_value;
1821
1822 /* get editor data */
1823 if (ANIM_animdata_get_context(C, &ac) == 0) {
1824 return OPERATOR_CANCELLED;
1825 }
1826
1827 /* get useful pointers from animation context data */
1828 /* region = ac.region; */ /* UNUSED */
1829
1830 /* select mode is either replace (deselect all, then add) or add/extend */
1831 const short selectmode = RNA_boolean_get(op->ptr, "extend") ? SELECT_INVERT : SELECT_REPLACE;
1832 const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all");
1833 const bool wait_to_deselect_others = RNA_boolean_get(op->ptr, "wait_to_deselect_others");
1834 int mval[2];
1835
1836 /* column selection */
1837 const bool column = RNA_boolean_get(op->ptr, "column");
1838 const bool channel = RNA_boolean_get(op->ptr, "channel");
1839
1840 mval[0] = RNA_int_get(op->ptr, "mouse_x");
1841 mval[1] = RNA_int_get(op->ptr, "mouse_y");
1842
1843 /* select keyframe(s) based upon mouse position*/
1844 ret_value = mouse_action_keys(
1845 &ac, mval, selectmode, deselect_all, column, channel, wait_to_deselect_others);
1846
1847 /* set notifier that keyframe selection (and channels too) have changed */
1848 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
1849 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL);
1850
1851 /* for tweak grab to work */
1852 return ret_value | OPERATOR_PASS_THROUGH;
1853 }
1854
ACTION_OT_clickselect(wmOperatorType * ot)1855 void ACTION_OT_clickselect(wmOperatorType *ot)
1856 {
1857 PropertyRNA *prop;
1858
1859 /* identifiers */
1860 ot->name = "Select Keyframes";
1861 ot->idname = "ACTION_OT_clickselect";
1862 ot->description = "Select keyframes by clicking on them";
1863
1864 /* callbacks */
1865 ot->poll = ED_operator_action_active;
1866 ot->exec = actkeys_clickselect_exec;
1867 ot->invoke = WM_generic_select_invoke;
1868 ot->modal = WM_generic_select_modal;
1869
1870 /* flags */
1871 ot->flag = OPTYPE_UNDO;
1872
1873 /* properties */
1874 WM_operator_properties_generic_select(ot);
1875 prop = RNA_def_boolean(
1876 ot->srna,
1877 "extend",
1878 0,
1879 "Extend Select",
1880 "Toggle keyframe selection instead of leaving newly selected keyframes only"); /* SHIFTKEY */
1881 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1882
1883 prop = RNA_def_boolean(ot->srna,
1884 "deselect_all",
1885 false,
1886 "Deselect On Nothing",
1887 "Deselect all when nothing under the cursor");
1888 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1889
1890 prop = RNA_def_boolean(
1891 ot->srna,
1892 "column",
1893 0,
1894 "Column Select",
1895 "Select all keyframes that occur on the same frame as the one under the mouse"); /* ALTKEY */
1896 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1897
1898 prop = RNA_def_boolean(
1899 ot->srna,
1900 "channel",
1901 0,
1902 "Only Channel",
1903 "Select all the keyframes in the channel under the mouse"); /* CTRLKEY + ALTKEY */
1904 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1905 }
1906
1907 /* ************************************************************************** */
1908