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