diff options
Diffstat (limited to 'source/blender/editors/space_ipo/ipo_select.c')
-rw-r--r-- | source/blender/editors/space_ipo/ipo_select.c | 894 |
1 files changed, 894 insertions, 0 deletions
diff --git a/source/blender/editors/space_ipo/ipo_select.c b/source/blender/editors/space_ipo/ipo_select.c new file mode 100644 index 00000000000..fff40f1c739 --- /dev/null +++ b/source/blender/editors/space_ipo/ipo_select.c @@ -0,0 +1,894 @@ +/** + * $Id: editaction.c 17746 2008-12-08 11:19:44Z aligorith $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2008 Blender Foundation + * + * Contributor(s): Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <math.h> +#include <stdlib.h> +#include <string.h> +#include <float.h> + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_arithb.h" + +#include "DNA_anim_types.h" +#include "DNA_action_types.h" +#include "DNA_armature_types.h" +#include "DNA_camera_types.h" +#include "DNA_curve_types.h" +#include "DNA_object_types.h" +#include "DNA_screen_types.h" +#include "DNA_scene_types.h" +#include "DNA_space_types.h" +#include "DNA_constraint_types.h" +#include "DNA_key_types.h" +#include "DNA_lamp_types.h" +#include "DNA_material_types.h" +#include "DNA_userdef_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_windowmanager_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "BKE_action.h" +#include "BKE_depsgraph.h" +#include "BKE_fcurve.h" +#include "BKE_key.h" +#include "BKE_material.h" +#include "BKE_object.h" +#include "BKE_context.h" +#include "BKE_utildefines.h" + +#include "UI_view2d.h" + +#include "ED_anim_api.h" +#include "ED_keyframing.h" +#include "ED_keyframes_draw.h" +#include "ED_keyframes_edit.h" +#include "ED_screen.h" +#include "ED_space_api.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ipo_intern.h" + + +/* ************************************************************************** */ +/* KEYFRAMES STUFF */ + +/* ******************** Deselect All Operator ***************************** */ +/* This operator works in one of three ways: + * 1) (de)select all (AKEY) - test if select all or deselect all + * 2) invert all (CTRL-IKEY) - invert selection of all keyframes + * 3) (de)select all - no testing is done; only for use internal tools as normal function... + */ + +/* Deselects keyframes in the action editor + * - This is called by the deselect all operator, as well as other ones! + * + * - test: check if select or deselect all + * - sel: how to select keyframes + * 0 = deselect + * 1 = select + * 2 = invert + */ +static void deselect_graph_keys (bAnimContext *ac, short test, short sel) +{ + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + BeztEditData bed; + BeztEditFunc test_cb, sel_cb; + + /* determine type-based settings */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_CURVESONLY); + + /* filter data */ + ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + + /* init BezTriple looping data */ + memset(&bed, 0, sizeof(BeztEditData)); + test_cb= ANIM_editkeyframes_ok(BEZT_OK_SELECTED); + + /* See if we should be selecting or deselecting */ + // xxx check for curves too + if (test) { + for (ale= anim_data.first; ale; ale= ale->next) { + if (ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, NULL, test_cb, NULL)) { + sel= SELECT_SUBTRACT; + break; + } + } + } + + /* convert sel to selectmode, and use that to get editor */ + sel_cb= ANIM_editkeyframes_select(sel); + + /* Now set the flags */ + // xxx check for curves too + for (ale= anim_data.first; ale; ale= ale->next) + ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, NULL, sel_cb, NULL); + + /* Cleanup */ + BLI_freelistN(&anim_data); +} + +/* ------------------- */ + +static int graphkeys_deselectall_exec(bContext *C, wmOperator *op) +{ + bAnimContext ac; + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + /* 'standard' behaviour - check if selected, then apply relevant selection */ + if (RNA_boolean_get(op->ptr, "invert")) + deselect_graph_keys(&ac, 0, SELECT_INVERT); + else + deselect_graph_keys(&ac, 1, SELECT_ADD); + + /* set notifier tha things have changed */ + ED_area_tag_redraw(CTX_wm_area(C)); // FIXME... should be updating 'keyframes' data context or so instead! + + return OPERATOR_FINISHED; +} + +void GRAPHEDIT_OT_keyframes_deselectall (wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Select All"; + ot->idname= "GRAPHEDIT_OT_keyframes_deselectall"; + + /* api callbacks */ + ot->exec= graphkeys_deselectall_exec; + ot->poll= ED_operator_areaactive; + + /* flags */ + ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/; + + /* props */ + RNA_def_boolean(ot->srna, "invert", 0, "Invert", ""); +} + +/* ******************** Border Select Operator **************************** */ +/* This operator currently works in one of three ways: + * -> BKEY - 1) all keyframes within region are selected (validation with BEZT_OK_REGION) + * -> ALT-BKEY - depending on which axis of the region was larger... + * -> 2) x-axis, so select all frames within frame range (validation with BEZT_OK_FRAMERANGE) + * -> 3) y-axis, so select all frames within channels that region included (validation with BEZT_OK_VALUERANGE) + */ + +/* Borderselect only selects keyframes now, as overshooting handles often get caught too, + * which means that they may be inadvertantly moved as well. + * Also, for convenience, handles should get same status as keyframe (if it was within bounds) + */ +static void borderselect_graphkeys (bAnimContext *ac, rcti rect, short mode, short selectmode) +{ + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + BeztEditData bed; + BeztEditFunc ok_cb, select_cb; + View2D *v2d= &ac->ar->v2d; + rctf rectf; + + /* convert mouse coordinates to frame ranges and channel coordinates corrected for view pan/zoom */ + UI_view2d_region_to_view(v2d, rect.xmin, rect.ymin, &rectf.xmin, &rectf.ymin); + UI_view2d_region_to_view(v2d, rect.xmax, rect.ymax, &rectf.xmax, &rectf.ymax); + + /* filter data */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVESONLY | ANIMFILTER_CURVEVISIBLE); + ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + + /* get beztriple editing/validation funcs */ + select_cb= ANIM_editkeyframes_select(selectmode); + ok_cb= ANIM_editkeyframes_ok(mode); + + /* init editing data */ + memset(&bed, 0, sizeof(BeztEditData)); + bed.data= &rectf; + + /* loop over data, doing border select */ + for (ale= anim_data.first; ale; ale= ale->next) { + Object *nob= ANIM_nla_mapping_get(ac, ale); + + /* set horizontal range (if applicable) */ + if (mode != BEZT_OK_VALUERANGE) { + /* if channel is mapped in NLA, apply correction */ + if (nob) { + bed.f1= get_action_frame(nob, rectf.xmin); + bed.f2= get_action_frame(nob, rectf.xmax); + } + else { + bed.f1= rectf.xmin; + bed.f2= rectf.xmax; + } + } + else { + bed.f1= rectf.ymin; + bed.f2= rectf.ymax; + } + + /* select keyframes that are in the appropriate places */ + ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, ok_cb, select_cb, NULL); + } + + /* cleanup */ + BLI_freelistN(&anim_data); +} + +/* ------------------- */ + +static int graphkeys_borderselect_exec(bContext *C, wmOperator *op) +{ + bAnimContext ac; + rcti rect; + short mode=0, selectmode=0; + int event; + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + /* get settings from operator */ + rect.xmin= RNA_int_get(op->ptr, "xmin"); + rect.ymin= RNA_int_get(op->ptr, "ymin"); + rect.xmax= RNA_int_get(op->ptr, "xmax"); + rect.ymax= RNA_int_get(op->ptr, "ymax"); + + event= RNA_int_get(op->ptr, "event_type"); + if (event == LEFTMOUSE) // FIXME... hardcoded + selectmode = SELECT_ADD; + else + selectmode = SELECT_SUBTRACT; + + /* selection 'mode' depends on whether borderselect region only matters on one axis */ + if (RNA_boolean_get(op->ptr, "axis_range")) { + /* mode depends on which axis of the range is larger to determine which axis to use + * - checking this in region-space is fine, as it's fundamentally still going to be a different rect size + * - the frame-range select option is favoured over the channel one (x over y), as frame-range one is often + * used for tweaking timing when "blocking", while channels is not that useful... + */ + if ((rect.xmax - rect.xmin) >= (rect.ymax - rect.ymin)) + mode= BEZT_OK_FRAMERANGE; + else + mode= BEZT_OK_VALUERANGE; + } + else + mode= BEZT_OK_REGION; + + /* apply borderselect action */ + borderselect_graphkeys(&ac, rect, mode, selectmode); + + return OPERATOR_FINISHED; +} + +void GRAPHEDIT_OT_keyframes_borderselect(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Border Select"; + ot->idname= "GRAPHEDIT_OT_keyframes_borderselect"; + + /* api callbacks */ + ot->invoke= WM_border_select_invoke; + ot->exec= graphkeys_borderselect_exec; + ot->modal= WM_border_select_modal; + + ot->poll= ED_operator_areaactive; + + /* flags */ + ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/; + + /* rna */ + RNA_def_int(ot->srna, "event_type", 0, INT_MIN, INT_MAX, "Event Type", "", INT_MIN, INT_MAX); + RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX); + RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX); + RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX); + RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX); + + RNA_def_boolean(ot->srna, "axis_range", 0, "Axis Range", ""); +} + +/* ******************** Column Select Operator **************************** */ +/* This operator works in one of four ways: + * - 1) select all keyframes in the same frame as a selected one (KKEY) + * - 2) select all keyframes in the same frame as the current frame marker (CTRL-KKEY) + * - 3) select all keyframes in the same frame as a selected markers (SHIFT-KKEY) + * - 4) select all keyframes that occur between selected markers (ALT-KKEY) + */ + +/* defines for column-select mode */ +static EnumPropertyItem prop_column_select_types[] = { + {GRAPHKEYS_COLUMNSEL_KEYS, "KEYS", "On Selected Keyframes", ""}, + {GRAPHKEYS_COLUMNSEL_CFRA, "CFRA", "On Current Frame", ""}, + {GRAPHKEYS_COLUMNSEL_MARKERS_COLUMN, "MARKERS_COLUMN", "On Selected Markers", ""}, + {GRAPHKEYS_COLUMNSEL_MARKERS_BETWEEN, "MARKERS_BETWEEN", "Between Min/Max Selected Markers", ""}, + {0, NULL, NULL, NULL} +}; + +/* ------------------- */ + +/* Selects all visible keyframes between the specified markers */ +static void markers_selectkeys_between (bAnimContext *ac) +{ + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + BeztEditFunc select_cb; + BeztEditData bed; + float min, max; + + /* get extreme markers */ + //get_minmax_markers(1, &min, &max); // FIXME... add back markers api! + min= (float)ac->scene->r.sfra; // xxx temp code + max= (float)ac->scene->r.efra; // xxx temp code + + if (min==max) return; + min -= 0.5f; + max += 0.5f; + + /* get editing funcs + data */ + select_cb= ANIM_editkeyframes_select(SELECT_ADD); + memset(&bed, 0, sizeof(BeztEditData)); + bed.f1= min; + bed.f2= max; + + /* filter data */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_CURVESONLY); + ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + + /* select keys in-between */ + for (ale= anim_data.first; ale; ale= ale->next) { + Object *nob= ANIM_nla_mapping_get(ac, ale); + + if (nob) { + ANIM_nla_mapping_apply_fcurve(nob, ale->key_data, 0, 1); + ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, NULL, select_cb, NULL); + ANIM_nla_mapping_apply_fcurve(nob, ale->key_data, 1, 1); + } + else { + ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, NULL, select_cb, NULL); + } + } + + /* Cleanup */ + BLI_freelistN(&anim_data); +} + + +/* helper callback for columnselect_graph_keys() -> populate list CfraElems with frame numbers from selected beztriples */ +// TODO: if some other code somewhere needs this, it'll be time to port this over to keyframes_edit.c!!! +static short bezt_to_cfraelem(BeztEditData *bed, BezTriple *bezt) +{ + /* only if selected */ + if (bezt->f2 & SELECT) { + CfraElem *ce= MEM_callocN(sizeof(CfraElem), "cfraElem"); + BLI_addtail(&bed->list, ce); + + ce->cfra= bezt->vec[1][0]; + } + + return 0; +} + +/* Selects all visible keyframes in the same frames as the specified elements */ +static void columnselect_graph_keys (bAnimContext *ac, short mode) +{ + ListBase anim_data= {NULL, NULL}; + bAnimListElem *ale; + int filter; + + Scene *scene= ac->scene; + CfraElem *ce; + BeztEditFunc select_cb, ok_cb; + BeztEditData bed; + + /* initialise keyframe editing data */ + memset(&bed, 0, sizeof(BeztEditData)); + + /* build list of columns */ + switch (mode) { + case GRAPHKEYS_COLUMNSEL_KEYS: /* list of selected keys */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_CURVESONLY); + ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + + for (ale= anim_data.first; ale; ale= ale->next) + ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, NULL, bezt_to_cfraelem, NULL); + + BLI_freelistN(&anim_data); + break; + + case GRAPHKEYS_COLUMNSEL_CFRA: /* current frame */ + /* make a single CfraElem for storing this */ + ce= MEM_callocN(sizeof(CfraElem), "cfraElem"); + BLI_addtail(&bed.list, ce); + + ce->cfra= (float)CFRA; + break; + + case GRAPHKEYS_COLUMNSEL_MARKERS_COLUMN: /* list of selected markers */ + // FIXME: markers api needs to be improved for this first! + //make_marker_cfra_list(&elems, 1); + return; // XXX currently, this does nothing! + break; + + default: /* invalid option */ + return; + } + + /* set up BezTriple edit callbacks */ + select_cb= ANIM_editkeyframes_select(SELECT_ADD); + ok_cb= ANIM_editkeyframes_ok(BEZT_OK_FRAME); + + /* loop through all of the keys and select additional keyframes + * based on the keys found to be selected above + */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_CURVESONLY); + ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + + for (ale= anim_data.first; ale; ale= ale->next) { + Object *nob= ANIM_nla_mapping_get(ac, ale); + + /* loop over cfraelems (stored in the BeztEditData->list) + * - we need to do this here, as we can apply fewer NLA-mapping conversions + */ + for (ce= bed.list.first; ce; ce= ce->next) { + /* set frame for validation callback to refer to */ + if (nob) + bed.f1= get_action_frame(nob, ce->cfra); + else + bed.f1= ce->cfra; + + /* select elements with frame number matching cfraelem */ + ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, ok_cb, select_cb, NULL); + } + } + + /* free elements */ + BLI_freelistN(&bed.list); + BLI_freelistN(&anim_data); +} + +/* ------------------- */ + +static int graphkeys_columnselect_exec(bContext *C, wmOperator *op) +{ + bAnimContext ac; + short mode; + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + /* action to take depends on the mode */ + mode= RNA_enum_get(op->ptr, "mode"); + + if (mode == GRAPHKEYS_COLUMNSEL_MARKERS_BETWEEN) + markers_selectkeys_between(&ac); + else + columnselect_graph_keys(&ac, mode); + + /* set notifier tha things have changed */ + ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_KEYFRAMES_SELECT); + + return OPERATOR_FINISHED; +} + +void GRAPHEDIT_OT_keyframes_columnselect (wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Select All"; + ot->idname= "GRAPHEDIT_OT_keyframes_columnselect"; + + /* api callbacks */ + ot->exec= graphkeys_columnselect_exec; + ot->poll= ED_operator_areaactive; + + /* flags */ + ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/; + + /* props */ + RNA_def_enum(ot->srna, "mode", prop_column_select_types, 0, "Mode", ""); +} + +/* ******************** Mouse-Click Select Operator *********************** */ +/* This operator works in one of three ways: + * - 1) keyframe under mouse - no special modifiers + * - 2) all keyframes on the same side of current frame indicator as mouse - ALT modifier + * - 3) column select all keyframes in frame under mouse - CTRL modifier + * + * In addition to these basic options, the SHIFT modifier can be used to toggle the + * selection mode between replacing the selection (without) and inverting the selection (with). + */ + +/* defines for left-right select tool */ +static EnumPropertyItem prop_leftright_select_types[] = { + {GRAPHKEYS_LRSEL_TEST, "CHECK", "Check if Select Left or Right", ""}, + {GRAPHKEYS_LRSEL_NONE, "OFF", "Don't select", ""}, + {GRAPHKEYS_LRSEL_LEFT, "LEFT", "Before current frame", ""}, + {GRAPHKEYS_LRSEL_RIGHT, "RIGHT", "After current frame", ""}, + {0, NULL, NULL, NULL} +}; + +/* ------------------- */ + +enum { + NEAREST_HANDLE_LEFT = 0, + NEAREST_HANDLE_KEY, + NEAREST_HANDLE_RIGHT +} eHandleIndex; + +/* Find the vertex (either handle (0/2) or the keyframe (1)) that is nearest to the mouse cursor (in area coordinates) + * Selected verts get a disadvantage, to make it easier to select handles behind. + * Returns eHandleIndex + */ +static short findnearest_fcurve_vert (bAnimContext *ac, int mval[2], FCurve **fcurve, BezTriple **bezt) +{ + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + SpaceIpo *sipo= (SpaceIpo *)ac->sa->spacedata.first; + View2D *v2d= &ac->ar->v2d; + int hpoint=0, sco[3][2]; + int dist= 100, temp, i; + + /* clear pointers first */ + *fcurve= 0; + *bezt= 0; + + /* get curves to search through */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_CURVESONLY); + ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + + for (ale= anim_data.first; ale; ale= ale->next) { + FCurve *fcu= (FCurve *)ale->key_data; + + /* try to progressively get closer to the right point... */ + if (fcu->bezt) { + BezTriple *bezt1=fcu->bezt, *prevbezt=NULL; + + for (i=0; i < fcu->totvert; i++, prevbezt=bezt1, bezt1++) { + /* convert beztriple points to screen-space */ + UI_view2d_to_region_no_clip(v2d, bezt1->vec[0][0], bezt1->vec[0][1], &sco[0][0], &sco[0][1]); + UI_view2d_to_region_no_clip(v2d, bezt1->vec[1][0], bezt1->vec[1][1], &sco[1][0], &sco[1][1]); + UI_view2d_to_region_no_clip(v2d, bezt1->vec[2][0], bezt1->vec[2][1], &sco[2][0], &sco[2][1]); + + /* keyframe - do select? */ + temp= abs(mval[0] - sco[1][0]) + abs(mval[1] - sco[1][1]); + + if (bezt1->f2 & SELECT) + temp += 5; + + if (temp < dist) { + hpoint= NEAREST_HANDLE_KEY; + *bezt= bezt1; + dist= temp; + *fcurve= fcu; + } + + /* handles - only do them if they're visible */ + if ((sipo->flag & SIPO_NOHANDLES)==0) { + /* first handle only visible if previous segment had handles */ + if ( (!prevbezt && (bezt1->ipo==BEZT_IPO_BEZ)) || (prevbezt && (prevbezt->ipo==BEZT_IPO_BEZ)) ) + { + temp= -3 + abs(mval[0] - sco[0][0]) + abs(mval[1] - sco[0][1]); + if (bezt1->f1 & SELECT) + temp += 5; + + if (temp < dist) { + hpoint= NEAREST_HANDLE_LEFT; + *bezt= bezt1; + dist= temp; + *fcurve= fcu; + } + } + + /* second handle only visible if this segment is bezier */ + if (bezt1->ipo == BEZT_IPO_BEZ) + { + temp= abs(mval[0] - sco[2][0]) + abs(mval[1] - sco[2][1]); + if (bezt1->f3 & SELECT) + temp += 5; + + if (temp < dist) { + hpoint= NEAREST_HANDLE_RIGHT; + *bezt=bezt1; + dist= temp; + *fcurve= fcu; + } + } + } + } + } + } + + /* free channels */ + BLI_freelistN(&anim_data); + + /* return handle */ + return hpoint; +} + +/* option 1) select keyframe directly under mouse */ +static void mouse_graph_keys (bAnimContext *ac, int mval[], short selectmode) +{ + FCurve *fcu; + BezTriple *bezt; + short handle; + + /* find the beztriple that we're selecting, and the handle that was clicked on */ + handle= findnearest_fcurve_vert(ac, mval, &fcu, &bezt); + + /* check if anything to select */ + if (fcu == NULL) + return; + + /* deselect all other curves? */ + if (selectmode == SELECT_REPLACE) { + deselect_graph_keys(ac, 0, SELECT_SUBTRACT); // XXX this should be curves, not keys + selectmode= SELECT_ADD; + } + + /* select or deselect? */ + if (selectmode == SELECT_ADD) + fcu->flag |= (FCURVE_ACTIVE|FCURVE_SELECTED); + else if (selectmode == SELECT_INVERT) + fcu->flag ^= (FCURVE_ACTIVE|FCURVE_SELECTED); + + /* if we're selecting points too */ + if ( ((fcu->flag & FCURVE_PROTECTED)==0) /*|| (curvesonly == 0) */) { + /* only if there's keyframe */ + if (bezt) { + /* depends on selection mode */ + if (selectmode == SELECT_INVERT) { + /* keyframe - invert select of all */ + if (handle == NEAREST_HANDLE_KEY) { + if (BEZSELECTED(bezt)) { + BEZ_DESEL(bezt); + } + else { + BEZ_SEL(bezt); + } + } + + /* handles - toggle selection of relevant handle */ + else if (handle == NEAREST_HANDLE_LEFT) { + /* toggle selection */ + bezt->f1 ^= SELECT; + } + else { + /* toggle selection */ + bezt->f3 ^= SELECT; + } + } + else { + /* deselect all other keyframes? */ + deselect_graph_keys(ac, 0, SELECT_SUBTRACT); + + /* if the keyframe was clicked on, select all verts of given beztriple */ + if (handle == NEAREST_HANDLE_KEY) { + BEZ_SEL(bezt); + } + /* otherwise, select the handle that applied */ + else if (handle == NEAREST_HANDLE_LEFT) + bezt->f1 |= SELECT; + else + bezt->f3 |= SELECT; + } + } + } +} + +/* Option 2) Selects all the keyframes on either side of the current frame (depends on which side the mouse is on) */ +static void graphkeys_select_leftright (bAnimContext *ac, short leftright, short select_mode) +{ + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + BeztEditFunc ok_cb, select_cb; + BeztEditData bed; + Scene *scene= ac->scene; + + /* if select mode is replace, deselect all keyframes first */ + if (select_mode==SELECT_REPLACE) { + select_mode=SELECT_ADD; + deselect_graph_keys(ac, 0, SELECT_SUBTRACT); + } + + /* set callbacks and editing data */ + ok_cb= ANIM_editkeyframes_ok(BEZT_OK_FRAMERANGE); + select_cb= ANIM_editkeyframes_select(select_mode); + + memset(&bed, 0, sizeof(BeztEditFunc)); + if (leftright == GRAPHKEYS_LRSEL_LEFT) { + bed.f1 = -MAXFRAMEF; + bed.f2 = (float)(CFRA + 0.1f); + } + else { + bed.f1 = (float)(CFRA - 0.1f); + bed.f2 = MAXFRAMEF; + } + + /* filter data */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_CURVESONLY); + ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + + /* select keys on the side where most data occurs */ + for (ale= anim_data.first; ale; ale= ale->next) { + Object *nob= ANIM_nla_mapping_get(ac, ale); + + if (nob) { + ANIM_nla_mapping_apply_fcurve(nob, ale->key_data, 0, 1); + ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, ok_cb, select_cb, NULL); + ANIM_nla_mapping_apply_fcurve(nob, ale->key_data, 1, 1); + } + else + ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, ok_cb, select_cb, NULL); + } + + /* Cleanup */ + BLI_freelistN(&anim_data); +} + +/* Option 3) Selects all visible keyframes in the same frame as the mouse click */ +static void mouse_columnselect_graph_keys (bAnimContext *ac, float selx) +{ + ListBase anim_data= {NULL, NULL}; + bAnimListElem *ale; + int filter; + + BeztEditFunc select_cb, ok_cb; + BeztEditData bed; + + /* initialise keyframe editing data */ + memset(&bed, 0, sizeof(BeztEditData)); + + /* set up BezTriple edit callbacks */ + select_cb= ANIM_editkeyframes_select(SELECT_ADD); + ok_cb= ANIM_editkeyframes_ok(BEZT_OK_FRAME); + + /* loop through all of the keys and select additional keyframes + * based on the keys found to be selected above + */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_CURVESONLY); + ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + + for (ale= anim_data.first; ale; ale= ale->next) { + Object *nob= ANIM_nla_mapping_get(ac, ale); + + /* set frame for validation callback to refer to */ + // XXX have a more sensitive range? + if (nob) + bed.f1= get_action_frame(nob, selx); + else + bed.f1= selx; + + /* select elements with frame number matching cfra */ + ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, ok_cb, select_cb, NULL); + } + + /* free elements */ + BLI_freelistN(&bed.list); + BLI_freelistN(&anim_data); +} + +/* ------------------- */ + +/* handle clicking */ +static int graphkeys_clickselect_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + bAnimContext ac; + Scene *scene; + ARegion *ar; + View2D *v2d; + short selectmode; + int mval[2]; + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + /* get useful pointers from animation context data */ + scene= ac.scene; + ar= ac.ar; + v2d= &ar->v2d; + + /* get mouse coordinates (in region coordinates) */ + mval[0]= (event->x - ar->winrct.xmin); + mval[1]= (event->y - ar->winrct.ymin); + + /* select mode is either replace (deselect all, then add) or add/extend */ + // XXX this is currently only available for normal select only + if (RNA_boolean_get(op->ptr, "extend_select")) + selectmode= SELECT_INVERT; + else + selectmode= SELECT_REPLACE; + + /* figure out action to take */ + if (RNA_enum_get(op->ptr, "left_right")) { + /* select all keys on same side of current frame as mouse */ + float x; + + UI_view2d_region_to_view(v2d, mval[0], mval[1], &x, NULL); + if (x < CFRA) + RNA_int_set(op->ptr, "left_right", GRAPHKEYS_LRSEL_LEFT); + else + RNA_int_set(op->ptr, "left_right", GRAPHKEYS_LRSEL_RIGHT); + + graphkeys_select_leftright(&ac, RNA_enum_get(op->ptr, "left_right"), selectmode); + } + else if (RNA_boolean_get(op->ptr, "column_select")) { + /* select all the keyframes that occur on the same frame as where the mouse clicked */ + float x; + + /* figure out where (the frame) the mouse clicked, and set all keyframes in that frame */ + UI_view2d_region_to_view(v2d, mval[0], mval[1], &x, NULL); + mouse_columnselect_graph_keys(&ac, x); + } + else { + /* select keyframe under mouse */ + mouse_graph_keys(&ac, mval, selectmode); // xxx curves only should become an arg + // XXX activate transform... + } + + /* set notifier tha things have changed */ + ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_BOTH); + + /* for tweak grab to work */ + return OPERATOR_FINISHED|OPERATOR_PASS_THROUGH; +} + +void GRAPHEDIT_OT_keyframes_clickselect (wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Mouse Select Keys"; + ot->idname= "GRAPHEDIT_OT_keyframes_clickselect"; + + /* api callbacks */ + ot->invoke= graphkeys_clickselect_invoke; + ot->poll= ED_operator_areaactive; + + /* id-props */ + // XXX should we make this into separate operators? + RNA_def_enum(ot->srna, "left_right", NULL /* XXX prop_graphkeys_clickselect_items */, 0, "Left Right", ""); // ALTKEY + RNA_def_boolean(ot->srna, "extend_select", 0, "Extend Select", ""); // SHIFTKEY + RNA_def_boolean(ot->srna, "column_select", 0, "Column Select", ""); // CTRLKEY +} + +/* ************************************************************************** */ |