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