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