- use clear, set, add, enable, disable and toggle as a prefix in operator names
[blender.git] / source / blender / editors / space_graph / graph_select.c
1 /**
2  * $Id: editaction.c 17746 2008-12-08 11:19:44Z aligorith $
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2008 Blender Foundation
21  *
22  * Contributor(s): Joshua Leung
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 #include <math.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <float.h>
31
32 #ifdef HAVE_CONFIG_H
33 #include <config.h>
34 #endif
35
36 #include "MEM_guardedalloc.h"
37
38 #include "BLI_blenlib.h"
39 #include "BLI_arithb.h"
40
41 #include "DNA_anim_types.h"
42 #include "DNA_action_types.h"
43 #include "DNA_armature_types.h"
44 #include "DNA_camera_types.h"
45 #include "DNA_curve_types.h"
46 #include "DNA_object_types.h"
47 #include "DNA_screen_types.h"
48 #include "DNA_scene_types.h"
49 #include "DNA_space_types.h"
50 #include "DNA_constraint_types.h"
51 #include "DNA_key_types.h"
52 #include "DNA_lamp_types.h"
53 #include "DNA_material_types.h"
54 #include "DNA_userdef_types.h"
55 #include "DNA_gpencil_types.h"
56 #include "DNA_windowmanager_types.h"
57
58 #include "RNA_access.h"
59 #include "RNA_define.h"
60
61 #include "BKE_action.h"
62 #include "BKE_depsgraph.h"
63 #include "BKE_fcurve.h"
64 #include "BKE_key.h"
65 #include "BKE_material.h"
66 #include "BKE_object.h"
67 #include "BKE_context.h"
68 #include "BKE_utildefines.h"
69
70 #include "UI_view2d.h"
71
72 #include "ED_anim_api.h"
73 #include "ED_keyframing.h"
74 #include "ED_keyframes_draw.h"
75 #include "ED_keyframes_edit.h"
76 #include "ED_screen.h"
77 #include "ED_space_api.h"
78
79 #include "WM_api.h"
80 #include "WM_types.h"
81
82 #include "graph_intern.h"
83
84
85 /* ************************************************************************** */
86 /* KEYFRAMES STUFF */
87
88 /* ******************** Deselect All Operator ***************************** */
89 /* This operator works in one of three ways:
90  *      1) (de)select all (AKEY) - test if select all or deselect all
91  *      2) invert all (CTRL-IKEY) - invert selection of all keyframes
92  *      3) (de)select all - no testing is done; only for use internal tools as normal function...
93  */
94
95 /* Deselects keyframes in the action editor
96  *      - This is called by the deselect all operator, as well as other ones!
97  *
98  *      - test: check if select or deselect all
99  *      - sel: how to select keyframes 
100  *              0 = deselect
101  *              1 = select
102  *              2 = invert
103  */
104 static void deselect_graph_keys (bAnimContext *ac, short test, short sel)
105 {
106         ListBase anim_data = {NULL, NULL};
107         bAnimListElem *ale;
108         int filter;
109         
110         BeztEditData bed;
111         BeztEditFunc test_cb, sel_cb;
112         
113         /* determine type-based settings */
114         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_CURVESONLY);
115         
116         /* filter data */
117         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
118         
119         /* init BezTriple looping data */
120         memset(&bed, 0, sizeof(BeztEditData));
121         test_cb= ANIM_editkeyframes_ok(BEZT_OK_SELECTED);
122         
123         /* See if we should be selecting or deselecting */
124         if (test) {
125                 for (ale= anim_data.first; ale; ale= ale->next) {
126                         if (ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, NULL, test_cb, NULL)) {
127                                 sel= SELECT_SUBTRACT;
128                                 break;
129                         }
130                 }
131         }
132         
133         /* convert sel to selectmode, and use that to get editor */
134         sel_cb= ANIM_editkeyframes_select(sel);
135         
136         /* Now set the flags */
137         for (ale= anim_data.first; ale; ale= ale->next) {
138                 FCurve *fcu= (FCurve *)ale->key_data;
139                 
140                 /* Keyframes First */
141                 ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, NULL, sel_cb, NULL);
142                 
143                 /* Curve Selection too */
144                 if (sel == SELECT_ADD)
145                         fcu->flag |= FCURVE_SELECTED;
146                 else if (sel == SELECT_SUBTRACT)
147                         fcu->flag &= ~FCURVE_SELECTED;
148                 else
149                         fcu->flag ^= FCURVE_SELECTED;
150                 fcu->flag &= ~FCURVE_ACTIVE;
151         }
152         
153         /* Cleanup */
154         BLI_freelistN(&anim_data);
155 }
156
157 /* ------------------- */
158
159 static int graphkeys_deselectall_exec(bContext *C, wmOperator *op)
160 {
161         bAnimContext ac;
162         
163         /* get editor data */
164         if (ANIM_animdata_get_context(C, &ac) == 0)
165                 return OPERATOR_CANCELLED;
166                 
167         /* 'standard' behaviour - check if selected, then apply relevant selection */
168         if (RNA_boolean_get(op->ptr, "invert"))
169                 deselect_graph_keys(&ac, 0, SELECT_INVERT);
170         else
171                 deselect_graph_keys(&ac, 1, SELECT_ADD);
172         
173         /* set notifier tha things have changed */
174         ED_area_tag_redraw(CTX_wm_area(C)); // FIXME... should be updating 'keyframes' data context or so instead!
175         
176         return OPERATOR_FINISHED;
177 }
178  
179 void GRAPHEDIT_OT_keyframes_select_all_toggle (wmOperatorType *ot)
180 {
181         /* identifiers */
182         ot->name= "Select All";
183         ot->idname= "GRAPHEDIT_OT_keyframes_select_all_toggle";
184         
185         /* api callbacks */
186         ot->exec= graphkeys_deselectall_exec;
187         ot->poll= ED_operator_areaactive;
188         
189         /* flags */
190         ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/;
191         
192         /* props */
193         RNA_def_boolean(ot->srna, "invert", 0, "Invert", "");
194 }
195
196 /* ******************** Border Select Operator **************************** */
197 /* This operator currently works in one of three ways:
198  *      -> BKEY         - 1) all keyframes within region are selected (validation with BEZT_OK_REGION)
199  *      -> ALT-BKEY - depending on which axis of the region was larger...
200  *              -> 2) x-axis, so select all frames within frame range (validation with BEZT_OK_FRAMERANGE)
201  *              -> 3) y-axis, so select all frames within channels that region included (validation with BEZT_OK_VALUERANGE)
202  */
203
204 /* Borderselect only selects keyframes now, as overshooting handles often get caught too,
205  * which means that they may be inadvertantly moved as well.
206  * Also, for convenience, handles should get same status as keyframe (if it was within bounds)
207  */
208 static void borderselect_graphkeys (bAnimContext *ac, rcti rect, short mode, short selectmode)
209 {
210         ListBase anim_data = {NULL, NULL};
211         bAnimListElem *ale;
212         int filter;
213         
214         BeztEditData bed;
215         BeztEditFunc ok_cb, select_cb;
216         View2D *v2d= &ac->ar->v2d;
217         rctf rectf;
218         
219         /* convert mouse coordinates to frame ranges and channel coordinates corrected for view pan/zoom */
220         UI_view2d_region_to_view(v2d, rect.xmin, rect.ymin, &rectf.xmin, &rectf.ymin);
221         UI_view2d_region_to_view(v2d, rect.xmax, rect.ymax, &rectf.xmax, &rectf.ymax);
222         
223         /* filter data */
224         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVESONLY | ANIMFILTER_CURVEVISIBLE);
225         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
226         
227         /* get beztriple editing/validation funcs  */
228         select_cb= ANIM_editkeyframes_select(selectmode);
229         ok_cb= ANIM_editkeyframes_ok(mode);
230         
231         /* init editing data */
232         memset(&bed, 0, sizeof(BeztEditData));
233         bed.data= &rectf;
234         
235         /* loop over data, doing border select */
236         for (ale= anim_data.first; ale; ale= ale->next) {
237                 Object *nob= ANIM_nla_mapping_get(ac, ale);
238                 
239                 /* set horizontal range (if applicable) */
240                 if (mode != BEZT_OK_VALUERANGE) {
241                         /* if channel is mapped in NLA, apply correction */
242                         if (nob) {
243                                 bed.f1= get_action_frame(nob, rectf.xmin);
244                                 bed.f2= get_action_frame(nob, rectf.xmax);
245                         }
246                         else {
247                                 bed.f1= rectf.xmin;
248                                 bed.f2= rectf.xmax;
249                         }
250                 }
251                 else {
252                         bed.f1= rectf.ymin;
253                         bed.f2= rectf.ymax;
254                 }
255                 
256                 /* select keyframes that are in the appropriate places */
257                 ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, ok_cb, select_cb, NULL);
258         }
259         
260         /* cleanup */
261         BLI_freelistN(&anim_data);
262 }
263
264 /* ------------------- */
265
266 static int graphkeys_borderselect_exec(bContext *C, wmOperator *op)
267 {
268         bAnimContext ac;
269         rcti rect;
270         short mode=0, selectmode=0;
271         int event;
272         
273         /* get editor data */
274         if (ANIM_animdata_get_context(C, &ac) == 0)
275                 return OPERATOR_CANCELLED;
276         
277         /* get settings from operator */
278         rect.xmin= RNA_int_get(op->ptr, "xmin");
279         rect.ymin= RNA_int_get(op->ptr, "ymin");
280         rect.xmax= RNA_int_get(op->ptr, "xmax");
281         rect.ymax= RNA_int_get(op->ptr, "ymax");
282                 
283         event= RNA_int_get(op->ptr, "event_type");
284         if (event == LEFTMOUSE) // FIXME... hardcoded
285                 selectmode = SELECT_ADD;
286         else
287                 selectmode = SELECT_SUBTRACT;
288         
289         /* selection 'mode' depends on whether borderselect region only matters on one axis */
290         if (RNA_boolean_get(op->ptr, "axis_range")) {
291                 /* mode depends on which axis of the range is larger to determine which axis to use 
292                  *      - checking this in region-space is fine, as it's fundamentally still going to be a different rect size
293                  *      - the frame-range select option is favoured over the channel one (x over y), as frame-range one is often
294                  *        used for tweaking timing when "blocking", while channels is not that useful...
295                  */
296                 if ((rect.xmax - rect.xmin) >= (rect.ymax - rect.ymin))
297                         mode= BEZT_OK_FRAMERANGE;
298                 else
299                         mode= BEZT_OK_VALUERANGE;
300         }
301         else 
302                 mode= BEZT_OK_REGION;
303         
304         /* apply borderselect action */
305         borderselect_graphkeys(&ac, rect, mode, selectmode);
306         
307         return OPERATOR_FINISHED;
308
309
310 void GRAPHEDIT_OT_keyframes_select_border(wmOperatorType *ot)
311 {
312         /* identifiers */
313         ot->name= "Border Select";
314         ot->idname= "GRAPHEDIT_OT_keyframes_select_border";
315         
316         /* api callbacks */
317         ot->invoke= WM_border_select_invoke;
318         ot->exec= graphkeys_borderselect_exec;
319         ot->modal= WM_border_select_modal;
320         
321         ot->poll= ED_operator_areaactive;
322         
323         /* flags */
324         ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/;
325         
326         /* rna */
327         RNA_def_int(ot->srna, "event_type", 0, INT_MIN, INT_MAX, "Event Type", "", INT_MIN, INT_MAX);
328         RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
329         RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
330         RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
331         RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
332         
333         RNA_def_boolean(ot->srna, "axis_range", 0, "Axis Range", "");
334 }
335
336 /* ******************** Column Select Operator **************************** */
337 /* This operator works in one of four ways:
338  *      - 1) select all keyframes in the same frame as a selected one  (KKEY)
339  *      - 2) select all keyframes in the same frame as the current frame marker (CTRL-KKEY)
340  *      - 3) select all keyframes in the same frame as a selected markers (SHIFT-KKEY)
341  *      - 4) select all keyframes that occur between selected markers (ALT-KKEY)
342  */
343
344 /* defines for column-select mode */
345 static EnumPropertyItem prop_column_select_types[] = {
346         {GRAPHKEYS_COLUMNSEL_KEYS, "KEYS", "On Selected Keyframes", ""},
347         {GRAPHKEYS_COLUMNSEL_CFRA, "CFRA", "On Current Frame", ""},
348         {GRAPHKEYS_COLUMNSEL_MARKERS_COLUMN, "MARKERS_COLUMN", "On Selected Markers", ""},
349         {GRAPHKEYS_COLUMNSEL_MARKERS_BETWEEN, "MARKERS_BETWEEN", "Between Min/Max Selected Markers", ""},
350         {0, NULL, NULL, NULL}
351 };
352
353 /* ------------------- */ 
354
355 /* Selects all visible keyframes between the specified markers */
356 static void markers_selectkeys_between (bAnimContext *ac)
357 {
358         ListBase anim_data = {NULL, NULL};
359         bAnimListElem *ale;
360         int filter;
361         
362         BeztEditFunc select_cb;
363         BeztEditData bed;
364         float min, max;
365         
366         /* get extreme markers */
367         //get_minmax_markers(1, &min, &max); // FIXME... add back markers api!
368         min= (float)ac->scene->r.sfra; // xxx temp code
369         max= (float)ac->scene->r.efra; // xxx temp code
370         
371         if (min==max) return;
372         min -= 0.5f;
373         max += 0.5f;
374         
375         /* get editing funcs + data */
376         select_cb= ANIM_editkeyframes_select(SELECT_ADD);
377         memset(&bed, 0, sizeof(BeztEditData));
378         bed.f1= min; 
379         bed.f2= max;
380         
381         /* filter data */
382         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_CURVESONLY);
383         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
384         
385         /* select keys in-between */
386         for (ale= anim_data.first; ale; ale= ale->next) {
387                 Object *nob= ANIM_nla_mapping_get(ac, ale);
388                 
389                 if (nob) {      
390                         ANIM_nla_mapping_apply_fcurve(nob, ale->key_data, 0, 1);
391                         ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, NULL, select_cb, NULL);
392                         ANIM_nla_mapping_apply_fcurve(nob, ale->key_data, 1, 1);
393                 }
394                 else {
395                         ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, NULL, select_cb, NULL);
396                 }
397         }
398         
399         /* Cleanup */
400         BLI_freelistN(&anim_data);
401 }
402
403
404 /* helper callback for columnselect_graph_keys() -> populate list CfraElems with frame numbers from selected beztriples */
405 // TODO: if some other code somewhere needs this, it'll be time to port this over to keyframes_edit.c!!!
406 static short bezt_to_cfraelem(BeztEditData *bed, BezTriple *bezt)
407 {
408         /* only if selected */
409         if (bezt->f2 & SELECT) {
410                 CfraElem *ce= MEM_callocN(sizeof(CfraElem), "cfraElem");
411                 BLI_addtail(&bed->list, ce);
412                 
413                 ce->cfra= bezt->vec[1][0];
414         }
415         
416         return 0;
417 }
418
419 /* Selects all visible keyframes in the same frames as the specified elements */
420 static void columnselect_graph_keys (bAnimContext *ac, short mode)
421 {
422         ListBase anim_data= {NULL, NULL};
423         bAnimListElem *ale;
424         int filter;
425         
426         Scene *scene= ac->scene;
427         CfraElem *ce;
428         BeztEditFunc select_cb, ok_cb;
429         BeztEditData bed;
430         
431         /* initialise keyframe editing data */
432         memset(&bed, 0, sizeof(BeztEditData));
433         
434         /* build list of columns */
435         switch (mode) {
436                 case GRAPHKEYS_COLUMNSEL_KEYS: /* list of selected keys */
437                         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_CURVESONLY);
438                         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
439                         
440                         for (ale= anim_data.first; ale; ale= ale->next)
441                                 ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, NULL, bezt_to_cfraelem, NULL);
442                         
443                         BLI_freelistN(&anim_data);
444                         break;
445                         
446                 case GRAPHKEYS_COLUMNSEL_CFRA: /* current frame */
447                         /* make a single CfraElem for storing this */
448                         ce= MEM_callocN(sizeof(CfraElem), "cfraElem");
449                         BLI_addtail(&bed.list, ce);
450                         
451                         ce->cfra= (float)CFRA;
452                         break;
453                         
454                 case GRAPHKEYS_COLUMNSEL_MARKERS_COLUMN: /* list of selected markers */
455                         // FIXME: markers api needs to be improved for this first!
456                         //make_marker_cfra_list(&elems, 1);
457                         return; // XXX currently, this does nothing!
458                         break;
459                         
460                 default: /* invalid option */
461                         return;
462         }
463         
464         /* set up BezTriple edit callbacks */
465         select_cb= ANIM_editkeyframes_select(SELECT_ADD);
466         ok_cb= ANIM_editkeyframes_ok(BEZT_OK_FRAME);
467         
468         /* loop through all of the keys and select additional keyframes
469          * based on the keys found to be selected above
470          */
471         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_CURVESONLY);
472         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
473         
474         for (ale= anim_data.first; ale; ale= ale->next) {
475                 Object *nob= ANIM_nla_mapping_get(ac, ale);
476                 
477                 /* loop over cfraelems (stored in the BeztEditData->list)
478                  *      - we need to do this here, as we can apply fewer NLA-mapping conversions
479                  */
480                 for (ce= bed.list.first; ce; ce= ce->next) {
481                         /* set frame for validation callback to refer to */
482                         if (nob)
483                                 bed.f1= get_action_frame(nob, ce->cfra);
484                         else
485                                 bed.f1= ce->cfra;
486                         
487                         /* select elements with frame number matching cfraelem */
488                         ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, ok_cb, select_cb, NULL);
489                 }
490         }
491         
492         /* free elements */
493         BLI_freelistN(&bed.list);
494         BLI_freelistN(&anim_data);
495 }
496
497 /* ------------------- */
498
499 static int graphkeys_columnselect_exec(bContext *C, wmOperator *op)
500 {
501         bAnimContext ac;
502         short mode;
503         
504         /* get editor data */
505         if (ANIM_animdata_get_context(C, &ac) == 0)
506                 return OPERATOR_CANCELLED;
507                 
508         /* action to take depends on the mode */
509         mode= RNA_enum_get(op->ptr, "mode");
510         
511         if (mode == GRAPHKEYS_COLUMNSEL_MARKERS_BETWEEN)
512                 markers_selectkeys_between(&ac);
513         else
514                 columnselect_graph_keys(&ac, mode);
515         
516         /* set notifier tha things have changed */
517         ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_KEYFRAMES_SELECT);
518         
519         return OPERATOR_FINISHED;
520 }
521  
522 void GRAPHEDIT_OT_keyframes_columnselect (wmOperatorType *ot)
523 {
524         /* identifiers */
525         ot->name= "Select All";
526         ot->idname= "GRAPHEDIT_OT_keyframes_columnselect";
527         
528         /* api callbacks */
529         ot->exec= graphkeys_columnselect_exec;
530         ot->poll= ED_operator_areaactive;
531         
532         /* flags */
533         ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/;
534         
535         /* props */
536         RNA_def_enum(ot->srna, "mode", prop_column_select_types, 0, "Mode", "");
537 }
538
539 /* ******************** Mouse-Click Select Operator *********************** */
540 /* This operator works in one of three ways:
541  *      - 1) keyframe under mouse - no special modifiers
542  *      - 2) all keyframes on the same side of current frame indicator as mouse - ALT modifier
543  *      - 3) column select all keyframes in frame under mouse - CTRL modifier
544  *
545  * In addition to these basic options, the SHIFT modifier can be used to toggle the 
546  * selection mode between replacing the selection (without) and inverting the selection (with).
547  */
548
549 /* defines for left-right select tool */
550 static EnumPropertyItem prop_leftright_select_types[] = {
551         {GRAPHKEYS_LRSEL_TEST, "CHECK", "Check if Select Left or Right", ""},
552         {GRAPHKEYS_LRSEL_NONE, "OFF", "Don't select", ""},
553         {GRAPHKEYS_LRSEL_LEFT, "LEFT", "Before current frame", ""},
554         {GRAPHKEYS_LRSEL_RIGHT, "RIGHT", "After current frame", ""},
555         {0, NULL, NULL, NULL}
556 };
557
558 /* ------------------- */
559
560 enum {
561         NEAREST_HANDLE_LEFT     = 0,
562         NEAREST_HANDLE_KEY,
563         NEAREST_HANDLE_RIGHT
564 } eHandleIndex; 
565  
566 /* Find the vertex (either handle (0/2) or the keyframe (1)) that is nearest to the mouse cursor (in area coordinates)  
567  * Selected verts get a disadvantage, to make it easier to select handles behind.
568  * Returns eHandleIndex
569  */
570 static short findnearest_fcurve_vert (bAnimContext *ac, int mval[2], FCurve **fcurve, BezTriple **bezt)
571 {
572         ListBase anim_data = {NULL, NULL};
573         bAnimListElem *ale;
574         int filter;
575         
576         SpaceIpo *sipo= (SpaceIpo *)ac->sa->spacedata.first;
577         View2D *v2d= &ac->ar->v2d;
578         int hpoint=0, sco[3][2];
579         int dist= 100, temp, i;
580         
581         /* clear pointers first */
582         *fcurve= 0;
583         *bezt= 0;
584         
585         /* get curves to search through */
586         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_CURVESONLY);
587         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
588         
589         for (ale= anim_data.first; ale; ale= ale->next) {
590                 FCurve *fcu= (FCurve *)ale->key_data;
591                 
592                 /* try to progressively get closer to the right point... */
593                 if (fcu->bezt) {
594                         BezTriple *bezt1=fcu->bezt, *prevbezt=NULL;
595                         
596                         for (i=0; i < fcu->totvert; i++, prevbezt=bezt1, bezt1++) {
597                                 /* convert beztriple points to screen-space */
598                                 UI_view2d_to_region_no_clip(v2d, bezt1->vec[0][0], bezt1->vec[0][1], &sco[0][0], &sco[0][1]);
599                                 UI_view2d_to_region_no_clip(v2d, bezt1->vec[1][0], bezt1->vec[1][1], &sco[1][0], &sco[1][1]);
600                                 UI_view2d_to_region_no_clip(v2d, bezt1->vec[2][0], bezt1->vec[2][1], &sco[2][0], &sco[2][1]);
601                                 
602                                 /* keyframe - do select? */
603                                 temp= abs(mval[0] - sco[1][0]) + abs(mval[1] - sco[1][1]);
604                                 
605                                 if (bezt1->f2 & SELECT) 
606                                         temp += 5;
607                                 
608                                 if (temp < dist) { 
609                                         hpoint= NEAREST_HANDLE_KEY; 
610                                         *bezt= bezt1; 
611                                         dist= temp; 
612                                         *fcurve= fcu; 
613                                 }
614                                 
615                                 /* handles - only do them if they're visible */
616                                 // XXX also need to check for int-values only?
617                                 if ((sipo->flag & SIPO_NOHANDLES)==0) {
618                                         /* first handle only visible if previous segment had handles */
619                                         if ( (!prevbezt && (bezt1->ipo==BEZT_IPO_BEZ)) || (prevbezt && (prevbezt->ipo==BEZT_IPO_BEZ)) )
620                                         {
621                                                 temp= -3 + abs(mval[0] - sco[0][0]) + abs(mval[1] - sco[0][1]);
622                                                 if (bezt1->f1 & SELECT) 
623                                                         temp += 5;
624                                                         
625                                                 if (temp < dist) { 
626                                                         hpoint= NEAREST_HANDLE_LEFT; 
627                                                         *bezt= bezt1; 
628                                                         dist= temp; 
629                                                         *fcurve= fcu; 
630                                                 }
631                                         }
632                                         
633                                         /* second handle only visible if this segment is bezier */
634                                         if (bezt1->ipo == BEZT_IPO_BEZ) 
635                                         {
636                                                 temp= abs(mval[0] - sco[2][0]) + abs(mval[1] - sco[2][1]);
637                                                 if (bezt1->f3 & SELECT) 
638                                                         temp += 5;
639                                                 
640                                                 if (temp < dist) { 
641                                                         hpoint= NEAREST_HANDLE_RIGHT; 
642                                                         *bezt=bezt1; 
643                                                         dist= temp; 
644                                                         *fcurve= fcu; 
645                                                 }
646                                         }
647                                 }
648                         }
649                 }
650         }
651         
652         /* free channels */
653         BLI_freelistN(&anim_data);
654         
655         /* return handle */
656         return hpoint;
657 }
658  
659 /* option 1) select keyframe directly under mouse */
660 static void mouse_graph_keys (bAnimContext *ac, int mval[], short selectmode)
661 {
662         FCurve *fcu;
663         BezTriple *bezt;
664         short handle;
665         int filter;
666         
667         /* find the beztriple that we're selecting, and the handle that was clicked on */
668         handle= findnearest_fcurve_vert(ac, mval, &fcu, &bezt);
669         
670         /* check if anything to select */
671         if (fcu == NULL)        
672                 return;
673         
674         /* deselect all other curves? */
675         if (selectmode == SELECT_REPLACE) {
676                 deselect_graph_keys(ac, 0, SELECT_SUBTRACT);    // XXX this should be curves, not keys
677                 selectmode= SELECT_ADD;
678         }
679         
680         /* if we're selecting points too */
681         if ( ((fcu->flag & FCURVE_PROTECTED)==0) /*|| (curvesonly == 0) */) {
682                 /* only if there's keyframe */
683                 if (bezt) {
684                         /* depends on selection mode */
685                         if (selectmode == SELECT_INVERT) {
686                                 /* keyframe - invert select of all */
687                                 if (handle == NEAREST_HANDLE_KEY) {
688                                         if (BEZSELECTED(bezt)) {
689                                                 BEZ_DESEL(bezt);
690                                         }
691                                         else {
692                                                 BEZ_SEL(bezt);
693                                         }
694                                 }
695                                 
696                                 /* handles - toggle selection of relevant handle */
697                                 else if (handle == NEAREST_HANDLE_LEFT) {
698                                         /* toggle selection */
699                                         bezt->f1 ^= SELECT;
700                                 }
701                                 else {
702                                         /* toggle selection */
703                                         bezt->f3 ^= SELECT;
704                                 }
705                         }
706                         else {
707                                 /* deselect all other keyframes? */
708                                 deselect_graph_keys(ac, 0, SELECT_SUBTRACT);
709                                 
710                                 /* if the keyframe was clicked on, select all verts of given beztriple */
711                                 if (handle == NEAREST_HANDLE_KEY) {
712                                         BEZ_SEL(bezt);
713                                 }
714                                 /* otherwise, select the handle that applied */
715                                 else if (handle == NEAREST_HANDLE_LEFT) 
716                                         bezt->f1 |= SELECT;
717                                 else 
718                                         bezt->f3 |= SELECT;
719                         }
720                 }
721         }
722         
723         /* select or deselect curve? */
724         if (selectmode == SELECT_INVERT)
725                 fcu->flag ^= FCURVE_SELECTED;
726         else if (selectmode == SELECT_ADD)
727                 fcu->flag |= FCURVE_SELECTED;
728                 
729         /* set active F-Curve (NOTE: sync the filter flags with findnearest_fcurve_vert) */
730         if (fcu->flag & FCURVE_SELECTED) {
731                 filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_CURVESONLY);
732                 ANIM_set_active_channel(ac->data, ac->datatype, filter, fcu, ANIMTYPE_FCURVE);
733         }
734 }
735
736 /* Option 2) Selects all the keyframes on either side of the current frame (depends on which side the mouse is on) */
737 static void graphkeys_select_leftright (bAnimContext *ac, short leftright, short select_mode)
738 {
739         ListBase anim_data = {NULL, NULL};
740         bAnimListElem *ale;
741         int filter;
742         
743         BeztEditFunc ok_cb, select_cb;
744         BeztEditData bed;
745         Scene *scene= ac->scene;
746         
747         /* if select mode is replace, deselect all keyframes first */
748         if (select_mode==SELECT_REPLACE) {
749                 select_mode=SELECT_ADD;
750                 deselect_graph_keys(ac, 0, SELECT_SUBTRACT);
751         }
752         
753         /* set callbacks and editing data */
754         ok_cb= ANIM_editkeyframes_ok(BEZT_OK_FRAMERANGE);
755         select_cb= ANIM_editkeyframes_select(select_mode);
756         
757         memset(&bed, 0, sizeof(BeztEditFunc));
758         if (leftright == GRAPHKEYS_LRSEL_LEFT) {
759                 bed.f1 = -MAXFRAMEF;
760                 bed.f2 = (float)(CFRA + 0.1f);
761         } 
762         else {
763                 bed.f1 = (float)(CFRA - 0.1f);
764                 bed.f2 = MAXFRAMEF;
765         }
766         
767         /* filter data */
768         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_CURVESONLY);
769         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
770                 
771         /* select keys on the side where most data occurs */
772         for (ale= anim_data.first; ale; ale= ale->next) {
773                 Object *nob= ANIM_nla_mapping_get(ac, ale);
774                 
775                 if (nob) {
776                         ANIM_nla_mapping_apply_fcurve(nob, ale->key_data, 0, 1);
777                         ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, ok_cb, select_cb, NULL);
778                         ANIM_nla_mapping_apply_fcurve(nob, ale->key_data, 1, 1);
779                 }
780                 else
781                         ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, ok_cb, select_cb, NULL);
782         }
783         
784         /* Cleanup */
785         BLI_freelistN(&anim_data);
786 }
787
788 /* Option 3) Selects all visible keyframes in the same frame as the mouse click */
789 static void mouse_columnselect_graph_keys (bAnimContext *ac, float selx)
790 {
791         ListBase anim_data= {NULL, NULL};
792         bAnimListElem *ale;
793         int filter;
794         
795         BeztEditFunc select_cb, ok_cb;
796         BeztEditData bed;
797         
798         /* initialise keyframe editing data */
799         memset(&bed, 0, sizeof(BeztEditData));
800         
801         /* set up BezTriple edit callbacks */
802         select_cb= ANIM_editkeyframes_select(SELECT_ADD);
803         ok_cb= ANIM_editkeyframes_ok(BEZT_OK_FRAME);
804         
805         /* loop through all of the keys and select additional keyframes
806          * based on the keys found to be selected above
807          */
808         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_CURVESONLY);
809         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
810         
811         for (ale= anim_data.first; ale; ale= ale->next) {
812                 Object *nob= ANIM_nla_mapping_get(ac, ale);
813                 
814                 /* set frame for validation callback to refer to */
815                 // XXX have a more sensitive range?
816                 if (nob)
817                         bed.f1= get_action_frame(nob, selx);
818                 else
819                         bed.f1= selx;
820                 
821                 /* select elements with frame number matching cfra */
822                 ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, ok_cb, select_cb, NULL);
823         }
824         
825         /* free elements */
826         BLI_freelistN(&bed.list);
827         BLI_freelistN(&anim_data);
828 }
829  
830 /* ------------------- */
831
832 /* handle clicking */
833 static int graphkeys_clickselect_invoke(bContext *C, wmOperator *op, wmEvent *event)
834 {
835         bAnimContext ac;
836         Scene *scene;
837         ARegion *ar;
838         View2D *v2d;
839         short selectmode;
840         int mval[2];
841         
842         /* get editor data */
843         if (ANIM_animdata_get_context(C, &ac) == 0)
844                 return OPERATOR_CANCELLED;
845         
846         /* get useful pointers from animation context data */
847         scene= ac.scene;
848         ar= ac.ar;
849         v2d= &ar->v2d;
850         
851         /* get mouse coordinates (in region coordinates) */
852         mval[0]= (event->x - ar->winrct.xmin);
853         mval[1]= (event->y - ar->winrct.ymin);
854         
855         /* select mode is either replace (deselect all, then add) or add/extend */
856         // XXX this is currently only available for normal select only
857         if (RNA_boolean_get(op->ptr, "extend"))
858                 selectmode= SELECT_INVERT;
859         else
860                 selectmode= SELECT_REPLACE;
861         
862         /* figure out action to take */
863         if (RNA_enum_get(op->ptr, "left_right")) {
864                 /* select all keys on same side of current frame as mouse */
865                 float x;
866                 
867                 UI_view2d_region_to_view(v2d, mval[0], mval[1], &x, NULL);
868                 if (x < CFRA)
869                         RNA_int_set(op->ptr, "left_right", GRAPHKEYS_LRSEL_LEFT);
870                 else    
871                         RNA_int_set(op->ptr, "left_right", GRAPHKEYS_LRSEL_RIGHT);
872                 
873                 graphkeys_select_leftright(&ac, RNA_enum_get(op->ptr, "left_right"), selectmode);
874         }
875         else if (RNA_boolean_get(op->ptr, "column")) {
876                 /* select all the keyframes that occur on the same frame as where the mouse clicked */
877                 float x;
878                 
879                 /* figure out where (the frame) the mouse clicked, and set all keyframes in that frame */
880                 UI_view2d_region_to_view(v2d, mval[0], mval[1], &x, NULL);
881                 mouse_columnselect_graph_keys(&ac, x);
882         }
883         else {
884                 /* select keyframe under mouse */
885                 mouse_graph_keys(&ac, mval, selectmode); // xxx curves only should become an arg
886         }
887         
888         /* set notifier tha things have changed */
889         ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_BOTH);
890         
891         /* for tweak grab to work */
892         return OPERATOR_FINISHED|OPERATOR_PASS_THROUGH;
893 }
894  
895 void GRAPHEDIT_OT_keyframes_clickselect (wmOperatorType *ot)
896 {
897         /* identifiers */
898         ot->name= "Mouse Select Keys";
899         ot->idname= "GRAPHEDIT_OT_keyframes_clickselect";
900         
901         /* api callbacks */
902         ot->invoke= graphkeys_clickselect_invoke;
903         ot->poll= ED_operator_areaactive;
904         
905         /* id-props */
906         // XXX should we make this into separate operators?
907         RNA_def_enum(ot->srna, "left_right", NULL /* XXX prop_graphkeys_clickselect_items */, 0, "Left Right", ""); // ALTKEY
908         RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", ""); // SHIFTKEY
909         RNA_def_boolean(ot->srna, "column", 0, "Column Select", ""); // CTRLKEY
910 }
911
912 /* ************************************************************************** */