2 * ***** BEGIN GPL LICENSE BLOCK *****
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * The Original Code is Copyright (C) 2008 Blender Foundation
20 * Contributor(s): Joshua Leung
22 * ***** END GPL LICENSE BLOCK *****
25 /** \file blender/editors/space_graph/graph_select.c
35 #include "MEM_guardedalloc.h"
37 #include "BLI_blenlib.h"
39 #include "BLI_utildefines.h"
40 #include "BLI_lasso.h"
42 #include "DNA_anim_types.h"
43 #include "DNA_screen_types.h"
44 #include "DNA_scene_types.h"
45 #include "DNA_space_types.h"
47 #include "RNA_access.h"
48 #include "RNA_define.h"
50 #include "BKE_fcurve.h"
52 #include "BKE_context.h"
54 #include "UI_view2d.h"
56 #include "ED_anim_api.h"
57 #include "ED_keyframes_edit.h"
58 #include "ED_markers.h"
63 #include "graph_intern.h"
66 /* ************************************************************************** */
69 static void graphkeys_auto_view(bContext *C)
71 const SpaceIpo *sipo = CTX_wm_space_graph(C);
73 if (sipo && sipo->flag & SIPO_AUTO_VIEW_SELECTED) {
74 WM_operator_name_call(C, "GRAPH_OT_view_selected", WM_OP_INVOKE_DEFAULT, NULL);
78 /* ******************** Deselect All Operator ***************************** */
79 /* This operator works in one of three ways:
80 * 1) (de)select all (AKEY) - test if select all or deselect all
81 * 2) invert all (CTRL-IKEY) - invert selection of all keyframes
82 * 3) (de)select all - no testing is done; only for use internal tools as normal function...
85 /* Deselects keyframes in the Graph Editor
86 * - This is called by the deselect all operator, as well as other ones!
88 * - test: check if select or deselect all
89 * - sel: how to select keyframes
93 * - do_channels: whether to affect selection status of channels
95 static short deselect_graph_keys(bAnimContext *ac, short test, short sel, short do_channels)
97 ListBase anim_data = {NULL, NULL};
101 SpaceIpo *sipo = (SpaceIpo *)ac->sl;
102 KeyframeEditData ked = {{NULL}};
103 KeyframeEditFunc test_cb, sel_cb;
105 /* determine type-based settings */
106 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
109 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
111 /* init BezTriple looping data */
112 test_cb = ANIM_editkeyframes_ok(BEZT_OK_SELECTED);
114 /* See if we should be selecting or deselecting */
116 for (ale = anim_data.first; ale; ale = ale->next) {
117 if (ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, test_cb, NULL)) {
118 sel = SELECT_SUBTRACT;
124 /* convert sel to selectmode, and use that to get editor */
125 sel_cb = ANIM_editkeyframes_select(sel);
127 /* Now set the flags */
128 for (ale = anim_data.first; ale; ale = ale->next) {
129 FCurve *fcu = (FCurve *)ale->key_data;
131 /* Keyframes First */
132 ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, sel_cb, NULL);
134 /* affect channel selection status? */
136 /* only change selection of channel when the visibility of keyframes doesn't depend on this */
137 if ((sipo->flag & SIPO_SELCUVERTSONLY) == 0) {
138 /* deactivate the F-Curve, and deselect if deselecting keyframes.
139 * otherwise select the F-Curve too since we've selected all the keyframes
141 if (sel == SELECT_SUBTRACT)
142 fcu->flag &= ~FCURVE_SELECTED;
144 fcu->flag |= FCURVE_SELECTED;
147 /* always deactivate all F-Curves if we perform batch ops for selection */
148 fcu->flag &= ~FCURVE_ACTIVE;
153 ANIM_animdata_freelist(&anim_data);
158 /* ------------------- */
160 static int graphkeys_deselectall_exec(bContext *C, wmOperator *op)
163 bAnimListElem *ale_active = NULL;
166 /* get editor data */
167 if (ANIM_animdata_get_context(C, &ac) == 0)
168 return OPERATOR_CANCELLED;
170 /* find active F-Curve, and preserve this for later
171 * or else it becomes annoying with the current active
172 * curve keeps fading out even while you're editing it
174 ale_active = get_active_fcurve_channel(&ac);
176 /* 'standard' behavior - check if selected, then apply relevant selection */
177 if (RNA_boolean_get(op->ptr, "invert"))
178 sel = deselect_graph_keys(&ac, 0, SELECT_INVERT, true);
180 sel = deselect_graph_keys(&ac, 1, SELECT_ADD, true);
182 /* restore active F-Curve... */
184 FCurve *fcu = (FCurve *)ale_active->data;
186 /* all others should not be disabled, so we should be able to just set this directly...
187 * - selection needs to be set too, or else this won't work...
189 fcu->flag |= (FCURVE_SELECTED | FCURVE_ACTIVE);
191 MEM_freeN(ale_active);
195 if (sel != SELECT_SUBTRACT)
196 graphkeys_auto_view(C);
198 /* set notifier that things have changed */
199 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
201 return OPERATOR_FINISHED;
204 void GRAPH_OT_select_all_toggle(wmOperatorType *ot)
207 ot->name = "Select All";
208 ot->idname = "GRAPH_OT_select_all_toggle";
209 ot->description = "Toggle selection of all keyframes";
212 ot->exec = graphkeys_deselectall_exec;
213 ot->poll = graphop_visible_keyframes_poll;
216 ot->flag = OPTYPE_REGISTER /*|OPTYPE_UNDO*/;
219 ot->prop = RNA_def_boolean(ot->srna, "invert", 0, "Invert", "");
222 /* ******************** Border Select Operator **************************** */
223 /* This operator currently works in one of three ways:
224 * -> BKEY - 1) all keyframes within region are selected (validation with BEZT_OK_REGION)
225 * -> ALT-BKEY - depending on which axis of the region was larger...
226 * -> 2) x-axis, so select all frames within frame range (validation with BEZT_OK_FRAMERANGE)
227 * -> 3) y-axis, so select all frames within channels that region included (validation with BEZT_OK_VALUERANGE)
230 /* Borderselect only selects keyframes now, as overshooting handles often get caught too,
231 * which means that they may be inadvertently moved as well. However, incl_handles overrides
232 * this, and allow handles to be considered independently too.
233 * Also, for convenience, handles should get same status as keyframe (if it was within bounds).
235 static void borderselect_graphkeys(
236 bAnimContext *ac, const rctf *rectf_view, short mode, short selectmode, bool incl_handles,
239 ListBase anim_data = {NULL, NULL};
241 int filter, mapping_flag;
243 SpaceIpo *sipo = (SpaceIpo *)ac->sl;
244 KeyframeEditData ked;
245 KeyframeEditFunc ok_cb, select_cb;
246 View2D *v2d = &ac->ar->v2d;
247 rctf rectf, scaled_rectf;
249 /* convert mouse coordinates to frame ranges and channel coordinates corrected for view pan/zoom */
250 UI_view2d_region_to_view_rctf(v2d, rectf_view, &rectf);
253 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
254 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
256 /* get beztriple editing/validation funcs */
257 select_cb = ANIM_editkeyframes_select(selectmode);
258 ok_cb = ANIM_editkeyframes_ok(mode);
260 /* init editing data */
261 memset(&ked, 0, sizeof(KeyframeEditData));
262 if (mode == BEZT_OK_REGION_LASSO) {
263 struct KeyframeEdit_LassoData *data_lasso = data;
264 data_lasso->rectf_scaled = &scaled_rectf;
265 ked.data = data_lasso;
267 else if (mode == BEZT_OK_REGION_CIRCLE) {
268 struct KeyframeEdit_CircleData *data_circle = data;
269 data_circle->rectf_scaled = &scaled_rectf;
273 ked.data = &scaled_rectf;
276 /* treat handles separately? */
278 ked.iterflags |= KEYFRAME_ITER_INCL_HANDLES;
282 mapping_flag = ANIM_UNITCONV_ONLYKEYS;
284 mapping_flag |= ANIM_get_normalization_flags(ac);
286 /* loop over data, doing border select */
287 for (ale = anim_data.first; ale; ale = ale->next) {
288 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
289 FCurve *fcu = (FCurve *)ale->key_data;
290 float unit_scale = ANIM_unit_mapping_get_factor(ac->scene, ale->id, fcu, mapping_flag);
292 /* apply NLA mapping to all the keyframes, since it's easier than trying to
293 * guess when a callback might use something different
296 ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, incl_handles == 0);
298 scaled_rectf.xmin = rectf.xmin;
299 scaled_rectf.xmax = rectf.xmax;
300 scaled_rectf.ymin = rectf.ymin / unit_scale;
301 scaled_rectf.ymax = rectf.ymax / unit_scale;
303 /* set horizontal range (if applicable)
304 * NOTE: these values are only used for x-range and y-range but not region
305 * (which uses ked.data, i.e. rectf)
307 if (mode != BEZT_OK_VALUERANGE) {
316 /* firstly, check if any keyframes will be hit by this */
317 if (ANIM_fcurve_keyframes_loop(&ked, fcu, NULL, ok_cb, NULL)) {
318 /* select keyframes that are in the appropriate places */
319 ANIM_fcurve_keyframes_loop(&ked, fcu, ok_cb, select_cb, NULL);
321 /* only change selection of channel when the visibility of keyframes doesn't depend on this */
322 if ((sipo->flag & SIPO_SELCUVERTSONLY) == 0) {
323 /* select the curve too now that curve will be touched */
324 if (selectmode == SELECT_ADD)
325 fcu->flag |= FCURVE_SELECTED;
329 /* un-apply NLA mapping from all the keyframes */
331 ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, incl_handles == 0);
335 ANIM_animdata_freelist(&anim_data);
338 /* ------------------- */
340 static int graphkeys_borderselect_exec(bContext *C, wmOperator *op)
345 short mode = 0, selectmode = 0;
349 /* get editor data */
350 if (ANIM_animdata_get_context(C, &ac) == 0)
351 return OPERATOR_CANCELLED;
353 /* clear all selection if not extending selection */
354 extend = RNA_boolean_get(op->ptr, "extend");
356 deselect_graph_keys(&ac, 1, SELECT_SUBTRACT, true);
359 * - 'gesture_mode' from the operator specifies how to select
360 * - 'include_handles' from the operator specifies whether to include handles in the selection
362 if (RNA_int_get(op->ptr, "gesture_mode") == GESTURE_MODAL_SELECT)
363 selectmode = SELECT_ADD;
365 selectmode = SELECT_SUBTRACT;
367 incl_handles = RNA_boolean_get(op->ptr, "include_handles");
369 /* get settings from operator */
370 WM_operator_properties_border_to_rcti(op, &rect);
372 /* selection 'mode' depends on whether borderselect region only matters on one axis */
373 if (RNA_boolean_get(op->ptr, "axis_range")) {
374 /* mode depends on which axis of the range is larger to determine which axis to use
375 * - checking this in region-space is fine, as it's fundamentally still going to be a different rect size
376 * - the frame-range select option is favored over the channel one (x over y), as frame-range one is often
377 * used for tweaking timing when "blocking", while channels is not that useful...
379 if ((BLI_rcti_size_x(&rect)) >= (BLI_rcti_size_y(&rect)))
380 mode = BEZT_OK_FRAMERANGE;
382 mode = BEZT_OK_VALUERANGE;
385 mode = BEZT_OK_REGION;
387 BLI_rctf_rcti_copy(&rect_fl, &rect);
389 /* apply borderselect action */
390 borderselect_graphkeys(&ac, &rect_fl, mode, selectmode, incl_handles, NULL);
392 graphkeys_auto_view(C);
394 /* send notifier that keyframe selection has changed */
395 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
397 return OPERATOR_FINISHED;
400 void GRAPH_OT_select_border(wmOperatorType *ot)
403 ot->name = "Border Select";
404 ot->idname = "GRAPH_OT_select_border";
405 ot->description = "Select all keyframes within the specified region";
408 ot->invoke = WM_border_select_invoke;
409 ot->exec = graphkeys_borderselect_exec;
410 ot->modal = WM_border_select_modal;
411 ot->cancel = WM_border_select_cancel;
413 ot->poll = graphop_visible_keyframes_poll;
416 ot->flag = OPTYPE_REGISTER /*|OPTYPE_UNDO*/;
419 WM_operator_properties_gesture_border(ot, true);
421 ot->prop = RNA_def_boolean(ot->srna, "axis_range", 0, "Axis Range", "");
422 RNA_def_boolean(ot->srna, "include_handles", 0, "Include Handles", "Are handles tested individually against the selection criteria");
425 static int graphkeys_lassoselect_exec(bContext *C, wmOperator *op)
434 struct KeyframeEdit_LassoData data_lasso;
436 /* get editor data */
437 if (ANIM_animdata_get_context(C, &ac) == 0)
438 return OPERATOR_CANCELLED;
440 data_lasso.rectf_view = &rect_fl;
441 data_lasso.mcords = WM_gesture_lasso_path_to_array(C, op, &data_lasso.mcords_tot);
442 if (data_lasso.mcords == NULL)
443 return OPERATOR_CANCELLED;
445 /* clear all selection if not extending selection */
446 extend = RNA_boolean_get(op->ptr, "extend");
448 deselect_graph_keys(&ac, 1, SELECT_SUBTRACT, true);
450 if (!RNA_boolean_get(op->ptr, "deselect"))
451 selectmode = SELECT_ADD;
453 selectmode = SELECT_SUBTRACT;
455 if (ac.spacetype == SPACE_IPO) {
456 SpaceIpo *sipo = (SpaceIpo *)ac.sl;
457 if (selectmode == SELECT_ADD) {
458 incl_handles = ((sipo->flag & SIPO_SELVHANDLESONLY) ||
459 (sipo->flag & SIPO_NOHANDLES)) == 0;
462 incl_handles = (sipo->flag & SIPO_NOHANDLES) == 0;
466 incl_handles = false;
470 /* get settings from operator */
471 BLI_lasso_boundbox(&rect, data_lasso.mcords, data_lasso.mcords_tot);
473 BLI_rctf_rcti_copy(&rect_fl, &rect);
475 /* apply borderselect action */
476 borderselect_graphkeys(&ac, &rect_fl, BEZT_OK_REGION_LASSO, selectmode, incl_handles, &data_lasso);
478 MEM_freeN((void *)data_lasso.mcords);
481 graphkeys_auto_view(C);
483 /* send notifier that keyframe selection has changed */
484 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
486 return OPERATOR_FINISHED;
490 void GRAPH_OT_select_lasso(wmOperatorType *ot)
493 ot->name = "Lasso Select";
494 ot->description = "Select keyframe points using lasso selection";
495 ot->idname = "GRAPH_OT_select_lasso";
498 ot->invoke = WM_gesture_lasso_invoke;
499 ot->modal = WM_gesture_lasso_modal;
500 ot->exec = graphkeys_lassoselect_exec;
501 ot->poll = graphop_visible_keyframes_poll;
502 ot->cancel = WM_gesture_lasso_cancel;
505 ot->flag = OPTYPE_UNDO;
508 RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", "");
509 RNA_def_boolean(ot->srna, "deselect", false, "Deselect", "Deselect rather than select items");
510 RNA_def_boolean(ot->srna, "extend", true, "Extend", "Extend selection instead of deselecting everything first");
513 static int graph_circle_select_modal(bContext *C, wmOperator *op, const wmEvent *event)
515 if (event->type == EVT_MODAL_MAP && event->val == GESTURE_MODAL_NOP)
516 graphkeys_auto_view(C);
518 return WM_gesture_circle_modal(C, op, event);
521 static int graph_circle_select_exec(bContext *C, wmOperator *op)
524 const int gesture_mode = RNA_int_get(op->ptr, "gesture_mode");
528 struct KeyframeEdit_CircleData data;
529 float x = RNA_int_get(op->ptr, "x");
530 float y = RNA_int_get(op->ptr, "y");
531 float radius = RNA_int_get(op->ptr, "radius");
533 /* get editor data */
534 if (ANIM_animdata_get_context(C, &ac) == 0)
535 return OPERATOR_CANCELLED;
539 data.radius_squared = radius * radius;
540 data.rectf_view = &rect_fl;
542 if (gesture_mode == GESTURE_MODAL_SELECT)
543 selectmode = SELECT_ADD;
545 selectmode = SELECT_SUBTRACT;
547 rect_fl.xmin = x - radius;
548 rect_fl.xmax = x + radius;
549 rect_fl.ymin = y - radius;
550 rect_fl.ymax = y + radius;
552 if (ac.spacetype == SPACE_IPO) {
553 SpaceIpo *sipo = (SpaceIpo *)ac.sl;
554 if (selectmode == SELECT_ADD) {
555 incl_handles = ((sipo->flag & SIPO_SELVHANDLESONLY) ||
556 (sipo->flag & SIPO_NOHANDLES)) == 0;
559 incl_handles = (sipo->flag & SIPO_NOHANDLES) == 0;
563 incl_handles = false;
566 /* apply borderselect action */
567 borderselect_graphkeys(&ac, &rect_fl, BEZT_OK_REGION_CIRCLE, selectmode, incl_handles, &data);
569 /* send notifier that keyframe selection has changed */
570 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
572 return OPERATOR_FINISHED;
575 void GRAPH_OT_select_circle(wmOperatorType *ot)
577 ot->name = "Circle Select";
578 ot->description = "Select keyframe points using circle selection";
579 ot->idname = "GRAPH_OT_select_circle";
581 ot->invoke = WM_gesture_circle_invoke;
582 ot->modal = graph_circle_select_modal;
583 ot->exec = graph_circle_select_exec;
584 ot->poll = graphop_visible_keyframes_poll;
585 ot->cancel = WM_gesture_circle_cancel;
588 ot->flag = OPTYPE_UNDO;
590 RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
591 RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
592 RNA_def_int(ot->srna, "radius", 1, 1, INT_MAX, "Radius", "", 1, INT_MAX);
593 RNA_def_int(ot->srna, "gesture_mode", 0, INT_MIN, INT_MAX, "Event Type", "", INT_MIN, INT_MAX);
596 /* ******************** Column Select Operator **************************** */
597 /* This operator works in one of four ways:
598 * - 1) select all keyframes in the same frame as a selected one (KKEY)
599 * - 2) select all keyframes in the same frame as the current frame marker (CTRL-KKEY)
600 * - 3) select all keyframes in the same frame as a selected markers (SHIFT-KKEY)
601 * - 4) select all keyframes that occur between selected markers (ALT-KKEY)
604 /* defines for column-select mode */
605 static EnumPropertyItem prop_column_select_types[] = {
606 {GRAPHKEYS_COLUMNSEL_KEYS, "KEYS", 0, "On Selected Keyframes", ""},
607 {GRAPHKEYS_COLUMNSEL_CFRA, "CFRA", 0, "On Current Frame", ""},
608 {GRAPHKEYS_COLUMNSEL_MARKERS_COLUMN, "MARKERS_COLUMN", 0, "On Selected Markers", ""},
609 {GRAPHKEYS_COLUMNSEL_MARKERS_BETWEEN, "MARKERS_BETWEEN", 0, "Between Min/Max Selected Markers", ""},
610 {0, NULL, 0, NULL, NULL}
613 /* ------------------- */
615 /* Selects all visible keyframes between the specified markers */
616 /* TODO, this is almost an _exact_ duplicate of a function of the same name in action_select.c
617 * should de-duplicate - campbell */
618 static void markers_selectkeys_between(bAnimContext *ac)
620 ListBase anim_data = {NULL, NULL};
624 KeyframeEditFunc ok_cb, select_cb;
625 KeyframeEditData ked = {{NULL}};
628 /* get extreme markers */
629 ED_markers_get_minmax(ac->markers, 1, &min, &max);
633 /* get editing funcs + data */
634 ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAMERANGE);
635 select_cb = ANIM_editkeyframes_select(SELECT_ADD);
641 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
642 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
644 /* select keys in-between */
645 for (ale = anim_data.first; ale; ale = ale->next) {
646 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
649 ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1);
650 ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
651 ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
654 ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
659 ANIM_animdata_freelist(&anim_data);
663 /* Selects all visible keyframes in the same frames as the specified elements */
664 static void columnselect_graph_keys(bAnimContext *ac, short mode)
666 ListBase anim_data = {NULL, NULL};
670 Scene *scene = ac->scene;
672 KeyframeEditFunc select_cb, ok_cb;
673 KeyframeEditData ked;
675 /* initialize keyframe editing data */
676 memset(&ked, 0, sizeof(KeyframeEditData));
678 /* build list of columns */
680 case GRAPHKEYS_COLUMNSEL_KEYS: /* list of selected keys */
681 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
682 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
684 for (ale = anim_data.first; ale; ale = ale->next)
685 ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, bezt_to_cfraelem, NULL);
687 ANIM_animdata_freelist(&anim_data);
690 case GRAPHKEYS_COLUMNSEL_CFRA: /* current frame */
691 /* make a single CfraElem for storing this */
692 ce = MEM_callocN(sizeof(CfraElem), "cfraElem");
693 BLI_addtail(&ked.list, ce);
695 ce->cfra = (float)CFRA;
698 case GRAPHKEYS_COLUMNSEL_MARKERS_COLUMN: /* list of selected markers */
699 ED_markers_make_cfra_list(ac->markers, &ked.list, SELECT);
702 default: /* invalid option */
706 /* set up BezTriple edit callbacks */
707 select_cb = ANIM_editkeyframes_select(SELECT_ADD);
708 ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAME);
710 /* loop through all of the keys and select additional keyframes
711 * based on the keys found to be selected above
713 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
714 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
716 for (ale = anim_data.first; ale; ale = ale->next) {
717 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
719 /* loop over cfraelems (stored in the KeyframeEditData->list)
720 * - we need to do this here, as we can apply fewer NLA-mapping conversions
722 for (ce = ked.list.first; ce; ce = ce->next) {
723 /* set frame for validation callback to refer to */
724 ked.f1 = BKE_nla_tweakedit_remap(adt, ce->cfra, NLATIME_CONVERT_UNMAP);
726 /* select elements with frame number matching cfraelem */
727 ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
732 BLI_freelistN(&ked.list);
733 ANIM_animdata_freelist(&anim_data);
736 /* ------------------- */
738 static int graphkeys_columnselect_exec(bContext *C, wmOperator *op)
743 /* get editor data */
744 if (ANIM_animdata_get_context(C, &ac) == 0)
745 return OPERATOR_CANCELLED;
747 /* action to take depends on the mode */
748 mode = RNA_enum_get(op->ptr, "mode");
750 if (mode == GRAPHKEYS_COLUMNSEL_MARKERS_BETWEEN)
751 markers_selectkeys_between(&ac);
753 columnselect_graph_keys(&ac, mode);
755 graphkeys_auto_view(C);
757 /* set notifier that keyframe selection has changed */
758 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
760 return OPERATOR_FINISHED;
763 void GRAPH_OT_select_column(wmOperatorType *ot)
766 ot->name = "Select All";
767 ot->idname = "GRAPH_OT_select_column";
768 ot->description = "Select all keyframes on the specified frame(s)";
771 ot->exec = graphkeys_columnselect_exec;
772 ot->poll = graphop_visible_keyframes_poll;
775 ot->flag = OPTYPE_REGISTER /*|OPTYPE_UNDO*/;
778 ot->prop = RNA_def_enum(ot->srna, "mode", prop_column_select_types, 0, "Mode", "");
781 /* ******************** Select Linked Operator *********************** */
783 static int graphkeys_select_linked_exec(bContext *C, wmOperator *UNUSED(op))
787 ListBase anim_data = {NULL, NULL};
791 KeyframeEditFunc ok_cb = ANIM_editkeyframes_ok(BEZT_OK_SELECTED);
792 KeyframeEditFunc sel_cb = ANIM_editkeyframes_select(SELECT_ADD);
794 /* get editor data */
795 if (ANIM_animdata_get_context(C, &ac) == 0)
796 return OPERATOR_CANCELLED;
798 /* loop through all of the keys and select additional keyframes based on these */
799 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
800 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
802 for (ale = anim_data.first; ale; ale = ale->next) {
803 FCurve *fcu = (FCurve *)ale->key_data;
805 /* check if anything selected? */
806 if (ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, ok_cb, NULL)) {
807 /* select every keyframe in this curve then */
808 ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, sel_cb, NULL);
813 ANIM_animdata_freelist(&anim_data);
815 graphkeys_auto_view(C);
817 /* set notifier that keyframe selection has changed */
818 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
820 return OPERATOR_FINISHED;
823 void GRAPH_OT_select_linked(wmOperatorType *ot)
826 ot->name = "Select Linked";
827 ot->idname = "GRAPH_OT_select_linked";
828 ot->description = "Select keyframes occurring in the same F-Curves as selected ones";
831 ot->exec = graphkeys_select_linked_exec;
832 ot->poll = graphop_visible_keyframes_poll;
835 ot->flag = OPTYPE_REGISTER /*|OPTYPE_UNDO*/;
838 /* ******************** Select More/Less Operators *********************** */
840 /* Common code to perform selection */
841 static void select_moreless_graph_keys(bContext *C, bAnimContext *ac, short mode)
843 ListBase anim_data = {NULL, NULL};
847 const SpaceIpo *sipo = (SpaceIpo *)ac->sl;
848 KeyframeEditData ked;
849 KeyframeEditFunc build_cb;
852 /* init selmap building data */
853 build_cb = ANIM_editkeyframes_buildselmap(mode);
854 memset(&ked, 0, sizeof(KeyframeEditData));
856 /* loop through all of the keys and select additional keyframes based on these */
857 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
858 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
860 for (ale = anim_data.first; ale; ale = ale->next) {
861 FCurve *fcu = (FCurve *)ale->key_data;
863 /* only continue if F-Curve has keyframes */
864 if (fcu->bezt == NULL)
867 /* build up map of whether F-Curve's keyframes should be selected or not */
868 ked.data = MEM_callocN(fcu->totvert, "selmap graphEdit");
869 ANIM_fcurve_keyframes_loop(&ked, fcu, NULL, build_cb, NULL);
871 /* based on this map, adjust the selection status of the keyframes */
872 ANIM_fcurve_keyframes_loop(&ked, fcu, NULL, bezt_selmap_flush, NULL);
874 /* free the selmap used here */
878 /* only do auto view if a bezier point is selected */
879 if (sipo->flag & SIPO_AUTO_VIEW_SELECTED) {
880 const FCurve *fcu = (FCurve *)ale->key_data;
881 const BezTriple *bezt;
884 /* only continue if F-Curve has keyframes */
885 if (fcu->bezt == NULL)
888 for (bezt = fcu->bezt, i = 0; i < fcu->totvert; bezt++, i++) {
889 if (BEZSELECTED(bezt)) {
890 graphkeys_auto_view(C);
898 ANIM_animdata_freelist(&anim_data);
901 /* ----------------- */
903 static int graphkeys_select_more_exec(bContext *C, wmOperator *UNUSED(op))
907 /* get editor data */
908 if (ANIM_animdata_get_context(C, &ac) == 0)
909 return OPERATOR_CANCELLED;
911 /* perform select changes */
912 select_moreless_graph_keys(C, &ac, SELMAP_MORE);
914 /* set notifier that keyframe selection has changed */
915 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
917 return OPERATOR_FINISHED;
920 void GRAPH_OT_select_more(wmOperatorType *ot)
923 ot->name = "Select More";
924 ot->idname = "GRAPH_OT_select_more";
925 ot->description = "Select keyframes beside already selected ones";
928 ot->exec = graphkeys_select_more_exec;
929 ot->poll = graphop_visible_keyframes_poll;
932 ot->flag = OPTYPE_REGISTER /*|OPTYPE_UNDO*/;
935 /* ----------------- */
937 static int graphkeys_select_less_exec(bContext *C, wmOperator *UNUSED(op))
941 /* get editor data */
942 if (ANIM_animdata_get_context(C, &ac) == 0)
943 return OPERATOR_CANCELLED;
945 /* perform select changes */
946 select_moreless_graph_keys(C, &ac, SELMAP_LESS);
948 /* set notifier that keyframe selection has changed */
949 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
951 return OPERATOR_FINISHED;
954 void GRAPH_OT_select_less(wmOperatorType *ot)
957 ot->name = "Select Less";
958 ot->idname = "GRAPH_OT_select_less";
959 ot->description = "Deselect keyframes on ends of selection islands";
962 ot->exec = graphkeys_select_less_exec;
963 ot->poll = graphop_visible_keyframes_poll;
966 ot->flag = OPTYPE_REGISTER /*|OPTYPE_UNDO*/;
969 /* ******************** Select Left/Right Operator ************************* */
970 /* Select keyframes left/right of the current frame indicator */
972 /* defines for left-right select tool */
973 static EnumPropertyItem prop_graphkeys_leftright_select_types[] = {
974 {GRAPHKEYS_LRSEL_TEST, "CHECK", 0, "Check if Select Left or Right", ""},
975 {GRAPHKEYS_LRSEL_LEFT, "LEFT", 0, "Before current frame", ""},
976 {GRAPHKEYS_LRSEL_RIGHT, "RIGHT", 0, "After current frame", ""},
977 {0, NULL, 0, NULL, NULL}
980 /* --------------------------------- */
982 static void graphkeys_select_leftright(bAnimContext *ac, short leftright, short select_mode)
984 ListBase anim_data = {NULL, NULL};
988 KeyframeEditFunc ok_cb, select_cb;
989 KeyframeEditData ked = {{NULL}};
990 Scene *scene = ac->scene;
992 /* if select mode is replace, deselect all keyframes (and channels) first */
993 if (select_mode == SELECT_REPLACE) {
994 select_mode = SELECT_ADD;
996 /* - deselect all other keyframes, so that just the newly selected remain
997 * - channels aren't deselected, since we don't re-select any as a consequence
999 deselect_graph_keys(ac, 0, SELECT_SUBTRACT, false);
1002 /* set callbacks and editing data */
1003 ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAMERANGE);
1004 select_cb = ANIM_editkeyframes_select(select_mode);
1006 if (leftright == GRAPHKEYS_LRSEL_LEFT) {
1007 ked.f1 = MINAFRAMEF;
1008 ked.f2 = (float)(CFRA + 0.1f);
1011 ked.f1 = (float)(CFRA - 0.1f);
1016 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_NODUPLIS);
1017 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1020 for (ale = anim_data.first; ale; ale = ale->next) {
1021 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
1024 ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1);
1025 ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
1026 ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
1029 ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
1033 ANIM_animdata_freelist(&anim_data);
1036 /* ----------------- */
1038 static int graphkeys_select_leftright_exec(bContext *C, wmOperator *op)
1041 short leftright = RNA_enum_get(op->ptr, "mode");
1044 /* get editor data */
1045 if (ANIM_animdata_get_context(C, &ac) == 0)
1046 return OPERATOR_CANCELLED;
1048 /* select mode is either replace (deselect all, then add) or add/extend */
1049 if (RNA_boolean_get(op->ptr, "extend"))
1050 selectmode = SELECT_INVERT;
1052 selectmode = SELECT_REPLACE;
1054 /* if "test" mode is set, we don't have any info to set this with */
1055 if (leftright == GRAPHKEYS_LRSEL_TEST)
1056 return OPERATOR_CANCELLED;
1058 /* do the selecting now */
1059 graphkeys_select_leftright(&ac, leftright, selectmode);
1061 graphkeys_auto_view(C);
1063 /* set notifier that keyframe selection (and channels too) have changed */
1064 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | ND_ANIMCHAN | NA_SELECTED, NULL);
1066 return OPERATOR_FINISHED;
1069 static int graphkeys_select_leftright_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1072 short leftright = RNA_enum_get(op->ptr, "mode");
1074 /* get editor data */
1075 if (ANIM_animdata_get_context(C, &ac) == 0)
1076 return OPERATOR_CANCELLED;
1078 /* handle mode-based testing */
1079 if (leftright == GRAPHKEYS_LRSEL_TEST) {
1080 Scene *scene = ac.scene;
1081 ARegion *ar = ac.ar;
1082 View2D *v2d = &ar->v2d;
1085 /* determine which side of the current frame mouse is on */
1086 x = UI_view2d_region_to_view_x(v2d, event->mval[0]);
1088 RNA_enum_set(op->ptr, "mode", GRAPHKEYS_LRSEL_LEFT);
1090 RNA_enum_set(op->ptr, "mode", GRAPHKEYS_LRSEL_RIGHT);
1093 /* perform selection */
1094 return graphkeys_select_leftright_exec(C, op);
1097 void GRAPH_OT_select_leftright(wmOperatorType *ot)
1102 ot->name = "Select Left/Right";
1103 ot->idname = "GRAPH_OT_select_leftright";
1104 ot->description = "Select keyframes to the left or the right of the current frame";
1107 ot->invoke = graphkeys_select_leftright_invoke;
1108 ot->exec = graphkeys_select_leftright_exec;
1109 ot->poll = graphop_visible_keyframes_poll;
1112 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1115 ot->prop = RNA_def_enum(ot->srna, "mode", prop_graphkeys_leftright_select_types, GRAPHKEYS_LRSEL_TEST, "Mode", "");
1116 RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE);
1118 prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", "");
1119 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1122 /* ******************** Mouse-Click Select Operator *********************** */
1123 /* This operator works in one of three ways:
1124 * - 1) keyframe under mouse - no special modifiers
1125 * - 2) all keyframes on the same side of current frame indicator as mouse - ALT modifier
1126 * - 3) column select all keyframes in frame under mouse - CTRL modifier
1128 * In addition to these basic options, the SHIFT modifier can be used to toggle the
1129 * selection mode between replacing the selection (without) and inverting the selection (with).
1132 /* temp info for caching handle vertices close */
1133 typedef struct tNearestVertInfo {
1134 struct tNearestVertInfo *next, *prev;
1136 FCurve *fcu; /* F-Curve that keyframe comes from */
1138 BezTriple *bezt; /* keyframe to consider */
1139 FPoint *fpt; /* sample point to consider */
1141 short hpoint; /* the handle index that we hit (eHandleIndex) */
1142 short sel; /* whether the handle is selected or not */
1143 int dist; /* distance from mouse to vert */
1146 /* Tags for the type of graph vert that we have */
1147 typedef enum eGraphVertIndex {
1148 NEAREST_HANDLE_LEFT = -1,
1150 NEAREST_HANDLE_RIGHT
1153 /* Tolerance for absolute radius (in pixels) of the vert from the cursor to use */
1154 // TODO: perhaps this should depend a bit on the size that the user set the vertices to be?
1155 #define GVERTSEL_TOL 10
1159 /* check if its ok to select a handle */
1160 // XXX also need to check for int-values only?
1161 static bool fcurve_handle_sel_check(SpaceIpo *sipo, BezTriple *bezt)
1163 if (sipo->flag & SIPO_NOHANDLES) return 0;
1164 if ((sipo->flag & SIPO_SELVHANDLESONLY) && BEZSELECTED(bezt) == 0) return 0;
1168 /* check if the given vertex is within bounds or not */
1169 // TODO: should we return if we hit something?
1170 static void nearest_fcurve_vert_store(
1171 ListBase *matches, View2D *v2d, FCurve *fcu,
1172 BezTriple *bezt, FPoint *fpt, short hpoint, const int mval[2], float unit_scale)
1174 /* Keyframes or Samples? */
1176 int screen_co[2], dist;
1178 /* convert from data-space to screen coordinates
1179 * NOTE: hpoint+1 gives us 0,1,2 respectively for each handle,
1180 * needed to access the relevant vertex coordinates in the 3x3
1183 if (UI_view2d_view_to_region_clip(v2d,
1184 bezt->vec[hpoint + 1][0], bezt->vec[hpoint + 1][1] * unit_scale,
1185 &screen_co[0], &screen_co[1]) &&
1186 /* check if distance from mouse cursor to vert in screen space is within tolerance */
1187 ((dist = len_v2v2_int(mval, screen_co)) <= GVERTSEL_TOL))
1189 tNearestVertInfo *nvi = (tNearestVertInfo *)matches->last;
1190 bool replace = false;
1192 /* if there is already a point for the F-Curve, check if this point is closer than that was */
1193 if ((nvi) && (nvi->fcu == fcu)) {
1194 /* replace if we are closer, or if equal and that one wasn't selected but we are... */
1195 if ((nvi->dist > dist) || ((nvi->sel == 0) && BEZSELECTED(bezt)))
1198 /* add new if not replacing... */
1200 nvi = MEM_callocN(sizeof(tNearestVertInfo), "Nearest Graph Vert Info - Bezt");
1205 nvi->hpoint = hpoint;
1208 nvi->sel = BEZSELECTED(bezt); // XXX... should this use the individual verts instead?
1210 /* add to list of matches if appropriate... */
1212 BLI_addtail(matches, nvi);
1220 /* helper for find_nearest_fcurve_vert() - build the list of nearest matches */
1221 static void get_nearest_fcurve_verts_list(bAnimContext *ac, const int mval[2], ListBase *matches)
1223 ListBase anim_data = {NULL, NULL};
1227 SpaceIpo *sipo = (SpaceIpo *)ac->sl;
1228 View2D *v2d = &ac->ar->v2d;
1229 short mapping_flag = 0;
1231 /* get curves to search through
1232 * - if the option to only show keyframes that belong to selected F-Curves is enabled,
1233 * include the 'only selected' flag...
1235 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
1236 if (sipo->flag & SIPO_SELCUVERTSONLY) // FIXME: this should really be check for by the filtering code...
1237 filter |= ANIMFILTER_SEL;
1238 mapping_flag |= ANIM_get_normalization_flags(ac);
1239 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1241 for (ale = anim_data.first; ale; ale = ale->next) {
1242 FCurve *fcu = (FCurve *)ale->key_data;
1243 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
1244 float unit_scale = ANIM_unit_mapping_get_factor(ac->scene, ale->id, fcu, mapping_flag);
1246 /* apply NLA mapping to all the keyframes */
1248 ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0);
1251 BezTriple *bezt1 = fcu->bezt, *prevbezt = NULL;
1254 for (i = 0; i < fcu->totvert; i++, prevbezt = bezt1, bezt1++) {
1256 nearest_fcurve_vert_store(matches, v2d, fcu, bezt1, NULL, NEAREST_HANDLE_KEY, mval, unit_scale);
1258 /* handles - only do them if they're visible */
1259 if (fcurve_handle_sel_check(sipo, bezt1) && (fcu->totvert > 1)) {
1260 /* first handle only visible if previous segment had handles */
1261 if ((!prevbezt && (bezt1->ipo == BEZT_IPO_BEZ)) || (prevbezt && (prevbezt->ipo == BEZT_IPO_BEZ))) {
1262 nearest_fcurve_vert_store(matches, v2d, fcu, bezt1, NULL, NEAREST_HANDLE_LEFT, mval, unit_scale);
1265 /* second handle only visible if this segment is bezier */
1266 if (bezt1->ipo == BEZT_IPO_BEZ) {
1267 nearest_fcurve_vert_store(matches, v2d, fcu, bezt1, NULL, NEAREST_HANDLE_RIGHT, mval, unit_scale);
1272 else if (fcu->fpt) {
1273 // TODO; do this for samples too
1277 /* un-apply NLA mapping from all the keyframes */
1279 ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 0);
1283 ANIM_animdata_freelist(&anim_data);
1286 /* helper for find_nearest_fcurve_vert() - get the best match to use */
1287 static tNearestVertInfo *get_best_nearest_fcurve_vert(ListBase *matches)
1289 tNearestVertInfo *nvi = NULL;
1292 /* abort if list is empty */
1293 if (BLI_listbase_is_empty(matches))
1296 /* if list only has 1 item, remove it from the list and return */
1297 if (BLI_listbase_is_single(matches)) {
1298 /* need to remove from the list, otherwise it gets freed and then we can't return it */
1299 return BLI_pophead(matches);
1302 /* try to find the first selected F-Curve vert, then take the one after it */
1303 for (nvi = matches->first; nvi; nvi = nvi->next) {
1304 /* which mode of search are we in: find first selected, or find vert? */
1306 /* just take this vert now that we've found the selected one
1307 * - we'll need to remove this from the list so that it can be returned to the original caller
1309 BLI_remlink(matches, nvi);
1313 /* if vert is selected, we've got what we want... */
1319 /* if we're still here, this means that we failed to find anything appropriate in the first pass,
1320 * so just take the first item now...
1322 return BLI_pophead(matches);
1325 /* Find the nearest vertices (either a handle or the keyframe) that are nearest to the mouse cursor (in area coordinates)
1326 * NOTE: the match info found must still be freed
1328 static tNearestVertInfo *find_nearest_fcurve_vert(bAnimContext *ac, const int mval[2])
1330 ListBase matches = {NULL, NULL};
1331 tNearestVertInfo *nvi;
1333 /* step 1: get the nearest verts */
1334 get_nearest_fcurve_verts_list(ac, mval, &matches);
1336 /* step 2: find the best vert */
1337 nvi = get_best_nearest_fcurve_vert(&matches);
1339 BLI_freelistN(&matches);
1341 /* return the best vert found */
1345 /* ------------------- */
1347 /* option 1) select keyframe directly under mouse */
1348 static void mouse_graph_keys(bAnimContext *ac, const int mval[2], short select_mode, short curves_only)
1350 SpaceIpo *sipo = (SpaceIpo *)ac->sl;
1351 tNearestVertInfo *nvi;
1352 BezTriple *bezt = NULL;
1354 /* find the beztriple that we're selecting, and the handle that was clicked on */
1355 nvi = find_nearest_fcurve_vert(ac, mval);
1357 /* check if anything to select */
1361 /* deselect all other curves? */
1362 if (select_mode == SELECT_REPLACE) {
1363 /* reset selection mode */
1364 select_mode = SELECT_ADD;
1366 /* deselect all other keyframes (+ F-Curves too) */
1367 deselect_graph_keys(ac, 0, SELECT_SUBTRACT, true);
1369 /* deselect other channels too, but only only do this if
1370 * selection of channel when the visibility of keyframes
1371 * doesn't depend on this
1373 if ((sipo->flag & SIPO_SELCUVERTSONLY) == 0)
1374 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
1377 /* if points can be selected on this F-Curve */
1378 // TODO: what about those with no keyframes?
1379 if ((curves_only == 0) && ((nvi->fcu->flag & FCURVE_PROTECTED) == 0)) {
1380 /* only if there's keyframe */
1382 bezt = nvi->bezt; /* used to check bezt seletion is set */
1383 /* depends on selection mode */
1384 if (select_mode == SELECT_INVERT) {
1385 /* keyframe - invert select of all */
1386 if (nvi->hpoint == NEAREST_HANDLE_KEY) {
1387 if (BEZSELECTED(bezt)) {
1395 /* handles - toggle selection of relevant handle */
1396 else if (nvi->hpoint == NEAREST_HANDLE_LEFT) {
1397 /* toggle selection */
1401 /* toggle selection */
1406 /* if the keyframe was clicked on, select all verts of given beztriple */
1407 if (nvi->hpoint == NEAREST_HANDLE_KEY) {
1410 /* otherwise, select the handle that applied */
1411 else if (nvi->hpoint == NEAREST_HANDLE_LEFT)
1417 else if (nvi->fpt) {
1418 // TODO: need to handle sample points
1422 KeyframeEditFunc select_cb;
1423 KeyframeEditData ked;
1425 /* initialize keyframe editing data */
1426 memset(&ked, 0, sizeof(KeyframeEditData));
1428 /* set up BezTriple edit callbacks */
1429 select_cb = ANIM_editkeyframes_select(select_mode);
1431 /* select all keyframes */
1432 ANIM_fcurve_keyframes_loop(&ked, nvi->fcu, NULL, select_cb, NULL);
1435 /* only change selection of channel when the visibility of keyframes doesn't depend on this */
1436 if ((sipo->flag & SIPO_SELCUVERTSONLY) == 0) {
1437 /* select or deselect curve? */
1439 /* take selection status from item that got hit, to prevent flip/flop on channel
1440 * selection status when shift-selecting (i.e. "SELECT_INVERT") points
1442 if (BEZSELECTED(bezt))
1443 nvi->fcu->flag |= FCURVE_SELECTED;
1445 nvi->fcu->flag &= ~FCURVE_SELECTED;
1448 /* didn't hit any channel, so just apply that selection mode to the curve's selection status */
1449 if (select_mode == SELECT_INVERT)
1450 nvi->fcu->flag ^= FCURVE_SELECTED;
1451 else if (select_mode == SELECT_ADD)
1452 nvi->fcu->flag |= FCURVE_SELECTED;
1456 /* set active F-Curve (NOTE: sync the filter flags with findnearest_fcurve_vert) */
1457 /* needs to be called with (sipo->flag & SIPO_SELCUVERTSONLY) otherwise the active flag won't be set [#26452] */
1458 if (nvi->fcu->flag & FCURVE_SELECTED) {
1459 int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
1460 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, nvi->fcu, ANIMTYPE_FCURVE);
1463 /* free temp sample data for filtering */
1467 /* Option 2) Selects all the keyframes on either side of the current frame (depends on which side the mouse is on) */
1468 /* (see graphkeys_select_leftright) */
1470 /* Option 3) Selects all visible keyframes in the same frame as the mouse click */
1471 static void graphkeys_mselect_column(bAnimContext *ac, const int mval[2], short select_mode)
1473 ListBase anim_data = {NULL, NULL};
1477 KeyframeEditFunc select_cb, ok_cb;
1478 KeyframeEditData ked;
1479 tNearestVertInfo *nvi;
1480 float selx = (float)ac->scene->r.cfra;
1482 /* find the beztriple that we're selecting, and the handle that was clicked on */
1483 nvi = find_nearest_fcurve_vert(ac, mval);
1485 /* check if anything to select */
1489 /* get frame number on which elements should be selected */
1490 // TODO: should we restrict to integer frames only?
1492 selx = nvi->bezt->vec[1][0];
1494 selx = nvi->fpt->vec[0];
1496 /* if select mode is replace, deselect all keyframes first */
1497 if (select_mode == SELECT_REPLACE) {
1498 /* reset selection mode to add to selection */
1499 select_mode = SELECT_ADD;
1501 /* - deselect all other keyframes, so that just the newly selected remain
1502 * - channels aren't deselected, since we don't re-select any as a consequence
1504 deselect_graph_keys(ac, 0, SELECT_SUBTRACT, false);
1507 /* initialize keyframe editing data */
1508 memset(&ked, 0, sizeof(KeyframeEditData));
1510 /* set up BezTriple edit callbacks */
1511 select_cb = ANIM_editkeyframes_select(select_mode);
1512 ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAME);
1514 /* loop through all of the keys and select additional keyframes
1515 * based on the keys found to be selected above
1517 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
1518 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1520 for (ale = anim_data.first; ale; ale = ale->next) {
1521 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
1523 /* set frame for validation callback to refer to */
1525 ked.f1 = BKE_nla_tweakedit_remap(adt, selx, NLATIME_CONVERT_UNMAP);
1529 /* select elements with frame number matching cfra */
1530 ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
1535 BLI_freelistN(&ked.list);
1536 ANIM_animdata_freelist(&anim_data);
1539 /* ------------------- */
1541 /* handle clicking */
1542 static int graphkeys_clickselect_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1547 /* get editor data */
1548 if (ANIM_animdata_get_context(C, &ac) == 0)
1549 return OPERATOR_CANCELLED;
1551 /* select mode is either replace (deselect all, then add) or add/extend */
1552 if (RNA_boolean_get(op->ptr, "extend"))
1553 selectmode = SELECT_INVERT;
1555 selectmode = SELECT_REPLACE;
1557 /* figure out action to take */
1558 if (RNA_boolean_get(op->ptr, "column")) {
1559 /* select all keyframes in the same frame as the one that was under the mouse */
1560 graphkeys_mselect_column(&ac, event->mval, selectmode);
1562 else if (RNA_boolean_get(op->ptr, "curves")) {
1563 /* select all keyframes in the same F-Curve as the one under the mouse */
1564 mouse_graph_keys(&ac, event->mval, selectmode, 1);
1567 /* select keyframe under mouse */
1568 mouse_graph_keys(&ac, event->mval, selectmode, 0);
1571 /* set notifier that keyframe selection (and also channel selection in some cases) has changed */
1572 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | ND_ANIMCHAN | NA_SELECTED, NULL);
1574 /* for tweak grab to work */
1575 return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
1578 void GRAPH_OT_clickselect(wmOperatorType *ot)
1583 ot->name = "Mouse Select Keys";
1584 ot->idname = "GRAPH_OT_clickselect";
1585 ot->description = "Select keyframes by clicking on them";
1588 ot->invoke = graphkeys_clickselect_invoke;
1589 ot->poll = graphop_visible_keyframes_poll;
1592 ot->flag = OPTYPE_UNDO;
1595 prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend Select",
1596 "Toggle keyframe selection instead of leaving newly selected keyframes only"); // SHIFTKEY
1597 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1599 prop = RNA_def_boolean(ot->srna, "column", 0, "Column Select",
1600 "Select all keyframes that occur on the same frame as the one under the mouse"); // ALTKEY
1601 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1603 prop = RNA_def_boolean(ot->srna, "curves", 0, "Only Curves",
1604 "Select all the keyframes in the curve"); // CTRLKEY + ALTKEY
1605 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1608 /* ************************************************************************** */