Durian Feature Request for Graph Editor: Border Select (optionally) considers handles
[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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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_math.h"
40
41 #include "DNA_anim_types.h"
42 #include "DNA_object_types.h"
43 #include "DNA_screen_types.h"
44 #include "DNA_scene_types.h"
45 #include "DNA_space_types.h"
46
47 #include "RNA_access.h"
48 #include "RNA_define.h"
49
50 #include "BKE_action.h"
51 #include "BKE_depsgraph.h"
52 #include "BKE_fcurve.h"
53 #include "BKE_key.h"
54 #include "BKE_material.h"
55 #include "BKE_nla.h"
56 #include "BKE_object.h"
57 #include "BKE_context.h"
58 #include "BKE_utildefines.h"
59
60 #include "UI_view2d.h"
61
62 #include "ED_anim_api.h"
63 #include "ED_keyframes_edit.h"
64 #include "ED_markers.h"
65
66 #include "WM_api.h"
67 #include "WM_types.h"
68
69 #include "graph_intern.h"
70
71
72 /* ************************************************************************** */
73 /* KEYFRAMES STUFF */
74
75 /* ******************** Deselect All Operator ***************************** */
76 /* This operator works in one of three ways:
77  *      1) (de)select all (AKEY) - test if select all or deselect all
78  *      2) invert all (CTRL-IKEY) - invert selection of all keyframes
79  *      3) (de)select all - no testing is done; only for use internal tools as normal function...
80  */
81
82 /* Deselects keyframes in the Graph Editor
83  *      - This is called by the deselect all operator, as well as other ones!
84  *
85  *      - test: check if select or deselect all
86  *      - sel: how to select keyframes 
87  *              0 = deselect
88  *              1 = select
89  *              2 = invert
90  */
91 static void deselect_graph_keys (bAnimContext *ac, short test, short sel)
92 {
93         ListBase anim_data = {NULL, NULL};
94         bAnimListElem *ale;
95         int filter;
96         
97         SpaceIpo *sipo= (SpaceIpo *)ac->sa->spacedata.first;
98         KeyframeEditData ked;
99         KeyframeEditFunc test_cb, sel_cb;
100         
101         /* determine type-based settings */
102         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_CURVESONLY);
103         
104         /* filter data */
105         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
106         
107         /* init BezTriple looping data */
108         memset(&ked, 0, sizeof(KeyframeEditData));
109         test_cb= ANIM_editkeyframes_ok(BEZT_OK_SELECTED);
110         
111         /* See if we should be selecting or deselecting */
112         if (test) {
113                 for (ale= anim_data.first; ale; ale= ale->next) {
114                         if (ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, test_cb, NULL)) {
115                                 sel= SELECT_SUBTRACT;
116                                 break;
117                         }
118                 }
119         }
120         
121         /* convert sel to selectmode, and use that to get editor */
122         sel_cb= ANIM_editkeyframes_select(sel);
123         
124         /* Now set the flags */
125         for (ale= anim_data.first; ale; ale= ale->next) {
126                 FCurve *fcu= (FCurve *)ale->key_data;
127                 
128                 /* Keyframes First */
129                 ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, sel_cb, NULL);
130                 
131                 /* only change selection of channel when the visibility of keyframes doesn't depend on this */
132                 if ((sipo->flag & SIPO_SELCUVERTSONLY) == 0) {
133                         /* deactivate the F-Curve, and deselect if deselecting keyframes.
134                          * otherwise select the F-Curve too since we've selected all the keyframes
135                          */
136                         if (sel == SELECT_SUBTRACT) 
137                                 fcu->flag &= ~FCURVE_SELECTED;
138                         else
139                                 fcu->flag |= FCURVE_SELECTED;
140                 }
141                 
142                 /* always deactivate all F-Curves if we perform batch ops for selection */
143                 fcu->flag &= ~FCURVE_ACTIVE;
144         }
145         
146         /* Cleanup */
147         BLI_freelistN(&anim_data);
148 }
149
150 /* ------------------- */
151
152 static int graphkeys_deselectall_exec(bContext *C, wmOperator *op)
153 {
154         bAnimContext ac;
155         
156         /* get editor data */
157         if (ANIM_animdata_get_context(C, &ac) == 0)
158                 return OPERATOR_CANCELLED;
159                 
160         /* 'standard' behaviour - check if selected, then apply relevant selection */
161         if (RNA_boolean_get(op->ptr, "invert"))
162                 deselect_graph_keys(&ac, 0, SELECT_INVERT);
163         else
164                 deselect_graph_keys(&ac, 1, SELECT_ADD);
165         
166         /* set notifier that things have changed */
167         WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_SELECT, NULL);
168         
169         return OPERATOR_FINISHED;
170 }
171  
172 void GRAPH_OT_select_all_toggle (wmOperatorType *ot)
173 {
174         /* identifiers */
175         ot->name= "Select All";
176         ot->idname= "GRAPH_OT_select_all_toggle";
177         ot->description= "Toggle selection of all keyframes";
178         
179         /* api callbacks */
180         ot->exec= graphkeys_deselectall_exec;
181         ot->poll= graphop_visible_keyframes_poll;
182         
183         /* flags */
184         ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/;
185         
186         /* props */
187         ot->prop= RNA_def_boolean(ot->srna, "invert", 0, "Invert", "");
188 }
189
190 /* ******************** Border Select Operator **************************** */
191 /* This operator currently works in one of three ways:
192  *      -> BKEY         - 1) all keyframes within region are selected (validation with BEZT_OK_REGION)
193  *      -> ALT-BKEY - depending on which axis of the region was larger...
194  *              -> 2) x-axis, so select all frames within frame range (validation with BEZT_OK_FRAMERANGE)
195  *              -> 3) y-axis, so select all frames within channels that region included (validation with BEZT_OK_VALUERANGE)
196  */
197
198 /* Borderselect only selects keyframes now, as overshooting handles often get caught too,
199  * which means that they may be inadvertantly moved as well. However, incl_handles overrides
200  * this, and allow handles to be considered independently too.
201  * Also, for convenience, handles should get same status as keyframe (if it was within bounds).
202  */
203 static void borderselect_graphkeys (bAnimContext *ac, rcti rect, short mode, short selectmode, short incl_handles)
204 {
205         ListBase anim_data = {NULL, NULL};
206         bAnimListElem *ale;
207         int filter;
208         
209         SpaceIpo *sipo= (SpaceIpo *)ac->sa->spacedata.first;
210         KeyframeEditData ked;
211         KeyframeEditFunc ok_cb, select_cb;
212         View2D *v2d= &ac->ar->v2d;
213         rctf rectf;
214         
215         /* convert mouse coordinates to frame ranges and channel coordinates corrected for view pan/zoom */
216         UI_view2d_region_to_view(v2d, rect.xmin, rect.ymin, &rectf.xmin, &rectf.ymin);
217         UI_view2d_region_to_view(v2d, rect.xmax, rect.ymax, &rectf.xmax, &rectf.ymax);
218         
219         /* filter data */
220         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVESONLY | ANIMFILTER_CURVEVISIBLE);
221         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
222         
223         /* get beztriple editing/validation funcs  */
224         select_cb= ANIM_editkeyframes_select(selectmode);
225         ok_cb= ANIM_editkeyframes_ok(mode);
226         
227         /* init editing data */
228         memset(&ked, 0, sizeof(KeyframeEditData));
229         ked.data= &rectf;
230         
231         /* treat handles separately? */
232         if (incl_handles)
233                 ked.iterflags |= KEYFRAME_ITER_INCL_HANDLES;
234         
235         /* loop over data, doing border select */
236         for (ale= anim_data.first; ale; ale= ale->next) {
237                 AnimData *adt= ANIM_nla_mapping_get(ac, ale);
238                 FCurve *fcu= (FCurve *)ale->key_data;
239                 
240                 /* apply unit corrections */
241                 ANIM_unit_mapping_apply_fcurve(ac->scene, ale->id, ale->key_data, ANIM_UNITCONV_ONLYKEYS);
242                 
243                 /* apply NLA mapping to all the keyframes, since it's easier than trying to
244                  * guess when a callback might use something different
245                  */
246                 if (adt)
247                         ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1);
248                 
249                 /* set horizontal range (if applicable) 
250                  * NOTE: these values are only used for x-range and y-range but not region 
251                  *              (which uses ked.data, i.e. rectf)
252                  */
253                 if (mode != BEZT_OK_VALUERANGE) {
254                         ked.f1= rectf.xmin;
255                         ked.f2= rectf.xmax;
256                 }
257                 else {
258                         ked.f1= rectf.ymin;
259                         ked.f2= rectf.ymax;
260                 }
261                 
262                 /* firstly, check if any keyframes will be hit by this */
263                 if (ANIM_fcurve_keyframes_loop(&ked, fcu, NULL, ok_cb, NULL)) {
264                         /* select keyframes that are in the appropriate places */
265                         ANIM_fcurve_keyframes_loop(&ked, fcu, ok_cb, select_cb, NULL);
266                         
267                         /* only change selection of channel when the visibility of keyframes doesn't depend on this */
268                         if ((sipo->flag & SIPO_SELCUVERTSONLY) == 0) {
269                                 /* select the curve too now that curve will be touched */
270                                 if (selectmode == SELECT_ADD)
271                                         fcu->flag |= FCURVE_SELECTED;
272                         }
273                 }
274                 
275                 /* un-apply NLA mapping from all the keyframes */
276                 if (adt)
277                         ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
278                         
279                 /* unapply unit corrections */
280                 ANIM_unit_mapping_apply_fcurve(ac->scene, ale->id, ale->key_data, ANIM_UNITCONV_RESTORE|ANIM_UNITCONV_ONLYKEYS);
281         }
282         
283         /* cleanup */
284         BLI_freelistN(&anim_data);
285 }
286
287 /* ------------------- */
288
289 static int graphkeys_borderselect_exec(bContext *C, wmOperator *op)
290 {
291         bAnimContext ac;
292         rcti rect;
293         short mode=0, selectmode=0;
294         short incl_handles;
295         
296         /* get editor data */
297         if (ANIM_animdata_get_context(C, &ac) == 0)
298                 return OPERATOR_CANCELLED;
299         
300         /* get select mode 
301          *      - 'gesture_mode' from the operator specifies how to select
302          *      - 'include_handles' from the operator specifies whether to include handles in the selection
303          */
304         if (RNA_int_get(op->ptr, "gesture_mode")==GESTURE_MODAL_SELECT)
305                 selectmode= SELECT_ADD;
306         else
307                 selectmode= SELECT_SUBTRACT;
308                 
309         incl_handles = RNA_boolean_get(op->ptr, "include_handles");
310         
311         /* get settings from operator */
312         rect.xmin= RNA_int_get(op->ptr, "xmin");
313         rect.ymin= RNA_int_get(op->ptr, "ymin");
314         rect.xmax= RNA_int_get(op->ptr, "xmax");
315         rect.ymax= RNA_int_get(op->ptr, "ymax");
316         
317         /* selection 'mode' depends on whether borderselect region only matters on one axis */
318         if (RNA_boolean_get(op->ptr, "axis_range")) {
319                 /* mode depends on which axis of the range is larger to determine which axis to use 
320                  *      - checking this in region-space is fine, as it's fundamentally still going to be a different rect size
321                  *      - the frame-range select option is favoured over the channel one (x over y), as frame-range one is often
322                  *        used for tweaking timing when "blocking", while channels is not that useful...
323                  */
324                 if ((rect.xmax - rect.xmin) >= (rect.ymax - rect.ymin))
325                         mode= BEZT_OK_FRAMERANGE;
326                 else
327                         mode= BEZT_OK_VALUERANGE;
328         }
329         else 
330                 mode= BEZT_OK_REGION;
331         
332         /* apply borderselect action */
333         borderselect_graphkeys(&ac, rect, mode, selectmode, incl_handles);
334         
335         /* send notifier that keyframe selection has changed */
336         WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_SELECT, NULL);
337         
338         return OPERATOR_FINISHED;
339
340
341 void GRAPH_OT_select_border(wmOperatorType *ot)
342 {
343         /* identifiers */
344         ot->name= "Border Select";
345         ot->idname= "GRAPH_OT_select_border";
346         ot->description= "Select all keyframes within the specified region";
347         
348         /* api callbacks */
349         ot->invoke= WM_border_select_invoke;
350         ot->exec= graphkeys_borderselect_exec;
351         ot->modal= WM_border_select_modal;
352         
353         ot->poll= graphop_visible_keyframes_poll;
354         
355         /* flags */
356         ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/;
357         
358         /* rna */
359         WM_operator_properties_gesture_border(ot, FALSE);
360         
361         ot->prop= RNA_def_boolean(ot->srna, "axis_range", 0, "Axis Range", "");
362         RNA_def_boolean(ot->srna, "include_handles", 0, "Include Handles", "Are handles tested individually against the selection criteria");
363 }
364
365 /* ******************** Column Select Operator **************************** */
366 /* This operator works in one of four ways:
367  *      - 1) select all keyframes in the same frame as a selected one  (KKEY)
368  *      - 2) select all keyframes in the same frame as the current frame marker (CTRL-KKEY)
369  *      - 3) select all keyframes in the same frame as a selected markers (SHIFT-KKEY)
370  *      - 4) select all keyframes that occur between selected markers (ALT-KKEY)
371  */
372
373 /* defines for column-select mode */
374 static EnumPropertyItem prop_column_select_types[] = {
375         {GRAPHKEYS_COLUMNSEL_KEYS, "KEYS", 0, "On Selected Keyframes", ""},
376         {GRAPHKEYS_COLUMNSEL_CFRA, "CFRA", 0, "On Current Frame", ""},
377         {GRAPHKEYS_COLUMNSEL_MARKERS_COLUMN, "MARKERS_COLUMN", 0, "On Selected Markers", ""},
378         {GRAPHKEYS_COLUMNSEL_MARKERS_BETWEEN, "MARKERS_BETWEEN", 0, "Between Min/Max Selected Markers", ""},
379         {0, NULL, 0, NULL, NULL}
380 };
381
382 /* ------------------- */ 
383
384 /* Selects all visible keyframes between the specified markers */
385 static void markers_selectkeys_between (bAnimContext *ac)
386 {
387         ListBase anim_data = {NULL, NULL};
388         bAnimListElem *ale;
389         int filter;
390         
391         KeyframeEditFunc ok_cb, select_cb;
392         KeyframeEditData ked;
393         float min, max;
394         
395         /* get extreme markers */
396         ED_markers_get_minmax(ac->markers, 1, &min, &max);
397         min -= 0.5f;
398         max += 0.5f;
399         
400         /* get editing funcs + data */
401         ok_cb= ANIM_editkeyframes_ok(BEZT_OK_FRAMERANGE);
402         select_cb= ANIM_editkeyframes_select(SELECT_ADD);
403         
404         memset(&ked, 0, sizeof(KeyframeEditData));
405         ked.f1= min; 
406         ked.f2= max;
407         
408         /* filter data */
409         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_CURVESONLY);
410         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
411         
412         /* select keys in-between */
413         for (ale= anim_data.first; ale; ale= ale->next) {
414                 AnimData *adt= ANIM_nla_mapping_get(ac, ale);
415                 
416                 if (adt) {      
417                         ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1);
418                         ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
419                         ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
420                 }
421                 else {
422                         ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
423                 }
424         }
425         
426         /* Cleanup */
427         BLI_freelistN(&anim_data);
428 }
429
430
431 /* Selects all visible keyframes in the same frames as the specified elements */
432 static void columnselect_graph_keys (bAnimContext *ac, short mode)
433 {
434         ListBase anim_data= {NULL, NULL};
435         bAnimListElem *ale;
436         int filter;
437         
438         Scene *scene= ac->scene;
439         CfraElem *ce;
440         KeyframeEditFunc select_cb, ok_cb;
441         KeyframeEditData ked;
442         
443         /* initialise keyframe editing data */
444         memset(&ked, 0, sizeof(KeyframeEditData));
445         
446         /* build list of columns */
447         switch (mode) {
448                 case GRAPHKEYS_COLUMNSEL_KEYS: /* list of selected keys */
449                         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_CURVESONLY);
450                         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
451                         
452                         for (ale= anim_data.first; ale; ale= ale->next)
453                                 ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, bezt_to_cfraelem, NULL);
454                         
455                         BLI_freelistN(&anim_data);
456                         break;
457                         
458                 case GRAPHKEYS_COLUMNSEL_CFRA: /* current frame */
459                         /* make a single CfraElem for storing this */
460                         ce= MEM_callocN(sizeof(CfraElem), "cfraElem");
461                         BLI_addtail(&ked.list, ce);
462                         
463                         ce->cfra= (float)CFRA;
464                         break;
465                         
466                 case GRAPHKEYS_COLUMNSEL_MARKERS_COLUMN: /* list of selected markers */
467                         ED_markers_make_cfra_list(ac->markers, &ked.list, 1);
468                         break;
469                         
470                 default: /* invalid option */
471                         return;
472         }
473         
474         /* set up BezTriple edit callbacks */
475         select_cb= ANIM_editkeyframes_select(SELECT_ADD);
476         ok_cb= ANIM_editkeyframes_ok(BEZT_OK_FRAME);
477         
478         /* loop through all of the keys and select additional keyframes
479          * based on the keys found to be selected above
480          */
481         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_CURVESONLY);
482         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
483         
484         for (ale= anim_data.first; ale; ale= ale->next) {
485                 AnimData *adt= ANIM_nla_mapping_get(ac, ale);
486                 
487                 /* loop over cfraelems (stored in the KeyframeEditData->list)
488                  *      - we need to do this here, as we can apply fewer NLA-mapping conversions
489                  */
490                 for (ce= ked.list.first; ce; ce= ce->next) {
491                         /* set frame for validation callback to refer to */
492                         if (ale)
493                                 ked.f1= BKE_nla_tweakedit_remap(adt, ce->cfra, NLATIME_CONVERT_UNMAP);
494                         else
495                                 ked.f1= ce->cfra;
496                         
497                         /* select elements with frame number matching cfraelem */
498                         ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
499                 }
500         }
501         
502         /* free elements */
503         BLI_freelistN(&ked.list);
504         BLI_freelistN(&anim_data);
505 }
506
507 /* ------------------- */
508
509 static int graphkeys_columnselect_exec(bContext *C, wmOperator *op)
510 {
511         bAnimContext ac;
512         short mode;
513         
514         /* get editor data */
515         if (ANIM_animdata_get_context(C, &ac) == 0)
516                 return OPERATOR_CANCELLED;
517                 
518         /* action to take depends on the mode */
519         mode= RNA_enum_get(op->ptr, "mode");
520         
521         if (mode == GRAPHKEYS_COLUMNSEL_MARKERS_BETWEEN)
522                 markers_selectkeys_between(&ac);
523         else
524                 columnselect_graph_keys(&ac, mode);
525         
526         /* set notifier that keyframe selection has changed */
527         WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_SELECT, NULL);
528         
529         return OPERATOR_FINISHED;
530 }
531  
532 void GRAPH_OT_select_column (wmOperatorType *ot)
533 {
534         /* identifiers */
535         ot->name= "Select All";
536         ot->idname= "GRAPH_OT_select_column";
537         ot->description= "Select all keyframes on the specified frame(s)";
538         
539         /* api callbacks */
540         ot->exec= graphkeys_columnselect_exec;
541         ot->poll= graphop_visible_keyframes_poll;
542         
543         /* flags */
544         ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/;
545         
546         /* props */
547         ot->prop= RNA_def_enum(ot->srna, "mode", prop_column_select_types, 0, "Mode", "");
548 }
549
550 /* ******************** Select More/Less Operators *********************** */
551
552 /* Common code to perform selection */
553 static void select_moreless_graph_keys (bAnimContext *ac, short mode)
554 {
555         ListBase anim_data= {NULL, NULL};
556         bAnimListElem *ale;
557         int filter;
558         
559         KeyframeEditData ked;
560         KeyframeEditFunc build_cb;
561         
562         
563         /* init selmap building data */
564         build_cb= ANIM_editkeyframes_buildselmap(mode);
565         memset(&ked, 0, sizeof(KeyframeEditData)); 
566         
567         /* loop through all of the keys and select additional keyframes based on these */
568         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_CURVESONLY);
569         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
570         
571         for (ale= anim_data.first; ale; ale= ale->next) {
572                 FCurve *fcu= (FCurve *)ale->key_data;
573                 
574                 /* only continue if F-Curve has keyframes */
575                 if (fcu->bezt == NULL)
576                         continue;
577                 
578                 /* build up map of whether F-Curve's keyframes should be selected or not */
579                 ked.data= MEM_callocN(fcu->totvert, "selmap graphEdit");
580                 ANIM_fcurve_keyframes_loop(&ked, fcu, NULL, build_cb, NULL);
581                 
582                 /* based on this map, adjust the selection status of the keyframes */
583                 ANIM_fcurve_keyframes_loop(&ked, fcu, NULL, bezt_selmap_flush, NULL);
584                 
585                 /* free the selmap used here */
586                 MEM_freeN(ked.data);
587                 ked.data= NULL;
588         }
589         
590         /* Cleanup */
591         BLI_freelistN(&anim_data);
592 }
593
594 /* ----------------- */
595
596 static int graphkeys_select_more_exec (bContext *C, wmOperator *op)
597 {
598         bAnimContext ac;
599         
600         /* get editor data */
601         if (ANIM_animdata_get_context(C, &ac) == 0)
602                 return OPERATOR_CANCELLED;
603         
604         /* perform select changes */
605         select_moreless_graph_keys(&ac, SELMAP_MORE);
606         
607         /* set notifier that keyframe selection has changed */
608         WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_SELECT, NULL);
609         
610         return OPERATOR_FINISHED;
611 }
612
613 void GRAPH_OT_select_more (wmOperatorType *ot)
614 {
615         /* identifiers */
616         ot->name = "Select More";
617         ot->idname= "GRAPH_OT_select_more";
618         ot->description = "Select keyframes beside already selected ones";
619         
620         /* api callbacks */
621         ot->exec= graphkeys_select_more_exec;
622         ot->poll= graphop_visible_keyframes_poll;
623         
624         /* flags */
625         ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/;
626 }
627
628 /* ----------------- */
629
630 static int graphkeys_select_less_exec (bContext *C, wmOperator *op)
631 {
632         bAnimContext ac;
633         
634         /* get editor data */
635         if (ANIM_animdata_get_context(C, &ac) == 0)
636                 return OPERATOR_CANCELLED;
637         
638         /* perform select changes */
639         select_moreless_graph_keys(&ac, SELMAP_LESS);
640         
641         /* set notifier that keyframe selection has changed */
642         WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_SELECT, NULL);
643         
644         return OPERATOR_FINISHED;
645 }
646
647 void GRAPH_OT_select_less (wmOperatorType *ot)
648 {
649         /* identifiers */
650         ot->name = "Select Less";
651         ot->idname= "GRAPH_OT_select_less";
652         ot->description = "Deselect keyframes on ends of selection islands";
653         
654         /* api callbacks */
655         ot->exec= graphkeys_select_less_exec;
656         ot->poll= graphop_visible_keyframes_poll;
657         
658         /* flags */
659         ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/;
660 }
661
662 /* ******************** Mouse-Click Select Operator *********************** */
663 /* This operator works in one of three ways:
664  *      - 1) keyframe under mouse - no special modifiers
665  *      - 2) all keyframes on the same side of current frame indicator as mouse - ALT modifier
666  *      - 3) column select all keyframes in frame under mouse - CTRL modifier
667  *
668  * In addition to these basic options, the SHIFT modifier can be used to toggle the 
669  * selection mode between replacing the selection (without) and inverting the selection (with).
670  */
671
672 /* defines for left-right select tool */
673 static EnumPropertyItem prop_graphkeys_leftright_select_types[] = {
674         {GRAPHKEYS_LRSEL_TEST, "CHECK", 0, "Check if Select Left or Right", ""},
675         {GRAPHKEYS_LRSEL_NONE, "OFF", 0, "Don't select", ""},
676         {GRAPHKEYS_LRSEL_LEFT, "LEFT", 0, "Before current frame", ""},
677         {GRAPHKEYS_LRSEL_RIGHT, "RIGHT", 0, "After current frame", ""},
678         {0, NULL, 0, NULL, NULL}
679 };
680
681 /* ------------------- */
682
683 /* temp info for caching handle vertices close */
684 typedef struct tNearestVertInfo {
685         struct tNearestVertInfo *next, *prev;
686         
687         FCurve *fcu;            /* F-Curve that keyframe comes from */
688         
689         BezTriple *bezt;        /* keyframe to consider */
690         FPoint *fpt;            /* sample point to consider */
691         
692         short hpoint;           /* the handle index that we hit (eHandleIndex) */
693         short sel;                      /* whether the handle is selected or not */
694         int dist;                       /* distance from mouse to vert */
695 } tNearestVertInfo;
696
697 /* Tags for the type of graph vert that we have */
698 typedef enum eGraphVertIndex {
699         NEAREST_HANDLE_LEFT     = -1,
700         NEAREST_HANDLE_KEY,
701         NEAREST_HANDLE_RIGHT
702 } eGraphVertIndex; 
703
704 /* Tolerance for absolute radius (in pixels) of the vert from the cursor to use */
705 // TODO: perhaps this should depend a bit on the size that the user set the vertices to be?
706 #define GVERTSEL_TOL    10
707
708 /* ....... */
709
710 /* check if its ok to select a handle */
711 // XXX also need to check for int-values only?
712 static int fcurve_handle_sel_check(SpaceIpo *sipo, BezTriple *bezt)
713 {
714         if (sipo->flag & SIPO_NOHANDLES) return 0;
715         if ((sipo->flag & SIPO_SELVHANDLESONLY) && BEZSELECTED(bezt)==0) return 0;
716         return 1;
717 }
718
719 /* check if the given vertex is within bounds or not */
720 // TODO: should we return if we hit something?
721 static void nearest_fcurve_vert_store (ListBase *matches, View2D *v2d, FCurve *fcu, BezTriple *bezt, FPoint *fpt, short hpoint, int mval[2])
722 {
723         /* Keyframes or Samples? */
724         if (bezt) {
725                 int screen_co[2], dist;
726                 
727                 /* convert from data-space to screen coordinates 
728                  * NOTE: hpoint+1 gives us 0,1,2 respectively for each handle, 
729                  *      needed to access the relevant vertex coordinates in the 3x3 
730                  *      'vec' matrix
731                  */
732                 UI_view2d_to_region_no_clip(v2d, bezt->vec[hpoint+1][0], bezt->vec[hpoint+1][1], &screen_co[0], &screen_co[1]);
733                 
734                 /* check if distance from mouse cursor to vert in screen space is within tolerance */
735                         // XXX: inlined distance calculation, since we cannot do this on ints using the math lib...
736                 //dist = len_v2v2(mval, screen_co);
737                 dist = sqrt((mval[0] - screen_co[0])*(mval[0] - screen_co[0]) + 
738                                         (mval[1] - screen_co[1])*(mval[1] - screen_co[1]));
739                 
740                 if (dist <= GVERTSEL_TOL) {
741                         tNearestVertInfo *nvi = (tNearestVertInfo *)matches->last;
742                         short replace = 0;
743                         
744                         /* if there is already a point for the F-Curve, check if this point is closer than that was */
745                         if ((nvi) && (nvi->fcu == fcu)) {
746                                 /* replace if we are closer, or if equal and that one wasn't selected but we are... */
747                                 if ( (nvi->dist > dist) || ((nvi->sel == 0) && BEZSELECTED(bezt)) )
748                                         replace= 1;
749                         }
750                         /* add new if not replacing... */
751                         if (replace == 0)
752                                 nvi = MEM_callocN(sizeof(tNearestVertInfo), "Nearest Graph Vert Info - Bezt");
753                         
754                         /* store values */
755                         nvi->fcu = fcu;
756                         nvi->bezt = bezt;
757                         nvi->hpoint = hpoint;
758                         nvi->dist = dist;
759                         
760                         nvi->sel= BEZSELECTED(bezt); // XXX... should this use the individual verts instead?
761                         
762                         /* add to list of matches if appropriate... */
763                         if (replace == 0)
764                                 BLI_addtail(matches, nvi);
765                 }
766         }
767         else if (fpt) {
768                 // TODO...
769         }
770
771
772 /* helper for find_nearest_fcurve_vert() - build the list of nearest matches */
773 static void get_nearest_fcurve_verts_list (bAnimContext *ac, int mval[2], ListBase *matches)
774 {
775         ListBase anim_data = {NULL, NULL};
776         bAnimListElem *ale;
777         int filter;
778         
779         SpaceIpo *sipo= (SpaceIpo *)ac->sa->spacedata.first;
780         View2D *v2d= &ac->ar->v2d;
781         
782         /* get curves to search through 
783          *      - if the option to only show keyframes that belong to selected F-Curves is enabled,
784          *        include the 'only selected' flag...
785          */
786         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_CURVESONLY);
787         if (sipo->flag & SIPO_SELCUVERTSONLY)   // FIXME: this should really be check for by the filtering code...
788                 filter |= ANIMFILTER_SEL;
789         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
790         
791         for (ale= anim_data.first; ale; ale= ale->next) {
792                 FCurve *fcu= (FCurve *)ale->key_data;
793                 AnimData *adt= ANIM_nla_mapping_get(ac, ale);
794                 
795                 /* apply unit corrections */
796                 ANIM_unit_mapping_apply_fcurve(ac->scene, ale->id, ale->key_data, 0);
797                 
798                 /* apply NLA mapping to all the keyframes */
799                 if (adt)
800                         ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1);
801                 
802                 if (fcu->bezt) {
803                         BezTriple *bezt1=fcu->bezt, *prevbezt=NULL;
804                         int i;
805                         
806                         for (i=0; i < fcu->totvert; i++, prevbezt=bezt1, bezt1++) {
807                                 /* keyframe */
808                                 nearest_fcurve_vert_store(matches, v2d, fcu, bezt1, NULL, NEAREST_HANDLE_KEY, mval);
809                                 
810                                 /* handles - only do them if they're visible */
811                                 if (fcurve_handle_sel_check(sipo, bezt1) && (fcu->totvert > 1)) {
812                                         /* first handle only visible if previous segment had handles */
813                                         if ( (!prevbezt && (bezt1->ipo==BEZT_IPO_BEZ)) || (prevbezt && (prevbezt->ipo==BEZT_IPO_BEZ)) )
814                                         {
815                                                 nearest_fcurve_vert_store(matches, v2d, fcu, bezt1, NULL, NEAREST_HANDLE_LEFT, mval);
816                                         }
817                                         
818                                         /* second handle only visible if this segment is bezier */
819                                         if (bezt1->ipo == BEZT_IPO_BEZ) 
820                                         {
821                                                 nearest_fcurve_vert_store(matches, v2d, fcu, bezt1, NULL, NEAREST_HANDLE_RIGHT, mval);
822                                         }
823                                 }
824                         }
825                 }
826                 else if (fcu->fpt) {
827                         // TODO; do this for samples too
828                         
829                 }
830                 
831                 /* un-apply NLA mapping from all the keyframes */
832                 if (adt)
833                         ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
834                 
835                 /* unapply unit corrections */
836                 ANIM_unit_mapping_apply_fcurve(ac->scene, ale->id, ale->key_data, ANIM_UNITCONV_RESTORE);
837         }
838         
839         /* free channels */
840         BLI_freelistN(&anim_data);
841 }
842
843 /* helper for find_nearest_fcurve_vert() - get the best match to use */
844 static tNearestVertInfo *get_best_nearest_fcurve_vert (bAnimContext *ac, ListBase *matches)
845 {
846         tNearestVertInfo *nvi = NULL;
847         short found = 0;
848         
849         /* abort if list is empty */
850         if (matches->first == NULL) 
851                 return NULL;
852                 
853         /* if list only has 1 item, remove it from the list and return */
854         if (matches->first == matches->last) {
855                 /* need to remove from the list, otherwise it gets freed and then we can't return it */
856                 nvi= matches->first;
857                 BLI_remlink(matches, nvi);
858                 
859                 return nvi;
860         }
861         
862         /* try to find the first selected F-Curve vert, then take the one after it */
863         for (nvi = matches->first; nvi; nvi = nvi->next) {
864                 /* which mode of search are we in: find first selected, or find vert? */
865                 if (found) {
866                         /* just take this vert now that we've found the selected one 
867                          *      - we'll need to remove this from the list so that it can be returned to the original caller
868                          */
869                         BLI_remlink(matches, nvi);
870                         return nvi;
871                 }
872                 else {
873                         /* if vert is selected, we've got what we want... */
874                         if (nvi->sel)
875                                 found= 1;
876                 }
877         }
878         
879         /* if we're still here, this means that we failed to find anything appropriate in the first pass,
880          * so just take the first item now...
881          */
882         nvi = matches->first;
883         BLI_remlink(matches, nvi);
884         return nvi;
885 }
886
887 /* Find the nearest vertices (either a handle or the keyframe) that are nearest to the mouse cursor (in area coordinates) 
888  * NOTE: the match info found must still be freed 
889  */
890 static tNearestVertInfo *find_nearest_fcurve_vert (bAnimContext *ac, int mval[2])
891 {
892         ListBase matches = {NULL, NULL};
893         tNearestVertInfo *nvi;
894         
895         /* step 1: get the nearest verts */
896         get_nearest_fcurve_verts_list(ac, mval, &matches);
897         
898         /* step 2: find the best vert */
899         nvi= get_best_nearest_fcurve_vert(ac, &matches);
900         
901         BLI_freelistN(&matches);
902         
903         /* return the best vert found */
904         return nvi;
905 }
906
907 /* ------------------- */
908
909 /* option 1) select keyframe directly under mouse */
910 static void mouse_graph_keys (bAnimContext *ac, int mval[], short select_mode, short curves_only)
911 {
912         SpaceIpo *sipo= (SpaceIpo *)ac->sa->spacedata.first;
913         tNearestVertInfo *nvi;
914         
915         /* find the beztriple that we're selecting, and the handle that was clicked on */
916         nvi = find_nearest_fcurve_vert(ac, mval);
917         
918         /* check if anything to select */
919         if (nvi == NULL)        
920                 return;
921         
922         /* deselect all other curves? */
923         if (select_mode == SELECT_REPLACE) {
924                 /* reset selection mode */
925                 select_mode= SELECT_ADD;
926                 
927                 /* deselect all other keyframes */
928                 deselect_graph_keys(ac, 0, SELECT_SUBTRACT);
929                 
930                 /* deselect other channels too, but only only do this if 
931                  * selection of channel when the visibility of keyframes 
932                  * doesn't depend on this 
933                  */
934                 if ((sipo->flag & SIPO_SELCUVERTSONLY) == 0)
935                         ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
936         }
937         
938         /* if points can be selected on this F-Curve */
939         // TODO: what about those with no keyframes?
940         if ((curves_only == 0) && ((nvi->fcu->flag & FCURVE_PROTECTED)==0)) {
941                 /* only if there's keyframe */
942                 if (nvi->bezt) {
943                         BezTriple *bezt= nvi->bezt;
944                         
945                         /* depends on selection mode */
946                         if (select_mode == SELECT_INVERT) {
947                                 /* keyframe - invert select of all */
948                                 if (nvi->hpoint == NEAREST_HANDLE_KEY) {
949                                         if (BEZSELECTED(bezt)) {
950                                                 BEZ_DESEL(bezt);
951                                         }
952                                         else {
953                                                 BEZ_SEL(bezt);
954                                         }
955                                 }
956                                 
957                                 /* handles - toggle selection of relevant handle */
958                                 else if (nvi->hpoint == NEAREST_HANDLE_LEFT) {
959                                         /* toggle selection */
960                                         bezt->f1 ^= SELECT;
961                                 }
962                                 else {
963                                         /* toggle selection */
964                                         bezt->f3 ^= SELECT;
965                                 }
966                         }
967                         else {
968                                 /* if the keyframe was clicked on, select all verts of given beztriple */
969                                 if (nvi->hpoint == NEAREST_HANDLE_KEY) {
970                                         BEZ_SEL(bezt);
971                                 }
972                                 /* otherwise, select the handle that applied */
973                                 else if (nvi->hpoint == NEAREST_HANDLE_LEFT) 
974                                         bezt->f1 |= SELECT;
975                                 else 
976                                         bezt->f3 |= SELECT;
977                         }
978                 }
979                 else if (nvi->fpt) {
980                         // TODO: need to handle sample points
981                 }
982         }
983         else {
984                 KeyframeEditFunc select_cb;
985                 KeyframeEditData ked;
986                 
987                 /* initialise keyframe editing data */
988                 memset(&ked, 0, sizeof(KeyframeEditData));
989                 
990                 /* set up BezTriple edit callbacks */
991                 select_cb= ANIM_editkeyframes_select(select_mode);
992                 
993                 /* select all keyframes */
994                 ANIM_fcurve_keyframes_loop(&ked, nvi->fcu, NULL, select_cb, NULL);
995         }
996         
997         /* only change selection of channel when the visibility of keyframes doesn't depend on this */
998         if ((sipo->flag & SIPO_SELCUVERTSONLY) == 0) {
999                 /* select or deselect curve? */
1000                 if (select_mode == SELECT_INVERT)
1001                         nvi->fcu->flag ^= FCURVE_SELECTED;
1002                 else if (select_mode == SELECT_ADD)
1003                         nvi->fcu->flag |= FCURVE_SELECTED;
1004                         
1005                 /* set active F-Curve (NOTE: sync the filter flags with findnearest_fcurve_vert) */
1006                 if (nvi->fcu->flag & FCURVE_SELECTED) {
1007                         int filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_CURVESONLY);
1008                         ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, nvi->fcu, ANIMTYPE_FCURVE);
1009                 }
1010         }
1011         
1012         /* free temp sample data for filtering */
1013         MEM_freeN(nvi);
1014 }
1015
1016 /* Option 2) Selects all the keyframes on either side of the current frame (depends on which side the mouse is on) */
1017 static void graphkeys_mselect_leftright (bAnimContext *ac, short leftright, short select_mode)
1018 {
1019         ListBase anim_data = {NULL, NULL};
1020         bAnimListElem *ale;
1021         int filter;
1022         
1023         SpaceIpo *sipo= (SpaceIpo *)ac->sa->spacedata.first;
1024         KeyframeEditFunc ok_cb, select_cb;
1025         KeyframeEditData ked;
1026         Scene *scene= ac->scene;
1027         
1028         /* if select mode is replace, deselect all keyframes (and channels) first */
1029         if (select_mode==SELECT_REPLACE) {
1030                 /* reset selection mode to add to selection */
1031                 select_mode= SELECT_ADD;
1032                 
1033                 /* deselect all other keyframes */
1034                 deselect_graph_keys(ac, 0, SELECT_SUBTRACT);
1035                 
1036                 /* deselect other channels too, but only only do this if 
1037                  * selection of channel when the visibility of keyframes 
1038                  * doesn't depend on this 
1039                  */
1040                 if ((sipo->flag & SIPO_SELCUVERTSONLY) == 0)
1041                         ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
1042         }
1043         
1044         /* set callbacks and editing data */
1045         ok_cb= ANIM_editkeyframes_ok(BEZT_OK_FRAMERANGE);
1046         select_cb= ANIM_editkeyframes_select(select_mode);
1047         
1048         memset(&ked, 0, sizeof(KeyframeEditFunc));
1049         if (leftright == GRAPHKEYS_LRSEL_LEFT) {
1050                 ked.f1 = MINAFRAMEF;
1051                 ked.f2 = (float)(CFRA + 0.1f);
1052         } 
1053         else {
1054                 ked.f1 = (float)(CFRA - 0.1f);
1055                 ked.f2 = MAXFRAMEF;
1056         }
1057         
1058         /* filter data */
1059         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_CURVESONLY);
1060         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1061                 
1062         /* select keys on the side where most data occurs */
1063         for (ale= anim_data.first; ale; ale= ale->next) {
1064                 AnimData *adt= ANIM_nla_mapping_get(ac, ale);
1065                 
1066                 if (adt) {
1067                         ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1);
1068                         ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
1069                         ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
1070                 }
1071                 else
1072                         ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
1073         }
1074         
1075         /* Cleanup */
1076         BLI_freelistN(&anim_data);
1077 }
1078
1079 /* Option 3) Selects all visible keyframes in the same frame as the mouse click */
1080 static void graphkeys_mselect_column (bAnimContext *ac, int mval[2], short select_mode)
1081 {
1082         ListBase anim_data= {NULL, NULL};
1083         bAnimListElem *ale;
1084         int filter;
1085         
1086         SpaceIpo *sipo= (SpaceIpo *)ac->sa->spacedata.first;
1087         KeyframeEditFunc select_cb, ok_cb;
1088         KeyframeEditData ked;
1089         tNearestVertInfo *nvi;
1090         float selx = (float)ac->scene->r.cfra;
1091         
1092         /* find the beztriple that we're selecting, and the handle that was clicked on */
1093         nvi = find_nearest_fcurve_vert(ac, mval);
1094         
1095         /* check if anything to select */
1096         if (nvi == NULL)        
1097                 return;
1098         
1099         /* get frame number on which elements should be selected */
1100         // TODO: should we restrict to integer frames only?
1101         if (nvi->bezt)
1102                 selx= nvi->bezt->vec[1][0];
1103         else if (nvi->fpt)
1104                 selx= nvi->fpt->vec[0];
1105         
1106         /* if select mode is replace, deselect all keyframes (and channels) first */
1107         if (select_mode==SELECT_REPLACE) {
1108                 /* reset selection mode to add to selection */
1109                 select_mode= SELECT_ADD;
1110                 
1111                 /* deselect all other keyframes */
1112                 deselect_graph_keys(ac, 0, SELECT_SUBTRACT);
1113                 
1114                 /* deselect other channels too, but only only do this if 
1115                  * selection of channel when the visibility of keyframes 
1116                  * doesn't depend on this 
1117                  */
1118                 if ((sipo->flag & SIPO_SELCUVERTSONLY) == 0)
1119                         ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
1120         }
1121         
1122         /* initialise keyframe editing data */
1123         memset(&ked, 0, sizeof(KeyframeEditData));
1124         
1125         /* set up BezTriple edit callbacks */
1126         select_cb= ANIM_editkeyframes_select(select_mode);
1127         ok_cb= ANIM_editkeyframes_ok(BEZT_OK_FRAME);
1128         
1129         /* loop through all of the keys and select additional keyframes
1130          * based on the keys found to be selected above
1131          */
1132         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_CURVESONLY);
1133         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1134         
1135         for (ale= anim_data.first; ale; ale= ale->next) {
1136                 AnimData *adt= ANIM_nla_mapping_get(ac, ale);
1137                 
1138                 /* set frame for validation callback to refer to */
1139                 if (adt)
1140                         ked.f1= BKE_nla_tweakedit_remap(adt, selx, NLATIME_CONVERT_UNMAP);
1141                 else
1142                         ked.f1= selx;
1143                 
1144                 /* select elements with frame number matching cfra */
1145                 ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
1146         }
1147         
1148         /* free elements */
1149         MEM_freeN(nvi);
1150         BLI_freelistN(&ked.list);
1151         BLI_freelistN(&anim_data);
1152 }
1153  
1154 /* ------------------- */
1155
1156 /* handle clicking */
1157 static int graphkeys_clickselect_invoke(bContext *C, wmOperator *op, wmEvent *event)
1158 {
1159         bAnimContext ac;
1160         Scene *scene;
1161         ARegion *ar;
1162         View2D *v2d;
1163         short selectmode;
1164         int mval[2];
1165         
1166         /* get editor data */
1167         if (ANIM_animdata_get_context(C, &ac) == 0)
1168                 return OPERATOR_CANCELLED;
1169         
1170         /* get useful pointers from animation context data */
1171         scene= ac.scene;
1172         ar= ac.ar;
1173         v2d= &ar->v2d;
1174         
1175         /* get mouse coordinates (in region coordinates) */
1176         mval[0]= (event->x - ar->winrct.xmin);
1177         mval[1]= (event->y - ar->winrct.ymin);
1178         
1179         /* select mode is either replace (deselect all, then add) or add/extend */
1180         if (RNA_boolean_get(op->ptr, "extend"))
1181                 selectmode= SELECT_INVERT;
1182         else
1183                 selectmode= SELECT_REPLACE;
1184         
1185         /* figure out action to take */
1186         if (RNA_enum_get(op->ptr, "left_right")) {
1187                 /* select all keys on same side of current frame as mouse */
1188                 float x;
1189                 
1190                 UI_view2d_region_to_view(v2d, mval[0], mval[1], &x, NULL);
1191                 if (x < CFRA)
1192                         RNA_int_set(op->ptr, "left_right", GRAPHKEYS_LRSEL_LEFT);
1193                 else    
1194                         RNA_int_set(op->ptr, "left_right", GRAPHKEYS_LRSEL_RIGHT);
1195                 
1196                 graphkeys_mselect_leftright(&ac, RNA_enum_get(op->ptr, "left_right"), selectmode);
1197         }
1198         else if (RNA_boolean_get(op->ptr, "column")) {
1199                 /* select all keyframes in the same frame as the one that was under the mouse */
1200                 graphkeys_mselect_column(&ac, mval, selectmode);
1201         }
1202         else if (RNA_boolean_get(op->ptr, "curves")) {
1203                 /* select all keyframes in F-Curve under mouse */
1204                 mouse_graph_keys(&ac, mval, selectmode, 1);
1205         }
1206         else {
1207                 /* select keyframe under mouse */
1208                 mouse_graph_keys(&ac, mval, selectmode, 0);
1209         }
1210         
1211         /* set notifier that keyframe selection (and also channel selection in some cases) has changed */
1212         WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_SELECT|ND_ANIMCHAN_SELECT, NULL);
1213         
1214         /* for tweak grab to work */
1215         return OPERATOR_FINISHED|OPERATOR_PASS_THROUGH;
1216 }
1217  
1218 void GRAPH_OT_clickselect (wmOperatorType *ot)
1219 {
1220         /* identifiers */
1221         ot->name= "Mouse Select Keys";
1222         ot->idname= "GRAPH_OT_clickselect";
1223         ot->description= "Select keyframes by clicking on them";
1224         
1225         /* api callbacks */
1226         ot->invoke= graphkeys_clickselect_invoke;
1227         ot->poll= graphop_visible_keyframes_poll;
1228         
1229         /* id-props */
1230         // XXX should we make this into separate operators?
1231         RNA_def_enum(ot->srna, "left_right", prop_graphkeys_leftright_select_types, 0, "Left Right", ""); // CTRLKEY
1232         RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", ""); // SHIFTKEY
1233         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
1234         RNA_def_boolean(ot->srna, "curves", 0, "Only Curves", "Select all the keyframes in the curve"); // CTRLKEY + ALTKEY
1235 }
1236
1237 /* ************************************************************************** */