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