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 spgraph
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_lasso_2d.h"
32 #include "BLI_math.h"
33 #include "BLI_utildefines.h"
34
35 #include "DNA_anim_types.h"
36 #include "DNA_scene_types.h"
37 #include "DNA_screen_types.h"
38 #include "DNA_space_types.h"
39
40 #include "RNA_access.h"
41 #include "RNA_define.h"
42
43 #include "BKE_context.h"
44 #include "BKE_fcurve.h"
45 #include "BKE_nla.h"
46
47 #include "UI_view2d.h"
48
49 #include "ED_anim_api.h"
50 #include "ED_keyframes_edit.h"
51 #include "ED_markers.h"
52 #include "ED_select_utils.h"
53
54 #include "WM_api.h"
55 #include "WM_types.h"
56
57 #include "graph_intern.h"
58
59 /* ************************************************************************** */
60 /* KEYFRAMES STUFF */
61
62 /* temp info for caching handle vertices close */
63 typedef struct tNearestVertInfo {
64 struct tNearestVertInfo *next, *prev;
65
66 FCurve *fcu; /* F-Curve that keyframe comes from */
67
68 BezTriple *bezt; /* keyframe to consider */
69 FPoint *fpt; /* sample point to consider */
70
71 short hpoint; /* the handle index that we hit (eHandleIndex) */
72 short sel; /* whether the handle is selected or not */
73 int dist; /* distance from mouse to vert */
74
75 eAnim_ChannelType ctype; /* type of animation channel this FCurve comes from */
76
77 float frame; /* frame that point was on when it matched (global time) */
78 } tNearestVertInfo;
79
80 /* Tags for the type of graph vert that we have */
81 typedef enum eGraphVertIndex {
82 NEAREST_HANDLE_LEFT = -1,
83 NEAREST_HANDLE_KEY,
84 NEAREST_HANDLE_RIGHT,
85 } eGraphVertIndex;
86
87 /* Tolerance for absolute radius (in pixels) of the vert from the cursor to use */
88 /* TODO: perhaps this should depend a bit on the size that the user set the vertices to be? */
89 #define GVERTSEL_TOL (10 * U.pixelsize)
90
91 /* ....... */
92
93 /* check if its ok to select a handle */
94 /* XXX also need to check for int-values only? */
fcurve_handle_sel_check(SpaceGraph * sipo,BezTriple * bezt)95 static bool fcurve_handle_sel_check(SpaceGraph *sipo, BezTriple *bezt)
96 {
97 if (sipo->flag & SIPO_NOHANDLES) {
98 return false;
99 }
100 if ((sipo->flag & SIPO_SELVHANDLESONLY) && BEZT_ISSEL_ANY(bezt) == 0) {
101 return false;
102 }
103 return true;
104 }
105
106 /* check if the given vertex is within bounds or not */
107 /* TODO: should we return if we hit something? */
nearest_fcurve_vert_store(ListBase * matches,View2D * v2d,FCurve * fcu,eAnim_ChannelType ctype,BezTriple * bezt,FPoint * fpt,short hpoint,const int mval[2],float unit_scale,float offset)108 static void nearest_fcurve_vert_store(ListBase *matches,
109 View2D *v2d,
110 FCurve *fcu,
111 eAnim_ChannelType ctype,
112 BezTriple *bezt,
113 FPoint *fpt,
114 short hpoint,
115 const int mval[2],
116 float unit_scale,
117 float offset)
118 {
119 /* Keyframes or Samples? */
120 if (bezt) {
121 int screen_co[2], dist;
122
123 /* convert from data-space to screen coordinates
124 * NOTE: hpoint+1 gives us 0,1,2 respectively for each handle,
125 * needed to access the relevant vertex coordinates in the 3x3
126 * 'vec' matrix
127 */
128 if (UI_view2d_view_to_region_clip(v2d,
129 bezt->vec[hpoint + 1][0],
130 (bezt->vec[hpoint + 1][1] + offset) * unit_scale,
131 &screen_co[0],
132 &screen_co[1]) &&
133 /* check if distance from mouse cursor to vert in screen space is within tolerance */
134 ((dist = len_v2v2_int(mval, screen_co)) <= GVERTSEL_TOL)) {
135 tNearestVertInfo *nvi = (tNearestVertInfo *)matches->last;
136 bool replace = false;
137
138 /* If there is already a point for the F-Curve,
139 * check if this point is closer than that was. */
140 if ((nvi) && (nvi->fcu == fcu)) {
141 /* replace if we are closer, or if equal and that one wasn't selected but we are... */
142 if ((nvi->dist > dist) || ((nvi->sel == 0) && BEZT_ISSEL_ANY(bezt))) {
143 replace = 1;
144 }
145 }
146 /* add new if not replacing... */
147 if (replace == 0) {
148 nvi = MEM_callocN(sizeof(tNearestVertInfo), "Nearest Graph Vert Info - Bezt");
149 }
150
151 /* store values */
152 nvi->fcu = fcu;
153 nvi->ctype = ctype;
154
155 nvi->bezt = bezt;
156 nvi->hpoint = hpoint;
157 nvi->dist = dist;
158
159 nvi->frame = bezt->vec[1][0]; /* currently in global time... */
160
161 nvi->sel = BEZT_ISSEL_ANY(bezt); /* XXX... should this use the individual verts instead? */
162
163 /* add to list of matches if appropriate... */
164 if (replace == 0) {
165 BLI_addtail(matches, nvi);
166 }
167 }
168 }
169 else if (fpt) {
170 /* TODO... */
171 }
172 }
173
174 /* helper for find_nearest_fcurve_vert() - build the list of nearest matches */
get_nearest_fcurve_verts_list(bAnimContext * ac,const int mval[2],ListBase * matches)175 static void get_nearest_fcurve_verts_list(bAnimContext *ac, const int mval[2], ListBase *matches)
176 {
177 ListBase anim_data = {NULL, NULL};
178 bAnimListElem *ale;
179 int filter;
180
181 SpaceGraph *sipo = (SpaceGraph *)ac->sl;
182 View2D *v2d = &ac->region->v2d;
183 short mapping_flag = 0;
184
185 /* get curves to search through
186 * - if the option to only show keyframes that belong to selected F-Curves is enabled,
187 * include the 'only selected' flag...
188 */
189 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
190 if (sipo->flag &
191 SIPO_SELCUVERTSONLY) { /* FIXME: this should really be check for by the filtering code... */
192 filter |= ANIMFILTER_SEL;
193 }
194 mapping_flag |= ANIM_get_normalization_flags(ac);
195 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
196
197 for (ale = anim_data.first; ale; ale = ale->next) {
198 FCurve *fcu = (FCurve *)ale->key_data;
199 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
200 float offset;
201 float unit_scale = ANIM_unit_mapping_get_factor(
202 ac->scene, ale->id, fcu, mapping_flag, &offset);
203
204 /* apply NLA mapping to all the keyframes */
205 if (adt) {
206 ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0);
207 }
208
209 if (fcu->bezt) {
210 BezTriple *bezt1 = fcu->bezt, *prevbezt = NULL;
211 int i;
212
213 for (i = 0; i < fcu->totvert; i++, prevbezt = bezt1, bezt1++) {
214 /* keyframe */
215 nearest_fcurve_vert_store(matches,
216 v2d,
217 fcu,
218 ale->type,
219 bezt1,
220 NULL,
221 NEAREST_HANDLE_KEY,
222 mval,
223 unit_scale,
224 offset);
225
226 /* handles - only do them if they're visible */
227 if (fcurve_handle_sel_check(sipo, bezt1) && (fcu->totvert > 1)) {
228 /* first handle only visible if previous segment had handles */
229 if ((!prevbezt && (bezt1->ipo == BEZT_IPO_BEZ)) ||
230 (prevbezt && (prevbezt->ipo == BEZT_IPO_BEZ))) {
231 nearest_fcurve_vert_store(matches,
232 v2d,
233 fcu,
234 ale->type,
235 bezt1,
236 NULL,
237 NEAREST_HANDLE_LEFT,
238 mval,
239 unit_scale,
240 offset);
241 }
242
243 /* second handle only visible if this segment is bezier */
244 if (bezt1->ipo == BEZT_IPO_BEZ) {
245 nearest_fcurve_vert_store(matches,
246 v2d,
247 fcu,
248 ale->type,
249 bezt1,
250 NULL,
251 NEAREST_HANDLE_RIGHT,
252 mval,
253 unit_scale,
254 offset);
255 }
256 }
257 }
258 }
259 else if (fcu->fpt) {
260 /* TODO; do this for samples too */
261 }
262
263 /* un-apply NLA mapping from all the keyframes */
264 if (adt) {
265 ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 0);
266 }
267 }
268
269 /* free channels */
270 ANIM_animdata_freelist(&anim_data);
271 }
272
273 /* helper for find_nearest_fcurve_vert() - get the best match to use */
get_best_nearest_fcurve_vert(ListBase * matches)274 static tNearestVertInfo *get_best_nearest_fcurve_vert(ListBase *matches)
275 {
276 tNearestVertInfo *nvi = NULL;
277 short found = 0;
278
279 /* abort if list is empty */
280 if (BLI_listbase_is_empty(matches)) {
281 return NULL;
282 }
283
284 /* if list only has 1 item, remove it from the list and return */
285 if (BLI_listbase_is_single(matches)) {
286 /* need to remove from the list, otherwise it gets freed and then we can't return it */
287 return BLI_pophead(matches);
288 }
289
290 /* try to find the first selected F-Curve vert, then take the one after it */
291 for (nvi = matches->first; nvi; nvi = nvi->next) {
292 /* which mode of search are we in: find first selected, or find vert? */
293 if (found) {
294 /* Just take this vert now that we've found the selected one
295 * - We'll need to remove this from the list
296 * so that it can be returned to the original caller.
297 */
298 BLI_remlink(matches, nvi);
299 return nvi;
300 }
301
302 /* if vert is selected, we've got what we want... */
303 if (nvi->sel) {
304 found = 1;
305 }
306 }
307
308 /* if we're still here, this means that we failed to find anything appropriate in the first pass,
309 * so just take the first item now...
310 */
311 return BLI_pophead(matches);
312 }
313
314 /**
315 * Find the nearest vertices (either a handle or the keyframe)
316 * that are nearest to the mouse cursor (in area coordinates)
317 *
318 * \note the match info found must still be freed.
319 */
find_nearest_fcurve_vert(bAnimContext * ac,const int mval[2])320 static tNearestVertInfo *find_nearest_fcurve_vert(bAnimContext *ac, const int mval[2])
321 {
322 ListBase matches = {NULL, NULL};
323 tNearestVertInfo *nvi;
324
325 /* step 1: get the nearest verts */
326 get_nearest_fcurve_verts_list(ac, mval, &matches);
327
328 /* step 2: find the best vert */
329 nvi = get_best_nearest_fcurve_vert(&matches);
330
331 BLI_freelistN(&matches);
332
333 /* return the best vert found */
334 return nvi;
335 }
336
337 /* ******************** Deselect All Operator ***************************** */
338 /* This operator works in one of three ways:
339 * 1) (de)select all (AKEY) - test if select all or deselect all
340 * 2) invert all (CTRL-IKEY) - invert selection of all keyframes
341 * 3) (de)select all - no testing is done; only for use internal tools as normal function...
342 */
343
344 /* Deselects keyframes in the Graph Editor
345 * - This is called by the deselect all operator, as well as other ones!
346 *
347 * - test: check if select or deselect all
348 * - sel: how to select keyframes
349 * 0 = deselect
350 * 1 = select
351 * 2 = invert
352 * - do_channels: whether to affect selection status of channels
353 */
deselect_graph_keys(bAnimContext * ac,bool test,short sel,bool do_channels)354 void deselect_graph_keys(bAnimContext *ac, bool test, short sel, bool do_channels)
355 {
356 ListBase anim_data = {NULL, NULL};
357 bAnimListElem *ale;
358 int filter;
359
360 SpaceGraph *sipo = (SpaceGraph *)ac->sl;
361 KeyframeEditData ked = {{NULL}};
362 KeyframeEditFunc test_cb, sel_cb;
363
364 /* determine type-based settings */
365 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
366
367 /* filter data */
368 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
369
370 /* init BezTriple looping data */
371 test_cb = ANIM_editkeyframes_ok(BEZT_OK_SELECTED);
372
373 /* See if we should be selecting or deselecting */
374 if (test) {
375 for (ale = anim_data.first; ale; ale = ale->next) {
376 if (ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, test_cb, NULL)) {
377 sel = SELECT_SUBTRACT;
378 break;
379 }
380 }
381 }
382
383 /* convert sel to selectmode, and use that to get editor */
384 sel_cb = ANIM_editkeyframes_select(sel);
385
386 /* Now set the flags */
387 for (ale = anim_data.first; ale; ale = ale->next) {
388 FCurve *fcu = (FCurve *)ale->key_data;
389
390 /* Keyframes First */
391 ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, sel_cb, NULL);
392
393 /* affect channel selection status? */
394 if (do_channels) {
395 /* Only change selection of channel when the visibility of keyframes
396 * doesn't depend on this. */
397 if ((sipo->flag & SIPO_SELCUVERTSONLY) == 0) {
398 /* deactivate the F-Curve, and deselect if deselecting keyframes.
399 * otherwise select the F-Curve too since we've selected all the keyframes
400 */
401 if (sel == SELECT_SUBTRACT) {
402 fcu->flag &= ~FCURVE_SELECTED;
403 }
404 else {
405 fcu->flag |= FCURVE_SELECTED;
406 }
407 }
408
409 /* always deactivate all F-Curves if we perform batch ops for selection */
410 fcu->flag &= ~FCURVE_ACTIVE;
411 }
412 }
413
414 /* Cleanup */
415 ANIM_animdata_freelist(&anim_data);
416 }
417
418 /* ------------------- */
419
graphkeys_deselectall_exec(bContext * C,wmOperator * op)420 static int graphkeys_deselectall_exec(bContext *C, wmOperator *op)
421 {
422 bAnimContext ac;
423 bAnimListElem *ale_active = NULL;
424
425 /* get editor data */
426 if (ANIM_animdata_get_context(C, &ac) == 0) {
427 return OPERATOR_CANCELLED;
428 }
429
430 /* find active F-Curve, and preserve this for later
431 * or else it becomes annoying with the current active
432 * curve keeps fading out even while you're editing it
433 */
434 ale_active = get_active_fcurve_channel(&ac);
435
436 /* 'standard' behavior - check if selected, then apply relevant selection */
437 const int action = RNA_enum_get(op->ptr, "action");
438 switch (action) {
439 case SEL_TOGGLE:
440 deselect_graph_keys(&ac, 1, SELECT_ADD, true);
441 break;
442 case SEL_SELECT:
443 deselect_graph_keys(&ac, 0, SELECT_ADD, true);
444 break;
445 case SEL_DESELECT:
446 deselect_graph_keys(&ac, 0, SELECT_SUBTRACT, true);
447 break;
448 case SEL_INVERT:
449 deselect_graph_keys(&ac, 0, SELECT_INVERT, true);
450 break;
451 default:
452 BLI_assert(0);
453 break;
454 }
455
456 /* restore active F-Curve... */
457 if (ale_active) {
458 FCurve *fcu = (FCurve *)ale_active->data;
459
460 /* all others should not be disabled, so we should be able to just set this directly...
461 * - selection needs to be set too, or else this won't work...
462 */
463 fcu->flag |= (FCURVE_SELECTED | FCURVE_ACTIVE);
464
465 MEM_freeN(ale_active);
466 ale_active = NULL;
467 }
468
469 /* set notifier that things have changed */
470 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
471
472 return OPERATOR_FINISHED;
473 }
474
GRAPH_OT_select_all(wmOperatorType * ot)475 void GRAPH_OT_select_all(wmOperatorType *ot)
476 {
477 /* identifiers */
478 ot->name = "Select All";
479 ot->idname = "GRAPH_OT_select_all";
480 ot->description = "Toggle selection of all keyframes";
481
482 /* api callbacks */
483 ot->exec = graphkeys_deselectall_exec;
484 ot->poll = graphop_visible_keyframes_poll;
485
486 /* flags */
487 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
488
489 /* properties */
490 WM_operator_properties_select_all(ot);
491 }
492
493 /* ******************** Box Select Operator **************************** */
494 /* This operator currently works in one of three ways:
495 * -> BKEY - 1) all keyframes within region are selected (validation with BEZT_OK_REGION)
496 * -> ALT-BKEY - depending on which axis of the region was larger...
497 * -> 2) x-axis, so select all frames within frame range (validation with BEZT_OK_FRAMERANGE)
498 * -> 3) y-axis, so select all frames within channels that region included
499 * (validation with BEZT_OK_VALUERANGE).
500 *
501 * The selection backend is also reused for the Lasso and Circle select operators.
502 */
503
504 /* Box Select only selects keyframes now, as overshooting handles often get caught too,
505 * which means that they may be inadvertently moved as well. However, incl_handles overrides
506 * this, and allow handles to be considered independently too.
507 * Also, for convenience, handles should get same status as keyframe (if it was within bounds).
508 */
box_select_graphkeys(bAnimContext * ac,const rctf * rectf_view,short mode,short selectmode,bool incl_handles,void * data)509 static void box_select_graphkeys(bAnimContext *ac,
510 const rctf *rectf_view,
511 short mode,
512 short selectmode,
513 bool incl_handles,
514 void *data)
515 {
516 ListBase anim_data = {NULL, NULL};
517 bAnimListElem *ale;
518 int filter, mapping_flag;
519
520 SpaceGraph *sipo = (SpaceGraph *)ac->sl;
521 KeyframeEditData ked;
522 KeyframeEditFunc ok_cb, select_cb;
523 View2D *v2d = &ac->region->v2d;
524 rctf rectf, scaled_rectf;
525
526 /* Convert mouse coordinates to frame ranges and
527 * channel coordinates corrected for view pan/zoom. */
528 UI_view2d_region_to_view_rctf(v2d, rectf_view, &rectf);
529
530 /* filter data */
531 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
532 if (sipo->flag & SIPO_SELCUVERTSONLY) {
533 filter |= ANIMFILTER_FOREDIT | ANIMFILTER_SELEDIT;
534 }
535 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
536
537 /* get beztriple editing/validation funcs */
538 select_cb = ANIM_editkeyframes_select(selectmode);
539 ok_cb = ANIM_editkeyframes_ok(mode);
540
541 /* init editing data */
542 memset(&ked, 0, sizeof(KeyframeEditData));
543 if (mode == BEZT_OK_REGION_LASSO) {
544 KeyframeEdit_LassoData *data_lasso = data;
545 data_lasso->rectf_scaled = &scaled_rectf;
546 ked.data = data_lasso;
547 }
548 else if (mode == BEZT_OK_REGION_CIRCLE) {
549 KeyframeEdit_CircleData *data_circle = data;
550 data_circle->rectf_scaled = &scaled_rectf;
551 ked.data = data;
552 }
553 else {
554 ked.data = &scaled_rectf;
555 }
556
557 if (sipo->flag & SIPO_SELVHANDLESONLY) {
558 ked.iterflags |= KEYFRAME_ITER_HANDLES_DEFAULT_INVISIBLE;
559 }
560
561 /* treat handles separately? */
562 if (incl_handles) {
563 ked.iterflags |= KEYFRAME_ITER_INCL_HANDLES;
564 mapping_flag = 0;
565 }
566 else {
567 mapping_flag = ANIM_UNITCONV_ONLYKEYS;
568 }
569
570 mapping_flag |= ANIM_get_normalization_flags(ac);
571
572 /* loop over data, doing box select */
573 for (ale = anim_data.first; ale; ale = ale->next) {
574 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
575 FCurve *fcu = (FCurve *)ale->key_data;
576 float offset;
577 float unit_scale = ANIM_unit_mapping_get_factor(
578 ac->scene, ale->id, fcu, mapping_flag, &offset);
579
580 /* apply NLA mapping to all the keyframes, since it's easier than trying to
581 * guess when a callback might use something different
582 */
583 if (adt) {
584 ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, incl_handles == 0);
585 }
586
587 scaled_rectf.xmin = rectf.xmin;
588 scaled_rectf.xmax = rectf.xmax;
589 scaled_rectf.ymin = rectf.ymin / unit_scale - offset;
590 scaled_rectf.ymax = rectf.ymax / unit_scale - offset;
591
592 /* set horizontal range (if applicable)
593 * NOTE: these values are only used for x-range and y-range but not region
594 * (which uses ked.data, i.e. rectf)
595 */
596 if (mode != BEZT_OK_VALUERANGE) {
597 ked.f1 = rectf.xmin;
598 ked.f2 = rectf.xmax;
599 }
600 else {
601 ked.f1 = rectf.ymin;
602 ked.f2 = rectf.ymax;
603 }
604
605 /* firstly, check if any keyframes will be hit by this */
606 if (ANIM_fcurve_keyframes_loop(&ked, fcu, NULL, ok_cb, NULL)) {
607 /* select keyframes that are in the appropriate places */
608 ANIM_fcurve_keyframes_loop(&ked, fcu, ok_cb, select_cb, NULL);
609
610 /* Only change selection of channel when the visibility of keyframes
611 * doesn't depend on this. */
612 if ((sipo->flag & SIPO_SELCUVERTSONLY) == 0) {
613 /* select the curve too now that curve will be touched */
614 if (selectmode == SELECT_ADD) {
615 fcu->flag |= FCURVE_SELECTED;
616 }
617 }
618 }
619
620 /* un-apply NLA mapping from all the keyframes */
621 if (adt) {
622 ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, incl_handles == 0);
623 }
624 }
625
626 /* cleanup */
627 ANIM_animdata_freelist(&anim_data);
628 }
629
630 /* ------------------- */
631
graphkeys_box_select_invoke(bContext * C,wmOperator * op,const wmEvent * event)632 static int graphkeys_box_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
633 {
634 bAnimContext ac;
635 if (ANIM_animdata_get_context(C, &ac) == 0) {
636 return OPERATOR_CANCELLED;
637 }
638
639 if (RNA_boolean_get(op->ptr, "tweak")) {
640 tNearestVertInfo *under_mouse = find_nearest_fcurve_vert(&ac, event->mval);
641 bool mouse_is_over_element = under_mouse != NULL;
642 if (under_mouse) {
643 MEM_freeN(under_mouse);
644 }
645
646 if (mouse_is_over_element) {
647 return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
648 }
649 }
650
651 return WM_gesture_box_invoke(C, op, event);
652 }
653
graphkeys_box_select_exec(bContext * C,wmOperator * op)654 static int graphkeys_box_select_exec(bContext *C, wmOperator *op)
655 {
656 bAnimContext ac;
657 rcti rect;
658 rctf rect_fl;
659 short mode = 0;
660
661 /* get editor data */
662 if (ANIM_animdata_get_context(C, &ac) == 0) {
663 return OPERATOR_CANCELLED;
664 }
665
666 const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
667 const int selectmode = (sel_op != SEL_OP_SUB) ? SELECT_ADD : SELECT_SUBTRACT;
668 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
669 deselect_graph_keys(&ac, 1, SELECT_SUBTRACT, true);
670 }
671
672 /* 'include_handles' from the operator specifies whether to include handles in the selection. */
673 const bool incl_handles = RNA_boolean_get(op->ptr, "include_handles");
674
675 /* get settings from operator */
676 WM_operator_properties_border_to_rcti(op, &rect);
677
678 /* selection 'mode' depends on whether box_select region only matters on one axis */
679 if (RNA_boolean_get(op->ptr, "axis_range")) {
680 /* mode depends on which axis of the range is larger to determine which axis to use
681 * - Checking this in region-space is fine, as it's fundamentally still going to be a
682 * different rect size.
683 * - The frame-range select option is favored over the channel one (x over y),
684 * as frame-range one is often used for tweaking timing when "blocking",
685 * while channels is not that useful.
686 */
687 if ((BLI_rcti_size_x(&rect)) >= (BLI_rcti_size_y(&rect))) {
688 mode = BEZT_OK_FRAMERANGE;
689 }
690 else {
691 mode = BEZT_OK_VALUERANGE;
692 }
693 }
694 else {
695 mode = BEZT_OK_REGION;
696 }
697
698 BLI_rctf_rcti_copy(&rect_fl, &rect);
699
700 /* apply box_select action */
701 box_select_graphkeys(&ac, &rect_fl, mode, selectmode, incl_handles, NULL);
702
703 /* send notifier that keyframe selection has changed */
704 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
705
706 return OPERATOR_FINISHED;
707 }
708
GRAPH_OT_select_box(wmOperatorType * ot)709 void GRAPH_OT_select_box(wmOperatorType *ot)
710 {
711 /* identifiers */
712 ot->name = "Box Select";
713 ot->idname = "GRAPH_OT_select_box";
714 ot->description = "Select all keyframes within the specified region";
715
716 /* api callbacks */
717 ot->invoke = graphkeys_box_select_invoke;
718 ot->exec = graphkeys_box_select_exec;
719 ot->modal = WM_gesture_box_modal;
720 ot->cancel = WM_gesture_box_cancel;
721
722 ot->poll = graphop_visible_keyframes_poll;
723
724 /* flags */
725 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
726
727 /* properties */
728 ot->prop = RNA_def_boolean(ot->srna, "axis_range", 0, "Axis Range", "");
729 RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE);
730
731 PropertyRNA *prop;
732 prop = RNA_def_boolean(ot->srna,
733 "include_handles",
734 true,
735 "Include Handles",
736 "Are handles tested individually against the selection criteria");
737 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
738
739 prop = RNA_def_boolean(
740 ot->srna, "tweak", 0, "Tweak", "Operator has been activated using a tweak event");
741 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
742
743 WM_operator_properties_gesture_box(ot);
744 WM_operator_properties_select_operation_simple(ot);
745 }
746
747 /* ------------------- */
748
graphkeys_lassoselect_exec(bContext * C,wmOperator * op)749 static int graphkeys_lassoselect_exec(bContext *C, wmOperator *op)
750 {
751 bAnimContext ac;
752
753 KeyframeEdit_LassoData data_lasso = {0};
754 rcti rect;
755 rctf rect_fl;
756
757 bool incl_handles;
758
759 /* get editor data */
760 if (ANIM_animdata_get_context(C, &ac) == 0) {
761 return OPERATOR_CANCELLED;
762 }
763
764 data_lasso.rectf_view = &rect_fl;
765 data_lasso.mcoords = WM_gesture_lasso_path_to_array(C, op, &data_lasso.mcoords_len);
766 if (data_lasso.mcoords == NULL) {
767 return OPERATOR_CANCELLED;
768 }
769
770 const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
771 const short selectmode = (sel_op != SEL_OP_SUB) ? SELECT_ADD : SELECT_SUBTRACT;
772 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
773 deselect_graph_keys(&ac, 0, SELECT_SUBTRACT, true);
774 }
775
776 {
777 SpaceGraph *sipo = (SpaceGraph *)ac.sl;
778 if (selectmode == SELECT_ADD) {
779 incl_handles = ((sipo->flag & SIPO_SELVHANDLESONLY) || (sipo->flag & SIPO_NOHANDLES)) == 0;
780 }
781 else {
782 incl_handles = (sipo->flag & SIPO_NOHANDLES) == 0;
783 }
784 }
785
786 /* get settings from operator */
787 BLI_lasso_boundbox(&rect, data_lasso.mcoords, data_lasso.mcoords_len);
788 BLI_rctf_rcti_copy(&rect_fl, &rect);
789
790 /* apply box_select action */
791 box_select_graphkeys(&ac, &rect_fl, BEZT_OK_REGION_LASSO, selectmode, incl_handles, &data_lasso);
792
793 MEM_freeN((void *)data_lasso.mcoords);
794
795 /* send notifier that keyframe selection has changed */
796 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
797
798 return OPERATOR_FINISHED;
799 }
800
GRAPH_OT_select_lasso(wmOperatorType * ot)801 void GRAPH_OT_select_lasso(wmOperatorType *ot)
802 {
803 /* identifiers */
804 ot->name = "Lasso Select";
805 ot->description = "Select keyframe points using lasso selection";
806 ot->idname = "GRAPH_OT_select_lasso";
807
808 /* api callbacks */
809 ot->invoke = WM_gesture_lasso_invoke;
810 ot->modal = WM_gesture_lasso_modal;
811 ot->exec = graphkeys_lassoselect_exec;
812 ot->poll = graphop_visible_keyframes_poll;
813 ot->cancel = WM_gesture_lasso_cancel;
814
815 /* flags */
816 ot->flag = OPTYPE_UNDO;
817
818 /* properties */
819 WM_operator_properties_gesture_lasso(ot);
820 WM_operator_properties_select_operation_simple(ot);
821 }
822
823 /* ------------------- */
824
graph_circle_select_exec(bContext * C,wmOperator * op)825 static int graph_circle_select_exec(bContext *C, wmOperator *op)
826 {
827 bAnimContext ac;
828 bool incl_handles = false;
829
830 KeyframeEdit_CircleData data = {0};
831 rctf rect_fl;
832
833 float x = RNA_int_get(op->ptr, "x");
834 float y = RNA_int_get(op->ptr, "y");
835 float radius = RNA_int_get(op->ptr, "radius");
836
837 /* get editor data */
838 if (ANIM_animdata_get_context(C, &ac) == 0) {
839 return OPERATOR_CANCELLED;
840 }
841
842 const eSelectOp sel_op = ED_select_op_modal(RNA_enum_get(op->ptr, "mode"),
843 WM_gesture_is_modal_first(op->customdata));
844 const short selectmode = (sel_op != SEL_OP_SUB) ? SELECT_ADD : SELECT_SUBTRACT;
845 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
846 deselect_graph_keys(&ac, 0, SELECT_SUBTRACT, true);
847 }
848
849 data.mval[0] = x;
850 data.mval[1] = y;
851 data.radius_squared = radius * radius;
852 data.rectf_view = &rect_fl;
853
854 rect_fl.xmin = x - radius;
855 rect_fl.xmax = x + radius;
856 rect_fl.ymin = y - radius;
857 rect_fl.ymax = y + radius;
858
859 {
860 SpaceGraph *sipo = (SpaceGraph *)ac.sl;
861 if (selectmode == SELECT_ADD) {
862 incl_handles = ((sipo->flag & SIPO_SELVHANDLESONLY) || (sipo->flag & SIPO_NOHANDLES)) == 0;
863 }
864 else {
865 incl_handles = (sipo->flag & SIPO_NOHANDLES) == 0;
866 }
867 }
868
869 /* apply box_select action */
870 box_select_graphkeys(&ac, &rect_fl, BEZT_OK_REGION_CIRCLE, selectmode, incl_handles, &data);
871
872 /* send notifier that keyframe selection has changed */
873 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
874
875 return OPERATOR_FINISHED;
876 }
877
GRAPH_OT_select_circle(wmOperatorType * ot)878 void GRAPH_OT_select_circle(wmOperatorType *ot)
879 {
880 ot->name = "Circle Select";
881 ot->description = "Select keyframe points using circle selection";
882 ot->idname = "GRAPH_OT_select_circle";
883
884 ot->invoke = WM_gesture_circle_invoke;
885 ot->modal = WM_gesture_circle_modal;
886 ot->exec = graph_circle_select_exec;
887 ot->poll = graphop_visible_keyframes_poll;
888 ot->cancel = WM_gesture_circle_cancel;
889
890 /* flags */
891 ot->flag = OPTYPE_UNDO;
892
893 /* properties */
894 WM_operator_properties_gesture_circle(ot);
895 WM_operator_properties_select_operation_simple(ot);
896 }
897
898 /* ******************** Column Select Operator **************************** */
899 /* This operator works in one of four ways:
900 * - 1) select all keyframes in the same frame as a selected one (KKEY)
901 * - 2) select all keyframes in the same frame as the current frame marker (CTRL-KKEY)
902 * - 3) select all keyframes in the same frame as a selected markers (SHIFT-KKEY)
903 * - 4) select all keyframes that occur between selected markers (ALT-KKEY)
904 */
905
906 /* defines for column-select mode */
907 static const EnumPropertyItem prop_column_select_types[] = {
908 {GRAPHKEYS_COLUMNSEL_KEYS, "KEYS", 0, "On Selected Keyframes", ""},
909 {GRAPHKEYS_COLUMNSEL_CFRA, "CFRA", 0, "On Current Frame", ""},
910 {GRAPHKEYS_COLUMNSEL_MARKERS_COLUMN, "MARKERS_COLUMN", 0, "On Selected Markers", ""},
911 {GRAPHKEYS_COLUMNSEL_MARKERS_BETWEEN,
912 "MARKERS_BETWEEN",
913 0,
914 "Between Min/Max Selected Markers",
915 ""},
916 {0, NULL, 0, NULL, NULL},
917 };
918
919 /* ------------------- */
920
921 /* Selects all visible keyframes between the specified markers */
922 /* TODO, this is almost an _exact_ duplicate of a function of the same name in action_select.c
923 * should de-duplicate - campbell */
markers_selectkeys_between(bAnimContext * ac)924 static void markers_selectkeys_between(bAnimContext *ac)
925 {
926 ListBase anim_data = {NULL, NULL};
927 bAnimListElem *ale;
928 int filter;
929
930 KeyframeEditFunc ok_cb, select_cb;
931 KeyframeEditData ked = {{NULL}};
932 float min, max;
933
934 /* get extreme markers */
935 ED_markers_get_minmax(ac->markers, 1, &min, &max);
936 min -= 0.5f;
937 max += 0.5f;
938
939 /* get editing funcs + data */
940 ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAMERANGE);
941 select_cb = ANIM_editkeyframes_select(SELECT_ADD);
942
943 ked.f1 = min;
944 ked.f2 = max;
945
946 /* filter data */
947 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
948 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
949
950 /* select keys in-between */
951 for (ale = anim_data.first; ale; ale = ale->next) {
952 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
953
954 if (adt) {
955 ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1);
956 ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
957 ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
958 }
959 else {
960 ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
961 }
962 }
963
964 /* Cleanup */
965 ANIM_animdata_freelist(&anim_data);
966 }
967
968 /* Selects all visible keyframes in the same frames as the specified elements */
columnselect_graph_keys(bAnimContext * ac,short mode)969 static void columnselect_graph_keys(bAnimContext *ac, short mode)
970 {
971 ListBase anim_data = {NULL, NULL};
972 bAnimListElem *ale;
973 int filter;
974
975 Scene *scene = ac->scene;
976 CfraElem *ce;
977 KeyframeEditFunc select_cb, ok_cb;
978 KeyframeEditData ked;
979
980 /* initialize keyframe editing data */
981 memset(&ked, 0, sizeof(KeyframeEditData));
982
983 /* build list of columns */
984 switch (mode) {
985 case GRAPHKEYS_COLUMNSEL_KEYS: /* list of selected keys */
986 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
987 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
988
989 for (ale = anim_data.first; ale; ale = ale->next) {
990 ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, bezt_to_cfraelem, NULL);
991 }
992
993 ANIM_animdata_freelist(&anim_data);
994 break;
995
996 case GRAPHKEYS_COLUMNSEL_CFRA: /* current frame */
997 /* make a single CfraElem for storing this */
998 ce = MEM_callocN(sizeof(CfraElem), "cfraElem");
999 BLI_addtail(&ked.list, ce);
1000
1001 ce->cfra = (float)CFRA;
1002 break;
1003
1004 case GRAPHKEYS_COLUMNSEL_MARKERS_COLUMN: /* list of selected markers */
1005 ED_markers_make_cfra_list(ac->markers, &ked.list, SELECT);
1006 break;
1007
1008 default: /* invalid option */
1009 return;
1010 }
1011
1012 /* set up BezTriple edit callbacks */
1013 select_cb = ANIM_editkeyframes_select(SELECT_ADD);
1014 ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAME);
1015
1016 /* loop through all of the keys and select additional keyframes
1017 * based on the keys found to be selected above
1018 */
1019 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
1020 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1021
1022 for (ale = anim_data.first; ale; ale = ale->next) {
1023 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
1024
1025 /* loop over cfraelems (stored in the KeyframeEditData->list)
1026 * - we need to do this here, as we can apply fewer NLA-mapping conversions
1027 */
1028 for (ce = ked.list.first; ce; ce = ce->next) {
1029 /* set frame for validation callback to refer to */
1030 ked.f1 = BKE_nla_tweakedit_remap(adt, ce->cfra, NLATIME_CONVERT_UNMAP);
1031
1032 /* select elements with frame number matching cfraelem */
1033 ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
1034 }
1035 }
1036
1037 /* free elements */
1038 BLI_freelistN(&ked.list);
1039 ANIM_animdata_freelist(&anim_data);
1040 }
1041
1042 /* ------------------- */
1043
graphkeys_columnselect_exec(bContext * C,wmOperator * op)1044 static int graphkeys_columnselect_exec(bContext *C, wmOperator *op)
1045 {
1046 bAnimContext ac;
1047 short mode;
1048
1049 /* get editor data */
1050 if (ANIM_animdata_get_context(C, &ac) == 0) {
1051 return OPERATOR_CANCELLED;
1052 }
1053
1054 /* action to take depends on the mode */
1055 mode = RNA_enum_get(op->ptr, "mode");
1056
1057 if (mode == GRAPHKEYS_COLUMNSEL_MARKERS_BETWEEN) {
1058 markers_selectkeys_between(&ac);
1059 }
1060 else {
1061 columnselect_graph_keys(&ac, mode);
1062 }
1063
1064 /* set notifier that keyframe selection has changed */
1065 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
1066
1067 return OPERATOR_FINISHED;
1068 }
1069
GRAPH_OT_select_column(wmOperatorType * ot)1070 void GRAPH_OT_select_column(wmOperatorType *ot)
1071 {
1072 /* identifiers */
1073 ot->name = "Select All";
1074 ot->idname = "GRAPH_OT_select_column";
1075 ot->description = "Select all keyframes on the specified frame(s)";
1076
1077 /* api callbacks */
1078 ot->exec = graphkeys_columnselect_exec;
1079 ot->poll = graphop_visible_keyframes_poll;
1080
1081 /* flags */
1082 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1083
1084 /* props */
1085 ot->prop = RNA_def_enum(ot->srna, "mode", prop_column_select_types, 0, "Mode", "");
1086 }
1087
1088 /* ******************** Select Linked Operator *********************** */
1089
graphkeys_select_linked_exec(bContext * C,wmOperator * UNUSED (op))1090 static int graphkeys_select_linked_exec(bContext *C, wmOperator *UNUSED(op))
1091 {
1092 bAnimContext ac;
1093
1094 ListBase anim_data = {NULL, NULL};
1095 bAnimListElem *ale;
1096 int filter;
1097
1098 KeyframeEditFunc ok_cb = ANIM_editkeyframes_ok(BEZT_OK_SELECTED);
1099 KeyframeEditFunc sel_cb = ANIM_editkeyframes_select(SELECT_ADD);
1100
1101 /* get editor data */
1102 if (ANIM_animdata_get_context(C, &ac) == 0) {
1103 return OPERATOR_CANCELLED;
1104 }
1105
1106 /* loop through all of the keys and select additional keyframes based on these */
1107 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
1108 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1109
1110 for (ale = anim_data.first; ale; ale = ale->next) {
1111 FCurve *fcu = (FCurve *)ale->key_data;
1112
1113 /* check if anything selected? */
1114 if (ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, ok_cb, NULL)) {
1115 /* select every keyframe in this curve then */
1116 ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, sel_cb, NULL);
1117 }
1118 }
1119
1120 /* Cleanup */
1121 ANIM_animdata_freelist(&anim_data);
1122
1123 /* set notifier that keyframe selection has changed */
1124 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
1125
1126 return OPERATOR_FINISHED;
1127 }
1128
GRAPH_OT_select_linked(wmOperatorType * ot)1129 void GRAPH_OT_select_linked(wmOperatorType *ot)
1130 {
1131 /* identifiers */
1132 ot->name = "Select Linked";
1133 ot->idname = "GRAPH_OT_select_linked";
1134 ot->description = "Select keyframes occurring in the same F-Curves as selected ones";
1135
1136 /* api callbacks */
1137 ot->exec = graphkeys_select_linked_exec;
1138 ot->poll = graphop_visible_keyframes_poll;
1139
1140 /* flags */
1141 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1142 }
1143
1144 /* ******************** Select More/Less Operators *********************** */
1145
1146 /* Common code to perform selection */
select_moreless_graph_keys(bAnimContext * ac,short mode)1147 static void select_moreless_graph_keys(bAnimContext *ac, short mode)
1148 {
1149 ListBase anim_data = {NULL, NULL};
1150 bAnimListElem *ale;
1151 int filter;
1152
1153 KeyframeEditData ked;
1154 KeyframeEditFunc build_cb;
1155
1156 /* init selmap building data */
1157 build_cb = ANIM_editkeyframes_buildselmap(mode);
1158 memset(&ked, 0, sizeof(KeyframeEditData));
1159
1160 /* loop through all of the keys and select additional keyframes based on these */
1161 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
1162 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1163
1164 for (ale = anim_data.first; ale; ale = ale->next) {
1165 FCurve *fcu = (FCurve *)ale->key_data;
1166
1167 /* only continue if F-Curve has keyframes */
1168 if (fcu->bezt == NULL) {
1169 continue;
1170 }
1171
1172 /* build up map of whether F-Curve's keyframes should be selected or not */
1173 ked.data = MEM_callocN(fcu->totvert, "selmap graphEdit");
1174 ANIM_fcurve_keyframes_loop(&ked, fcu, NULL, build_cb, NULL);
1175
1176 /* based on this map, adjust the selection status of the keyframes */
1177 ANIM_fcurve_keyframes_loop(&ked, fcu, NULL, bezt_selmap_flush, NULL);
1178
1179 /* free the selmap used here */
1180 MEM_freeN(ked.data);
1181 ked.data = NULL;
1182 }
1183
1184 /* Cleanup */
1185 ANIM_animdata_freelist(&anim_data);
1186 }
1187
1188 /* ----------------- */
1189
graphkeys_select_more_exec(bContext * C,wmOperator * UNUSED (op))1190 static int graphkeys_select_more_exec(bContext *C, wmOperator *UNUSED(op))
1191 {
1192 bAnimContext ac;
1193
1194 /* get editor data */
1195 if (ANIM_animdata_get_context(C, &ac) == 0) {
1196 return OPERATOR_CANCELLED;
1197 }
1198
1199 /* perform select changes */
1200 select_moreless_graph_keys(&ac, SELMAP_MORE);
1201
1202 /* set notifier that keyframe selection has changed */
1203 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
1204
1205 return OPERATOR_FINISHED;
1206 }
1207
GRAPH_OT_select_more(wmOperatorType * ot)1208 void GRAPH_OT_select_more(wmOperatorType *ot)
1209 {
1210 /* identifiers */
1211 ot->name = "Select More";
1212 ot->idname = "GRAPH_OT_select_more";
1213 ot->description = "Select keyframes beside already selected ones";
1214
1215 /* api callbacks */
1216 ot->exec = graphkeys_select_more_exec;
1217 ot->poll = graphop_visible_keyframes_poll;
1218
1219 /* flags */
1220 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1221 }
1222
1223 /* ----------------- */
1224
graphkeys_select_less_exec(bContext * C,wmOperator * UNUSED (op))1225 static int graphkeys_select_less_exec(bContext *C, wmOperator *UNUSED(op))
1226 {
1227 bAnimContext ac;
1228
1229 /* get editor data */
1230 if (ANIM_animdata_get_context(C, &ac) == 0) {
1231 return OPERATOR_CANCELLED;
1232 }
1233
1234 /* perform select changes */
1235 select_moreless_graph_keys(&ac, SELMAP_LESS);
1236
1237 /* set notifier that keyframe selection has changed */
1238 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
1239
1240 return OPERATOR_FINISHED;
1241 }
1242
GRAPH_OT_select_less(wmOperatorType * ot)1243 void GRAPH_OT_select_less(wmOperatorType *ot)
1244 {
1245 /* identifiers */
1246 ot->name = "Select Less";
1247 ot->idname = "GRAPH_OT_select_less";
1248 ot->description = "Deselect keyframes on ends of selection islands";
1249
1250 /* api callbacks */
1251 ot->exec = graphkeys_select_less_exec;
1252 ot->poll = graphop_visible_keyframes_poll;
1253
1254 /* flags */
1255 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1256 }
1257
1258 /* ******************** Select Left/Right Operator ************************* */
1259 /* Select keyframes left/right of the current frame indicator */
1260
1261 /* defines for left-right select tool */
1262 static const EnumPropertyItem prop_graphkeys_leftright_select_types[] = {
1263 {GRAPHKEYS_LRSEL_TEST, "CHECK", 0, "Check if Select Left or Right", ""},
1264 {GRAPHKEYS_LRSEL_LEFT, "LEFT", 0, "Before current frame", ""},
1265 {GRAPHKEYS_LRSEL_RIGHT, "RIGHT", 0, "After current frame", ""},
1266 {0, NULL, 0, NULL, NULL},
1267 };
1268
1269 /* --------------------------------- */
1270
graphkeys_select_leftright(bAnimContext * ac,short leftright,short select_mode)1271 static void graphkeys_select_leftright(bAnimContext *ac, short leftright, short select_mode)
1272 {
1273 ListBase anim_data = {NULL, NULL};
1274 bAnimListElem *ale;
1275 int filter;
1276
1277 KeyframeEditFunc ok_cb, select_cb;
1278 KeyframeEditData ked = {{NULL}};
1279 Scene *scene = ac->scene;
1280
1281 /* if select mode is replace, deselect all keyframes (and channels) first */
1282 if (select_mode == SELECT_REPLACE) {
1283 select_mode = SELECT_ADD;
1284
1285 /* - deselect all other keyframes, so that just the newly selected remain
1286 * - channels aren't deselected, since we don't re-select any as a consequence
1287 */
1288 deselect_graph_keys(ac, 0, SELECT_SUBTRACT, false);
1289 }
1290
1291 /* set callbacks and editing data */
1292 ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAMERANGE);
1293 select_cb = ANIM_editkeyframes_select(select_mode);
1294
1295 if (leftright == GRAPHKEYS_LRSEL_LEFT) {
1296 ked.f1 = MINAFRAMEF;
1297 ked.f2 = (float)(CFRA + 0.1f);
1298 }
1299 else {
1300 ked.f1 = (float)(CFRA - 0.1f);
1301 ked.f2 = MAXFRAMEF;
1302 }
1303
1304 /* filter data */
1305 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_NODUPLIS);
1306 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1307
1308 /* select keys */
1309 for (ale = anim_data.first; ale; ale = ale->next) {
1310 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
1311
1312 if (adt) {
1313 ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1);
1314 ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
1315 ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
1316 }
1317 else {
1318 ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
1319 }
1320 }
1321
1322 /* Cleanup */
1323 ANIM_animdata_freelist(&anim_data);
1324 }
1325
1326 /* ----------------- */
1327
graphkeys_select_leftright_exec(bContext * C,wmOperator * op)1328 static int graphkeys_select_leftright_exec(bContext *C, wmOperator *op)
1329 {
1330 bAnimContext ac;
1331 short leftright = RNA_enum_get(op->ptr, "mode");
1332 short selectmode;
1333
1334 /* get editor data */
1335 if (ANIM_animdata_get_context(C, &ac) == 0) {
1336 return OPERATOR_CANCELLED;
1337 }
1338
1339 /* select mode is either replace (deselect all, then add) or add/extend */
1340 if (RNA_boolean_get(op->ptr, "extend")) {
1341 selectmode = SELECT_INVERT;
1342 }
1343 else {
1344 selectmode = SELECT_REPLACE;
1345 }
1346
1347 /* if "test" mode is set, we don't have any info to set this with */
1348 if (leftright == GRAPHKEYS_LRSEL_TEST) {
1349 return OPERATOR_CANCELLED;
1350 }
1351
1352 /* do the selecting now */
1353 graphkeys_select_leftright(&ac, leftright, selectmode);
1354
1355 /* set notifier that keyframe selection (and channels too) have changed */
1356 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
1357 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL);
1358
1359 return OPERATOR_FINISHED;
1360 }
1361
graphkeys_select_leftright_invoke(bContext * C,wmOperator * op,const wmEvent * event)1362 static int graphkeys_select_leftright_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1363 {
1364 bAnimContext ac;
1365 short leftright = RNA_enum_get(op->ptr, "mode");
1366
1367 /* get editor data */
1368 if (ANIM_animdata_get_context(C, &ac) == 0) {
1369 return OPERATOR_CANCELLED;
1370 }
1371
1372 /* handle mode-based testing */
1373 if (leftright == GRAPHKEYS_LRSEL_TEST) {
1374 Scene *scene = ac.scene;
1375 ARegion *region = ac.region;
1376 View2D *v2d = ®ion->v2d;
1377 float x;
1378
1379 /* determine which side of the current frame mouse is on */
1380 x = UI_view2d_region_to_view_x(v2d, event->mval[0]);
1381 if (x < CFRA) {
1382 RNA_enum_set(op->ptr, "mode", GRAPHKEYS_LRSEL_LEFT);
1383 }
1384 else {
1385 RNA_enum_set(op->ptr, "mode", GRAPHKEYS_LRSEL_RIGHT);
1386 }
1387 }
1388
1389 /* perform selection */
1390 return graphkeys_select_leftright_exec(C, op);
1391 }
1392
GRAPH_OT_select_leftright(wmOperatorType * ot)1393 void GRAPH_OT_select_leftright(wmOperatorType *ot)
1394 {
1395 PropertyRNA *prop;
1396
1397 /* identifiers */
1398 ot->name = "Select Left/Right";
1399 ot->idname = "GRAPH_OT_select_leftright";
1400 ot->description = "Select keyframes to the left or the right of the current frame";
1401
1402 /* api callbacks */
1403 ot->invoke = graphkeys_select_leftright_invoke;
1404 ot->exec = graphkeys_select_leftright_exec;
1405 ot->poll = graphop_visible_keyframes_poll;
1406
1407 /* flags */
1408 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1409
1410 /* id-props */
1411 ot->prop = RNA_def_enum(
1412 ot->srna, "mode", prop_graphkeys_leftright_select_types, GRAPHKEYS_LRSEL_TEST, "Mode", "");
1413 RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE);
1414
1415 prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", "");
1416 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1417 }
1418
1419 /* ******************** Mouse-Click Select Operator *********************** */
1420 /* This operator works in one of three ways:
1421 * - 1) keyframe under mouse - no special modifiers
1422 * - 2) all keyframes on the same side of current frame indicator as mouse - ALT modifier
1423 * - 3) column select all keyframes in frame under mouse - CTRL modifier
1424 *
1425 * In addition to these basic options, the SHIFT modifier can be used to toggle the
1426 * selection mode between replacing the selection (without) and inverting the selection (with).
1427 */
1428
1429 /* option 1) select keyframe directly under mouse */
mouse_graph_keys(bAnimContext * ac,const int mval[2],eEditKeyframes_Select select_mode,const bool deselect_all,const bool curves_only,bool wait_to_deselect_others)1430 static int mouse_graph_keys(bAnimContext *ac,
1431 const int mval[2],
1432 eEditKeyframes_Select select_mode,
1433 const bool deselect_all,
1434 const bool curves_only,
1435 bool wait_to_deselect_others)
1436 {
1437 SpaceGraph *sipo = (SpaceGraph *)ac->sl;
1438 tNearestVertInfo *nvi;
1439 BezTriple *bezt = NULL;
1440 bool run_modal = false;
1441
1442 /* find the beztriple that we're selecting, and the handle that was clicked on */
1443 nvi = find_nearest_fcurve_vert(ac, mval);
1444
1445 if (select_mode != SELECT_REPLACE) {
1446 /* The modal execution to delay deselecting other items is only needed for normal click
1447 * selection, i.e. for SELECT_REPLACE. */
1448 wait_to_deselect_others = false;
1449 }
1450
1451 sipo->runtime.flag &= ~(SIPO_RUNTIME_FLAG_TWEAK_HANDLES_LEFT |
1452 SIPO_RUNTIME_FLAG_TWEAK_HANDLES_RIGHT);
1453
1454 const bool already_selected =
1455 (nvi != NULL) && (((nvi->hpoint == NEAREST_HANDLE_KEY) && (nvi->bezt->f2 & SELECT)) ||
1456 ((nvi->hpoint == NEAREST_HANDLE_LEFT) && (nvi->bezt->f1 & SELECT)) ||
1457 ((nvi->hpoint == NEAREST_HANDLE_RIGHT) && (nvi->bezt->f3 & SELECT)));
1458
1459 if (wait_to_deselect_others && nvi && already_selected) {
1460 run_modal = true;
1461 }
1462 /* For replacing selection, if we have something to select, we have to clear existing selection.
1463 * The same goes if we found nothing to select, and deselect_all is true
1464 * (deselect on nothing behavior). */
1465 else if ((nvi != NULL && select_mode == SELECT_REPLACE) || (nvi == NULL && deselect_all)) {
1466 /* reset selection mode */
1467 select_mode = SELECT_ADD;
1468
1469 /* deselect all other keyframes (+ F-Curves too) */
1470 deselect_graph_keys(ac, 0, SELECT_SUBTRACT, true);
1471
1472 /* deselect other channels too, but only only do this if
1473 * selection of channel when the visibility of keyframes
1474 * doesn't depend on this
1475 */
1476 if ((sipo->flag & SIPO_SELCUVERTSONLY) == 0) {
1477 ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_CLEAR);
1478 }
1479 }
1480
1481 if (nvi == NULL) {
1482 return deselect_all ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
1483 }
1484
1485 /* if points can be selected on this F-Curve */
1486 /* TODO: what about those with no keyframes? */
1487 bool something_was_selected = false;
1488 if (!curves_only && ((nvi->fcu->flag & FCURVE_PROTECTED) == 0)) {
1489 /* only if there's keyframe */
1490 if (nvi->bezt) {
1491 bezt = nvi->bezt; /* Used to check `bezt` selection is set. */
1492 if (select_mode == SELECT_INVERT) {
1493 if (nvi->hpoint == NEAREST_HANDLE_KEY) {
1494 bezt->f2 ^= SELECT;
1495 something_was_selected = (bezt->f2 & SELECT);
1496 }
1497 else if (nvi->hpoint == NEAREST_HANDLE_LEFT) {
1498 /* toggle selection */
1499 bezt->f1 ^= SELECT;
1500 something_was_selected = (bezt->f1 & SELECT);
1501 }
1502 else {
1503 /* toggle selection */
1504 bezt->f3 ^= SELECT;
1505 something_was_selected = (bezt->f3 & SELECT);
1506 }
1507 }
1508 else {
1509 if (nvi->hpoint == NEAREST_HANDLE_KEY) {
1510 bezt->f2 |= SELECT;
1511 }
1512 else if (nvi->hpoint == NEAREST_HANDLE_LEFT) {
1513 bezt->f1 |= SELECT;
1514 }
1515 else {
1516 bezt->f3 |= SELECT;
1517 }
1518 something_was_selected = true;
1519 }
1520
1521 if (!run_modal && BEZT_ISSEL_ANY(bezt)) {
1522 const bool may_activate = !already_selected ||
1523 BKE_fcurve_active_keyframe_index(nvi->fcu) ==
1524 FCURVE_ACTIVE_KEYFRAME_NONE;
1525 if (may_activate) {
1526 BKE_fcurve_active_keyframe_set(nvi->fcu, bezt);
1527 }
1528 }
1529 }
1530 else if (nvi->fpt) {
1531 /* TODO: need to handle sample points */
1532 }
1533 }
1534 else {
1535 KeyframeEditFunc select_cb;
1536 KeyframeEditData ked;
1537
1538 /* initialize keyframe editing data */
1539 memset(&ked, 0, sizeof(KeyframeEditData));
1540
1541 /* set up BezTriple edit callbacks */
1542 select_cb = ANIM_editkeyframes_select(select_mode);
1543
1544 /* select all keyframes */
1545 ANIM_fcurve_keyframes_loop(&ked, nvi->fcu, NULL, select_cb, NULL);
1546 }
1547
1548 /* only change selection of channel when the visibility of keyframes doesn't depend on this */
1549 if ((sipo->flag & SIPO_SELCUVERTSONLY) == 0) {
1550 /* select or deselect curve? */
1551 if (bezt) {
1552 /* take selection status from item that got hit, to prevent flip/flop on channel
1553 * selection status when shift-selecting (i.e. "SELECT_INVERT") points
1554 */
1555 if (BEZT_ISSEL_ANY(bezt)) {
1556 nvi->fcu->flag |= FCURVE_SELECTED;
1557 }
1558 else {
1559 nvi->fcu->flag &= ~FCURVE_SELECTED;
1560 }
1561 }
1562 else {
1563 /* Didn't hit any channel,
1564 * so just apply that selection mode to the curve's selection status. */
1565 if (select_mode == SELECT_INVERT) {
1566 nvi->fcu->flag ^= FCURVE_SELECTED;
1567 }
1568 else if (select_mode == SELECT_ADD) {
1569 nvi->fcu->flag |= FCURVE_SELECTED;
1570 }
1571 }
1572 }
1573
1574 /* Set active F-Curve when something was actually selected (so not on a deselect), except when
1575 * dragging the selected keys. Needs to be called with (sipo->flag & SIPO_SELCUVERTSONLY),
1576 * otherwise the active flag won't be set T26452. */
1577 if (!run_modal && (nvi->fcu->flag & FCURVE_SELECTED) && something_was_selected) {
1578 /* NOTE: Sync the filter flags with findnearest_fcurve_vert. */
1579 int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
1580 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, nvi->fcu, nvi->ctype);
1581 }
1582
1583 if (nvi->hpoint == NEAREST_HANDLE_LEFT) {
1584 sipo->runtime.flag |= SIPO_RUNTIME_FLAG_TWEAK_HANDLES_LEFT;
1585 }
1586 else if (nvi->hpoint == NEAREST_HANDLE_RIGHT) {
1587 sipo->runtime.flag |= SIPO_RUNTIME_FLAG_TWEAK_HANDLES_RIGHT;
1588 }
1589
1590 /* free temp sample data for filtering */
1591 MEM_freeN(nvi);
1592
1593 return run_modal ? OPERATOR_RUNNING_MODAL : OPERATOR_FINISHED;
1594 }
1595
1596 /* Option 2) Selects all the keyframes on either side of the current frame
1597 * (depends on which side the mouse is on) */
1598 /* (see graphkeys_select_leftright) */
1599
1600 /* Option 3) Selects all visible keyframes in the same frame as the mouse click */
graphkeys_mselect_column(bAnimContext * ac,const int mval[2],eEditKeyframes_Select select_mode,bool wait_to_deselect_others)1601 static int graphkeys_mselect_column(bAnimContext *ac,
1602 const int mval[2],
1603 eEditKeyframes_Select select_mode,
1604 bool wait_to_deselect_others)
1605 {
1606 ListBase anim_data = {NULL, NULL};
1607 bAnimListElem *ale;
1608 int filter;
1609 bool run_modal = false;
1610
1611 KeyframeEditFunc select_cb, ok_cb;
1612 KeyframeEditData ked;
1613 tNearestVertInfo *nvi;
1614 float selx = (float)ac->scene->r.cfra;
1615
1616 /* find the beztriple that we're selecting, and the handle that was clicked on */
1617 nvi = find_nearest_fcurve_vert(ac, mval);
1618
1619 /* check if anything to select */
1620 if (nvi == NULL) {
1621 return OPERATOR_CANCELLED;
1622 }
1623
1624 /* get frame number on which elements should be selected */
1625 /* TODO: should we restrict to integer frames only? */
1626 selx = nvi->frame;
1627
1628 if (select_mode != SELECT_REPLACE) {
1629 /* Doesn't need to deselect anything -> Pass. */
1630 }
1631 else if (wait_to_deselect_others && (nvi->bezt->f2 & SELECT)) {
1632 run_modal = true;
1633 }
1634 /* If select mode is replace (and we don't do delayed deselection on mouse release), deselect all
1635 * keyframes first. */
1636 else {
1637 /* reset selection mode to add to selection */
1638 select_mode = SELECT_ADD;
1639
1640 /* - deselect all other keyframes, so that just the newly selected remain
1641 * - channels aren't deselected, since we don't re-select any as a consequence
1642 */
1643 deselect_graph_keys(ac, 0, SELECT_SUBTRACT, false);
1644 }
1645
1646 /* initialize keyframe editing data */
1647 memset(&ked, 0, sizeof(KeyframeEditData));
1648
1649 /* set up BezTriple edit callbacks */
1650 select_cb = ANIM_editkeyframes_select(select_mode);
1651 ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAME);
1652
1653 /* loop through all of the keys and select additional keyframes
1654 * based on the keys found to be selected above
1655 */
1656 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
1657 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1658
1659 for (ale = anim_data.first; ale; ale = ale->next) {
1660 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
1661
1662 /* set frame for validation callback to refer to */
1663 if (adt) {
1664 ked.f1 = BKE_nla_tweakedit_remap(adt, selx, NLATIME_CONVERT_UNMAP);
1665 }
1666 else {
1667 ked.f1 = selx;
1668 }
1669
1670 /* select elements with frame number matching cfra */
1671 ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
1672 }
1673
1674 /* free elements */
1675 MEM_freeN(nvi);
1676 BLI_freelistN(&ked.list);
1677 ANIM_animdata_freelist(&anim_data);
1678
1679 return run_modal ? OPERATOR_RUNNING_MODAL : OPERATOR_FINISHED;
1680 }
1681
1682 /* ------------------- */
1683
1684 /* handle clicking */
graphkeys_clickselect_exec(bContext * C,wmOperator * op)1685 static int graphkeys_clickselect_exec(bContext *C, wmOperator *op)
1686 {
1687 bAnimContext ac;
1688
1689 /* get editor data */
1690 if (ANIM_animdata_get_context(C, &ac) == 0) {
1691 return OPERATOR_CANCELLED;
1692 }
1693
1694 /* select mode is either replace (deselect all, then add) or add/extend */
1695 const short selectmode = RNA_boolean_get(op->ptr, "extend") ? SELECT_INVERT : SELECT_REPLACE;
1696 const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all");
1697 /* See #WM_operator_properties_generic_select() for a detailed description of the how and why of
1698 * this. */
1699 const bool wait_to_deselect_others = RNA_boolean_get(op->ptr, "wait_to_deselect_others");
1700 int mval[2];
1701 int ret_val;
1702
1703 mval[0] = RNA_int_get(op->ptr, "mouse_x");
1704 mval[1] = RNA_int_get(op->ptr, "mouse_y");
1705
1706 /* figure out action to take */
1707 if (RNA_boolean_get(op->ptr, "column")) {
1708 /* select all keyframes in the same frame as the one that was under the mouse */
1709 ret_val = graphkeys_mselect_column(&ac, mval, selectmode, wait_to_deselect_others);
1710 }
1711 else if (RNA_boolean_get(op->ptr, "curves")) {
1712 /* select all keyframes in the same F-Curve as the one under the mouse */
1713 ret_val = mouse_graph_keys(&ac, mval, selectmode, deselect_all, true, wait_to_deselect_others);
1714 }
1715 else {
1716 /* select keyframe under mouse */
1717 ret_val = mouse_graph_keys(
1718 &ac, mval, selectmode, deselect_all, false, wait_to_deselect_others);
1719 }
1720
1721 /* set notifier that keyframe selection (and also channel selection in some cases) has
1722 * changed */
1723 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
1724 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL);
1725
1726 /* for tweak grab to work */
1727 return ret_val | OPERATOR_PASS_THROUGH;
1728 }
1729
GRAPH_OT_clickselect(wmOperatorType * ot)1730 void GRAPH_OT_clickselect(wmOperatorType *ot)
1731 {
1732 PropertyRNA *prop;
1733
1734 /* identifiers */
1735 ot->name = "Select Keyframes";
1736 ot->idname = "GRAPH_OT_clickselect";
1737 ot->description = "Select keyframes by clicking on them";
1738
1739 /* callbacks */
1740 ot->poll = graphop_visible_keyframes_poll;
1741 ot->exec = graphkeys_clickselect_exec;
1742 ot->invoke = WM_generic_select_invoke;
1743 ot->modal = WM_generic_select_modal;
1744
1745 /* flags */
1746 ot->flag = OPTYPE_UNDO;
1747
1748 /* properties */
1749 WM_operator_properties_generic_select(ot);
1750 prop = RNA_def_boolean(ot->srna,
1751 "extend",
1752 0,
1753 "Extend Select",
1754 "Toggle keyframe selection instead of leaving newly selected "
1755 "keyframes only"); /* SHIFTKEY */
1756 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1757
1758 prop = RNA_def_boolean(ot->srna,
1759 "deselect_all",
1760 false,
1761 "Deselect On Nothing",
1762 "Deselect all when nothing under the cursor");
1763 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1764
1765 prop = RNA_def_boolean(ot->srna,
1766 "column",
1767 0,
1768 "Column Select",
1769 "Select all keyframes that occur on the same frame as the one under "
1770 "the mouse"); /* ALTKEY */
1771 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1772
1773 prop = RNA_def_boolean(ot->srna,
1774 "curves",
1775 0,
1776 "Only Curves",
1777 "Select all the keyframes in the curve"); /* CTRLKEY + ALTKEY */
1778 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1779 }
1780
1781 /* ************************************************************************** */
1782