Cleanup: style, use braces for editors
[blender.git] / source / blender / editors / space_action / action_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 spaction
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_dlrbTree.h"
32 #include "BLI_lasso_2d.h"
33 #include "BLI_utildefines.h"
34
35 #include "DNA_anim_types.h"
36 #include "DNA_gpencil_types.h"
37 #include "DNA_object_types.h"
38 #include "DNA_scene_types.h"
39 #include "DNA_mask_types.h"
40
41 #include "RNA_access.h"
42 #include "RNA_define.h"
43
44 #include "BKE_fcurve.h"
45 #include "BKE_nla.h"
46 #include "BKE_context.h"
47 #include "BKE_gpencil.h"
48
49 #include "UI_view2d.h"
50
51 #include "ED_anim_api.h"
52 #include "ED_gpencil.h"
53 #include "ED_mask.h"
54 #include "ED_keyframes_draw.h"
55 #include "ED_keyframes_edit.h"
56 #include "ED_markers.h"
57 #include "ED_screen.h"
58 #include "ED_select_utils.h"
59
60 #include "WM_api.h"
61 #include "WM_types.h"
62
63 #include "action_intern.h"
64
65 /* ************************************************************************** */
66 /* KEYFRAMES STUFF */
67
68 /* ******************** Deselect All Operator ***************************** */
69 /* This operator works in one of three ways:
70  * 1) (de)select all (AKEY) - test if select all or deselect all
71  * 2) invert all (CTRL-IKEY) - invert selection of all keyframes
72  * 3) (de)select all - no testing is done; only for use internal tools as normal function...
73  */
74
75 /* Deselects keyframes in the action editor
76  * - This is called by the deselect all operator, as well as other ones!
77  *
78  * - test: check if select or deselect all
79  * - sel: how to select keyframes (SELECT_*)
80  */
81 static void deselect_action_keys(bAnimContext *ac, short test, short sel)
82 {
83   ListBase anim_data = {NULL, NULL};
84   bAnimListElem *ale;
85   int filter;
86
87   KeyframeEditData ked = {{NULL}};
88   KeyframeEditFunc test_cb, sel_cb;
89
90   /* determine type-based settings */
91   if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
92     filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS);
93   }
94   else {
95     filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/ |
96               ANIMFILTER_NODUPLIS);
97   }
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 (ale->type == ANIMTYPE_GPLAYER) {
109         if (ED_gplayer_frame_select_check(ale->data)) {
110           sel = SELECT_SUBTRACT;
111           break;
112         }
113       }
114       else if (ale->type == ANIMTYPE_MASKLAYER) {
115         if (ED_masklayer_frame_select_check(ale->data)) {
116           sel = SELECT_SUBTRACT;
117           break;
118         }
119       }
120       else {
121         if (ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, test_cb, NULL)) {
122           sel = SELECT_SUBTRACT;
123           break;
124         }
125       }
126     }
127   }
128
129   /* convert sel to selectmode, and use that to get editor */
130   sel_cb = ANIM_editkeyframes_select(sel);
131
132   /* Now set the flags */
133   for (ale = anim_data.first; ale; ale = ale->next) {
134     if (ale->type == ANIMTYPE_GPLAYER) {
135       ED_gplayer_frame_select_set(ale->data, sel);
136       ale->update |= ANIM_UPDATE_DEPS;
137     }
138     else if (ale->type == ANIMTYPE_MASKLAYER) {
139       ED_masklayer_frame_select_set(ale->data, sel);
140     }
141     else {
142       ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, sel_cb, NULL);
143     }
144   }
145
146   /* Cleanup */
147   ANIM_animdata_update(ac, &anim_data);
148   ANIM_animdata_freelist(&anim_data);
149 }
150
151 /* ------------------- */
152
153 static int actkeys_deselectall_exec(bContext *C, wmOperator *op)
154 {
155   bAnimContext ac;
156
157   /* get editor data */
158   if (ANIM_animdata_get_context(C, &ac) == 0) {
159     return OPERATOR_CANCELLED;
160   }
161
162   /* 'standard' behavior - check if selected, then apply relevant selection */
163   const int action = RNA_enum_get(op->ptr, "action");
164   switch (action) {
165     case SEL_TOGGLE:
166       deselect_action_keys(&ac, 1, SELECT_ADD);
167       break;
168     case SEL_SELECT:
169       deselect_action_keys(&ac, 0, SELECT_ADD);
170       break;
171     case SEL_DESELECT:
172       deselect_action_keys(&ac, 0, SELECT_SUBTRACT);
173       break;
174     case SEL_INVERT:
175       deselect_action_keys(&ac, 0, SELECT_INVERT);
176       break;
177     default:
178       BLI_assert(0);
179       break;
180   }
181
182   /* set notifier that keyframe selection have changed */
183   WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
184
185   return OPERATOR_FINISHED;
186 }
187
188 void ACTION_OT_select_all(wmOperatorType *ot)
189 {
190   /* identifiers */
191   ot->name = "Select All";
192   ot->idname = "ACTION_OT_select_all";
193   ot->description = "Toggle selection of all keyframes";
194
195   /* api callbacks */
196   ot->exec = actkeys_deselectall_exec;
197   ot->poll = ED_operator_action_active;
198
199   /* flags */
200   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
201
202   /* properties */
203   WM_operator_properties_select_all(ot);
204 }
205
206 /* ******************** Box Select Operator **************************** */
207 /**
208  * This operator currently works in one of three ways:
209  * - BKEY     - 1) all keyframes within region are selected #ACTKEYS_BORDERSEL_ALLKEYS.
210  * - ALT-BKEY - depending on which axis of the region was larger...
211  *   - 2) x-axis, so select all frames within frame range #ACTKEYS_BORDERSEL_FRAMERANGE.
212  *   - 3) y-axis, so select all frames within channels that region included
213  *     #ACTKEYS_BORDERSEL_CHANNELS.
214  */
215
216 /* defines for box_select mode */
217 enum {
218   ACTKEYS_BORDERSEL_ALLKEYS = 0,
219   ACTKEYS_BORDERSEL_FRAMERANGE,
220   ACTKEYS_BORDERSEL_CHANNELS,
221 } /*eActKeys_BoxSelect_Mode*/;
222
223 static void box_select_action(bAnimContext *ac, const rcti rect, short mode, short selectmode)
224 {
225   ListBase anim_data = {NULL, NULL};
226   bAnimListElem *ale;
227   int filter;
228
229   KeyframeEditData ked;
230   KeyframeEditFunc ok_cb, select_cb;
231   View2D *v2d = &ac->ar->v2d;
232   rctf rectf;
233   float ymin = 0, ymax = (float)(-ACHANNEL_HEIGHT_HALF(ac));
234
235   /* Convert mouse coordinates to frame ranges and channel
236    * coordinates corrected for view pan/zoom. */
237   UI_view2d_region_to_view(v2d, rect.xmin, rect.ymin + 2, &rectf.xmin, &rectf.ymin);
238   UI_view2d_region_to_view(v2d, rect.xmax, rect.ymax - 2, &rectf.xmax, &rectf.ymax);
239
240   /* filter data */
241   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
242   ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
243
244   /* get beztriple editing/validation funcs  */
245   select_cb = ANIM_editkeyframes_select(selectmode);
246
247   if (ELEM(mode, ACTKEYS_BORDERSEL_FRAMERANGE, ACTKEYS_BORDERSEL_ALLKEYS)) {
248     ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAMERANGE);
249   }
250   else {
251     ok_cb = NULL;
252   }
253
254   /* init editing data */
255   memset(&ked, 0, sizeof(KeyframeEditData));
256
257   /* loop over data, doing box select */
258   for (ale = anim_data.first; ale; ale = ale->next) {
259     AnimData *adt = ANIM_nla_mapping_get(ac, ale);
260
261     /* get new vertical minimum extent of channel */
262     ymin = ymax - ACHANNEL_STEP(ac);
263
264     /* set horizontal range (if applicable) */
265     if (ELEM(mode, ACTKEYS_BORDERSEL_FRAMERANGE, ACTKEYS_BORDERSEL_ALLKEYS)) {
266       /* if channel is mapped in NLA, apply correction */
267       if (adt) {
268         ked.iterflags &= ~(KED_F1_NLA_UNMAP | KED_F2_NLA_UNMAP);
269         ked.f1 = BKE_nla_tweakedit_remap(adt, rectf.xmin, NLATIME_CONVERT_UNMAP);
270         ked.f2 = BKE_nla_tweakedit_remap(adt, rectf.xmax, NLATIME_CONVERT_UNMAP);
271       }
272       else {
273         ked.iterflags |= (KED_F1_NLA_UNMAP | KED_F2_NLA_UNMAP); /* for summary tracks */
274         ked.f1 = rectf.xmin;
275         ked.f2 = rectf.xmax;
276       }
277     }
278
279     /* perform vertical suitability check (if applicable) */
280     if ((mode == ACTKEYS_BORDERSEL_FRAMERANGE) || !((ymax < rectf.ymin) || (ymin > rectf.ymax))) {
281       /* loop over data selecting */
282       switch (ale->type) {
283 #if 0 /* XXX: Keyframes are not currently shown here */
284         case ANIMTYPE_GPDATABLOCK: {
285           bGPdata *gpd = ale->data;
286           bGPDlayer *gpl;
287           for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
288             ED_gplayer_frames_select_box(gpl, rectf.xmin, rectf.xmax, selectmode);
289           }
290           ale->update |= ANIM_UPDATE_DEPS;
291           break;
292         }
293 #endif
294         case ANIMTYPE_GPLAYER: {
295           ED_gplayer_frames_select_box(ale->data, rectf.xmin, rectf.xmax, selectmode);
296           ale->update |= ANIM_UPDATE_DEPS;
297           break;
298         }
299         case ANIMTYPE_MASKDATABLOCK: {
300           Mask *mask = ale->data;
301           MaskLayer *masklay;
302           for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
303             ED_masklayer_frames_select_box(masklay, rectf.xmin, rectf.xmax, selectmode);
304           }
305           break;
306         }
307         case ANIMTYPE_MASKLAYER: {
308           ED_masklayer_frames_select_box(ale->data, rectf.xmin, rectf.xmax, selectmode);
309           break;
310         }
311         default: {
312           ANIM_animchannel_keyframes_loop(&ked, ac->ads, ale, ok_cb, select_cb, NULL);
313           break;
314         }
315       }
316     }
317
318     /* set minimum extent to be the maximum of the next channel */
319     ymax = ymin;
320   }
321
322   /* cleanup */
323   ANIM_animdata_update(ac, &anim_data);
324   ANIM_animdata_freelist(&anim_data);
325 }
326
327 /* ------------------- */
328
329 static int actkeys_box_select_exec(bContext *C, wmOperator *op)
330 {
331   bAnimContext ac;
332   rcti rect;
333   short mode = 0;
334
335   /* get editor data */
336   if (ANIM_animdata_get_context(C, &ac) == 0) {
337     return OPERATOR_CANCELLED;
338   }
339
340   const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
341   const int selectmode = (sel_op != SEL_OP_SUB) ? SELECT_ADD : SELECT_SUBTRACT;
342   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
343     deselect_action_keys(&ac, 1, SELECT_SUBTRACT);
344   }
345
346   /* get settings from operator */
347   WM_operator_properties_border_to_rcti(op, &rect);
348
349   /* selection 'mode' depends on whether box_select region only matters on one axis */
350   if (RNA_boolean_get(op->ptr, "axis_range")) {
351     /* Mode depends on which axis of the range is larger to determine which axis to use:
352      * - checking this in region-space is fine,
353      *   as it's fundamentally still going to be a different rect size.
354      * - the frame-range select option is favored over the channel one (x over y),
355      *   as frame-range one is often used for tweaking timing when "blocking",
356      *   while channels is not that useful...
357      */
358     if (BLI_rcti_size_x(&rect) >= BLI_rcti_size_y(&rect)) {
359       mode = ACTKEYS_BORDERSEL_FRAMERANGE;
360     }
361     else {
362       mode = ACTKEYS_BORDERSEL_CHANNELS;
363     }
364   }
365   else {
366     mode = ACTKEYS_BORDERSEL_ALLKEYS;
367   }
368
369   /* apply box_select action */
370   box_select_action(&ac, rect, mode, selectmode);
371
372   /* set notifier that keyframe selection have changed */
373   WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
374
375   return OPERATOR_FINISHED;
376 }
377
378 void ACTION_OT_select_box(wmOperatorType *ot)
379 {
380   /* identifiers */
381   ot->name = "Box Select";
382   ot->idname = "ACTION_OT_select_box";
383   ot->description = "Select all keyframes within the specified region";
384
385   /* api callbacks */
386   ot->invoke = WM_gesture_box_invoke;
387   ot->exec = actkeys_box_select_exec;
388   ot->modal = WM_gesture_box_modal;
389   ot->cancel = WM_gesture_box_cancel;
390
391   ot->poll = ED_operator_action_active;
392
393   /* flags */
394   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
395
396   /* rna */
397   ot->prop = RNA_def_boolean(ot->srna, "axis_range", 0, "Axis Range", "");
398
399   /* properties */
400   WM_operator_properties_gesture_box(ot);
401   WM_operator_properties_select_operation_simple(ot);
402 }
403
404 /* ******************** Region Select Operators ***************************** */
405 /* "Region Select" operators include the Lasso and Circle Select operators.
406  * These two ended up being lumped together, as it was easier in the
407  * original Graph Editor implementation of these to do it this way.
408  */
409
410 static void region_select_action_keys(
411     bAnimContext *ac, const rctf *rectf_view, short mode, short selectmode, void *data)
412 {
413   ListBase anim_data = {NULL, NULL};
414   bAnimListElem *ale;
415   int filter;
416
417   KeyframeEditData ked;
418   KeyframeEditFunc ok_cb, select_cb;
419   View2D *v2d = &ac->ar->v2d;
420   rctf rectf, scaled_rectf;
421   float ymin = 0, ymax = (float)(-ACHANNEL_HEIGHT_HALF(ac));
422
423   /* Convert mouse coordinates to frame ranges and channel
424    * coordinates corrected for view pan/zoom. */
425   UI_view2d_region_to_view_rctf(v2d, rectf_view, &rectf);
426
427   /* filter data */
428   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
429   ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
430
431   /* get beztriple editing/validation funcs  */
432   select_cb = ANIM_editkeyframes_select(selectmode);
433   ok_cb = ANIM_editkeyframes_ok(mode);
434
435   /* init editing data */
436   memset(&ked, 0, sizeof(KeyframeEditData));
437   if (mode == BEZT_OK_CHANNEL_LASSO) {
438     KeyframeEdit_LassoData *data_lasso = data;
439     data_lasso->rectf_scaled = &scaled_rectf;
440     ked.data = data_lasso;
441   }
442   else if (mode == BEZT_OK_CHANNEL_CIRCLE) {
443     KeyframeEdit_CircleData *data_circle = data;
444     data_circle->rectf_scaled = &scaled_rectf;
445     ked.data = data;
446   }
447   else {
448     ked.data = &scaled_rectf;
449   }
450
451   /* loop over data, doing region select */
452   for (ale = anim_data.first; ale; ale = ale->next) {
453     AnimData *adt = ANIM_nla_mapping_get(ac, ale);
454
455     /* get new vertical minimum extent of channel */
456     ymin = ymax - ACHANNEL_STEP(ac);
457
458     /* compute midpoint of channel (used for testing if the key is in the region or not) */
459     ked.channel_y = ymin + ACHANNEL_HEIGHT_HALF(ac);
460
461     /* if channel is mapped in NLA, apply correction
462      * - Apply to the bounds being checked, not all the keyframe points,
463      *   to avoid having scaling everything
464      * - Save result to the scaled_rect, which is all that these operators
465      *   will read from
466      */
467     if (adt) {
468       ked.iterflags &= ~(KED_F1_NLA_UNMAP | KED_F2_NLA_UNMAP);
469       ked.f1 = BKE_nla_tweakedit_remap(adt, rectf.xmin, NLATIME_CONVERT_UNMAP);
470       ked.f2 = BKE_nla_tweakedit_remap(adt, rectf.xmax, NLATIME_CONVERT_UNMAP);
471     }
472     else {
473       ked.iterflags |= (KED_F1_NLA_UNMAP | KED_F2_NLA_UNMAP); /* for summary tracks */
474       ked.f1 = rectf.xmin;
475       ked.f2 = rectf.xmax;
476     }
477
478     /* Update values for scaled_rectf - which is used to compute the mapping in the callbacks
479      * NOTE: Since summary tracks need late-binding remapping, the callbacks may overwrite these
480      *       with the properly remapped ked.f1/f2 values, when needed
481      */
482     scaled_rectf.xmin = ked.f1;
483     scaled_rectf.xmax = ked.f2;
484     scaled_rectf.ymin = ymin;
485     scaled_rectf.ymax = ymax;
486
487     /* perform vertical suitability check (if applicable) */
488     if ((mode == ACTKEYS_BORDERSEL_FRAMERANGE) || !((ymax < rectf.ymin) || (ymin > rectf.ymax))) {
489       /* loop over data selecting */
490       switch (ale->type) {
491 #if 0 /* XXX: Keyframes are not currently shown here */
492         case ANIMTYPE_GPDATABLOCK: {
493           bGPdata *gpd = ale->data;
494           bGPDlayer *gpl;
495           for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
496             ED_gplayer_frames_select_region(&ked, ale->data, mode, selectmode);
497           }
498           break;
499         }
500 #endif
501         case ANIMTYPE_GPLAYER: {
502           ED_gplayer_frames_select_region(&ked, ale->data, mode, selectmode);
503           ale->update |= ANIM_UPDATE_DEPS;
504           break;
505         }
506         case ANIMTYPE_MASKDATABLOCK: {
507           Mask *mask = ale->data;
508           MaskLayer *masklay;
509           for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
510             ED_masklayer_frames_select_region(&ked, masklay, mode, selectmode);
511           }
512           break;
513         }
514         case ANIMTYPE_MASKLAYER: {
515           ED_masklayer_frames_select_region(&ked, ale->data, mode, selectmode);
516           break;
517         }
518         default:
519           ANIM_animchannel_keyframes_loop(&ked, ac->ads, ale, ok_cb, select_cb, NULL);
520           break;
521       }
522     }
523
524     /* set minimum extent to be the maximum of the next channel */
525     ymax = ymin;
526   }
527
528   /* cleanup */
529   ANIM_animdata_update(ac, &anim_data);
530   ANIM_animdata_freelist(&anim_data);
531 }
532
533 /* ----------------------------------- */
534
535 static int actkeys_lassoselect_exec(bContext *C, wmOperator *op)
536 {
537   bAnimContext ac;
538
539   KeyframeEdit_LassoData data_lasso;
540   rcti rect;
541   rctf rect_fl;
542
543   /* get editor data */
544   if (ANIM_animdata_get_context(C, &ac) == 0) {
545     return OPERATOR_CANCELLED;
546   }
547
548   data_lasso.rectf_view = &rect_fl;
549   data_lasso.mcords = WM_gesture_lasso_path_to_array(C, op, &data_lasso.mcords_tot);
550   if (data_lasso.mcords == NULL) {
551     return OPERATOR_CANCELLED;
552   }
553
554   const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
555   const int selectmode = (sel_op != SEL_OP_SUB) ? SELECT_ADD : SELECT_SUBTRACT;
556   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
557     deselect_action_keys(&ac, 1, SELECT_SUBTRACT);
558   }
559
560   /* get settings from operator */
561   BLI_lasso_boundbox(&rect, data_lasso.mcords, data_lasso.mcords_tot);
562   BLI_rctf_rcti_copy(&rect_fl, &rect);
563
564   /* apply box_select action */
565   region_select_action_keys(&ac, &rect_fl, BEZT_OK_CHANNEL_LASSO, selectmode, &data_lasso);
566
567   MEM_freeN((void *)data_lasso.mcords);
568
569   /* send notifier that keyframe selection has changed */
570   WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
571
572   return OPERATOR_FINISHED;
573 }
574
575 void ACTION_OT_select_lasso(wmOperatorType *ot)
576 {
577   /* identifiers */
578   ot->name = "Lasso Select";
579   ot->description = "Select keyframe points using lasso selection";
580   ot->idname = "ACTION_OT_select_lasso";
581
582   /* api callbacks */
583   ot->invoke = WM_gesture_lasso_invoke;
584   ot->modal = WM_gesture_lasso_modal;
585   ot->exec = actkeys_lassoselect_exec;
586   ot->poll = ED_operator_action_active;
587   ot->cancel = WM_gesture_lasso_cancel;
588
589   /* flags */
590   ot->flag = OPTYPE_UNDO;
591
592   /* properties */
593   WM_operator_properties_gesture_lasso(ot);
594   WM_operator_properties_select_operation_simple(ot);
595 }
596
597 /* ------------------- */
598
599 static int action_circle_select_exec(bContext *C, wmOperator *op)
600 {
601   bAnimContext ac;
602
603   KeyframeEdit_CircleData data = {0};
604   rctf rect_fl;
605
606   float x = RNA_int_get(op->ptr, "x");
607   float y = RNA_int_get(op->ptr, "y");
608   float radius = RNA_int_get(op->ptr, "radius");
609
610   /* get editor data */
611   if (ANIM_animdata_get_context(C, &ac) == 0) {
612     return OPERATOR_CANCELLED;
613   }
614
615   const eSelectOp sel_op = ED_select_op_modal(RNA_enum_get(op->ptr, "mode"),
616                                               WM_gesture_is_modal_first(op->customdata));
617   const short selectmode = (sel_op != SEL_OP_SUB) ? SELECT_ADD : SELECT_SUBTRACT;
618   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
619     deselect_action_keys(&ac, 0, SELECT_SUBTRACT);
620   }
621
622   data.mval[0] = x;
623   data.mval[1] = y;
624   data.radius_squared = radius * radius;
625   data.rectf_view = &rect_fl;
626
627   rect_fl.xmin = x - radius;
628   rect_fl.xmax = x + radius;
629   rect_fl.ymin = y - radius;
630   rect_fl.ymax = y + radius;
631
632   /* apply region select action */
633   region_select_action_keys(&ac, &rect_fl, BEZT_OK_CHANNEL_CIRCLE, selectmode, &data);
634
635   /* send notifier that keyframe selection has changed */
636   WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
637
638   return OPERATOR_FINISHED;
639 }
640
641 void ACTION_OT_select_circle(wmOperatorType *ot)
642 {
643   ot->name = "Circle Select";
644   ot->description = "Select keyframe points using circle selection";
645   ot->idname = "ACTION_OT_select_circle";
646
647   ot->invoke = WM_gesture_circle_invoke;
648   ot->modal = WM_gesture_circle_modal;
649   ot->exec = action_circle_select_exec;
650   ot->poll = ED_operator_action_active;
651   ot->cancel = WM_gesture_circle_cancel;
652
653   /* flags */
654   ot->flag = OPTYPE_UNDO;
655
656   /* properties */
657   WM_operator_properties_gesture_circle(ot);
658   WM_operator_properties_select_operation_simple(ot);
659 }
660
661 /* ******************** Column Select Operator **************************** */
662 /* This operator works in one of four ways:
663  * - 1) select all keyframes in the same frame as a selected one  (KKEY)
664  * - 2) select all keyframes in the same frame as the current frame marker (CTRL-KKEY)
665  * - 3) select all keyframes in the same frame as a selected markers (SHIFT-KKEY)
666  * - 4) select all keyframes that occur between selected markers (ALT-KKEY)
667  */
668
669 /* defines for column-select mode */
670 static const EnumPropertyItem prop_column_select_types[] = {
671     {ACTKEYS_COLUMNSEL_KEYS, "KEYS", 0, "On Selected Keyframes", ""},
672     {ACTKEYS_COLUMNSEL_CFRA, "CFRA", 0, "On Current Frame", ""},
673     {ACTKEYS_COLUMNSEL_MARKERS_COLUMN, "MARKERS_COLUMN", 0, "On Selected Markers", ""},
674     {ACTKEYS_COLUMNSEL_MARKERS_BETWEEN,
675      "MARKERS_BETWEEN",
676      0,
677      "Between Min/Max Selected Markers",
678      ""},
679     {0, NULL, 0, NULL, NULL},
680 };
681
682 /* ------------------- */
683
684 /* Selects all visible keyframes between the specified markers */
685 /* TODO, this is almost an _exact_ duplicate of a function of the same name in graph_select.c
686  * should de-duplicate - campbell */
687 static void markers_selectkeys_between(bAnimContext *ac)
688 {
689   ListBase anim_data = {NULL, NULL};
690   bAnimListElem *ale;
691   int filter;
692
693   KeyframeEditFunc ok_cb, select_cb;
694   KeyframeEditData ked = {{NULL}};
695   float min, max;
696
697   /* get extreme markers */
698   ED_markers_get_minmax(ac->markers, 1, &min, &max);
699   min -= 0.5f;
700   max += 0.5f;
701
702   /* get editing funcs + data */
703   ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAMERANGE);
704   select_cb = ANIM_editkeyframes_select(SELECT_ADD);
705
706   ked.f1 = min;
707   ked.f2 = max;
708
709   /* filter data */
710   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY */ |
711             ANIMFILTER_NODUPLIS);
712   ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
713
714   /* select keys in-between */
715   for (ale = anim_data.first; ale; ale = ale->next) {
716     AnimData *adt = ANIM_nla_mapping_get(ac, ale);
717
718     if (adt) {
719       ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1);
720       ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
721       ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
722     }
723     else if (ale->type == ANIMTYPE_GPLAYER) {
724       ED_gplayer_frames_select_box(ale->data, min, max, SELECT_ADD);
725       ale->update |= ANIM_UPDATE_DEPS;
726     }
727     else if (ale->type == ANIMTYPE_MASKLAYER) {
728       ED_masklayer_frames_select_box(ale->data, min, max, SELECT_ADD);
729     }
730     else {
731       ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
732     }
733   }
734
735   /* Cleanup */
736   ANIM_animdata_update(ac, &anim_data);
737   ANIM_animdata_freelist(&anim_data);
738 }
739
740 /* Selects all visible keyframes in the same frames as the specified elements */
741 static void columnselect_action_keys(bAnimContext *ac, short mode)
742 {
743   ListBase anim_data = {NULL, NULL};
744   bAnimListElem *ale;
745   int filter;
746
747   Scene *scene = ac->scene;
748   CfraElem *ce;
749   KeyframeEditFunc select_cb, ok_cb;
750   KeyframeEditData ked = {{NULL}};
751
752   /* build list of columns */
753   switch (mode) {
754     case ACTKEYS_COLUMNSEL_KEYS: /* list of selected keys */
755       if (ac->datatype == ANIMCONT_GPENCIL) {
756         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE);
757         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
758
759         for (ale = anim_data.first; ale; ale = ale->next) {
760           ED_gplayer_make_cfra_list(ale->data, &ked.list, 1);
761         }
762       }
763       else {
764         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/);
765         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
766
767         for (ale = anim_data.first; ale; ale = ale->next) {
768           ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, bezt_to_cfraelem, NULL);
769         }
770       }
771       ANIM_animdata_freelist(&anim_data);
772       break;
773
774     case ACTKEYS_COLUMNSEL_CFRA: /* current frame */
775       /* make a single CfraElem for storing this */
776       ce = MEM_callocN(sizeof(CfraElem), "cfraElem");
777       BLI_addtail(&ked.list, ce);
778
779       ce->cfra = (float)CFRA;
780       break;
781
782     case ACTKEYS_COLUMNSEL_MARKERS_COLUMN: /* list of selected markers */
783       ED_markers_make_cfra_list(ac->markers, &ked.list, SELECT);
784       break;
785
786     default: /* invalid option */
787       return;
788   }
789
790   /* set up BezTriple edit callbacks */
791   select_cb = ANIM_editkeyframes_select(SELECT_ADD);
792   ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAME);
793
794   /* loop through all of the keys and select additional keyframes
795    * based on the keys found to be selected above
796    */
797   if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
798     filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE);
799   }
800   else {
801     filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/);
802   }
803   ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
804
805   for (ale = anim_data.first; ale; ale = ale->next) {
806     AnimData *adt = ANIM_nla_mapping_get(ac, ale);
807
808     /* loop over cfraelems (stored in the KeyframeEditData->list)
809      * - we need to do this here, as we can apply fewer NLA-mapping conversions
810      */
811     for (ce = ked.list.first; ce; ce = ce->next) {
812       /* set frame for validation callback to refer to */
813       if (adt) {
814         ked.f1 = BKE_nla_tweakedit_remap(adt, ce->cfra, NLATIME_CONVERT_UNMAP);
815       }
816       else {
817         ked.f1 = ce->cfra;
818       }
819
820       /* select elements with frame number matching cfraelem */
821       if (ale->type == ANIMTYPE_GPLAYER) {
822         ED_gpencil_select_frame(ale->data, ce->cfra, SELECT_ADD);
823         ale->update |= ANIM_UPDATE_DEPS;
824       }
825       else if (ale->type == ANIMTYPE_MASKLAYER) {
826         ED_mask_select_frame(ale->data, ce->cfra, SELECT_ADD);
827       }
828       else {
829         ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
830       }
831     }
832   }
833
834   /* free elements */
835   BLI_freelistN(&ked.list);
836
837   ANIM_animdata_update(ac, &anim_data);
838   ANIM_animdata_freelist(&anim_data);
839 }
840
841 /* ------------------- */
842
843 static int actkeys_columnselect_exec(bContext *C, wmOperator *op)
844 {
845   bAnimContext ac;
846   short mode;
847
848   /* get editor data */
849   if (ANIM_animdata_get_context(C, &ac) == 0) {
850     return OPERATOR_CANCELLED;
851   }
852
853   /* action to take depends on the mode */
854   mode = RNA_enum_get(op->ptr, "mode");
855
856   if (mode == ACTKEYS_COLUMNSEL_MARKERS_BETWEEN) {
857     markers_selectkeys_between(&ac);
858   }
859   else {
860     columnselect_action_keys(&ac, mode);
861   }
862
863   /* set notifier that keyframe selection have changed */
864   WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
865
866   return OPERATOR_FINISHED;
867 }
868
869 void ACTION_OT_select_column(wmOperatorType *ot)
870 {
871   /* identifiers */
872   ot->name = "Select All";
873   ot->idname = "ACTION_OT_select_column";
874   ot->description = "Select all keyframes on the specified frame(s)";
875
876   /* api callbacks */
877   ot->exec = actkeys_columnselect_exec;
878   ot->poll = ED_operator_action_active;
879
880   /* flags */
881   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
882
883   /* props */
884   ot->prop = RNA_def_enum(ot->srna, "mode", prop_column_select_types, 0, "Mode", "");
885 }
886
887 /* ******************** Select Linked Operator *********************** */
888
889 static int actkeys_select_linked_exec(bContext *C, wmOperator *UNUSED(op))
890 {
891   bAnimContext ac;
892
893   ListBase anim_data = {NULL, NULL};
894   bAnimListElem *ale;
895   int filter;
896
897   KeyframeEditFunc ok_cb = ANIM_editkeyframes_ok(BEZT_OK_SELECTED);
898   KeyframeEditFunc sel_cb = ANIM_editkeyframes_select(SELECT_ADD);
899
900   /* get editor data */
901   if (ANIM_animdata_get_context(C, &ac) == 0) {
902     return OPERATOR_CANCELLED;
903   }
904
905   /* loop through all of the keys and select additional keyframes based on these */
906   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/ |
907             ANIMFILTER_NODUPLIS);
908   ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
909
910   for (ale = anim_data.first; ale; ale = ale->next) {
911     FCurve *fcu = (FCurve *)ale->key_data;
912
913     /* check if anything selected? */
914     if (ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, ok_cb, NULL)) {
915       /* select every keyframe in this curve then */
916       ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, sel_cb, NULL);
917     }
918   }
919
920   /* Cleanup */
921   ANIM_animdata_freelist(&anim_data);
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 ACTION_OT_select_linked(wmOperatorType *ot)
930 {
931   /* identifiers */
932   ot->name = "Select Linked";
933   ot->idname = "ACTION_OT_select_linked";
934   ot->description = "Select keyframes occurring in the same F-Curves as selected ones";
935
936   /* api callbacks */
937   ot->exec = actkeys_select_linked_exec;
938   ot->poll = ED_operator_action_active;
939
940   /* flags */
941   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
942 }
943
944 /* ******************** Select More/Less Operators *********************** */
945
946 /* Common code to perform selection */
947 static void select_moreless_action_keys(bAnimContext *ac, short mode)
948 {
949   ListBase anim_data = {NULL, NULL};
950   bAnimListElem *ale;
951   int filter;
952
953   KeyframeEditData ked = {{NULL}};
954   KeyframeEditFunc build_cb;
955
956   /* init selmap building data */
957   build_cb = ANIM_editkeyframes_buildselmap(mode);
958
959   /* loop through all of the keys and select additional keyframes based on these */
960   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/ |
961             ANIMFILTER_NODUPLIS);
962   ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
963
964   for (ale = anim_data.first; ale; ale = ale->next) {
965     FCurve *fcu = (FCurve *)ale->key_data;
966
967     /* only continue if F-Curve has keyframes */
968     if (fcu->bezt == NULL) {
969       continue;
970     }
971
972     /* build up map of whether F-Curve's keyframes should be selected or not */
973     ked.data = MEM_callocN(fcu->totvert, "selmap actEdit more");
974     ANIM_fcurve_keyframes_loop(&ked, fcu, NULL, build_cb, NULL);
975
976     /* based on this map, adjust the selection status of the keyframes */
977     ANIM_fcurve_keyframes_loop(&ked, fcu, NULL, bezt_selmap_flush, NULL);
978
979     /* free the selmap used here */
980     MEM_freeN(ked.data);
981     ked.data = NULL;
982   }
983
984   /* Cleanup */
985   ANIM_animdata_freelist(&anim_data);
986 }
987
988 /* ----------------- */
989
990 static int actkeys_select_more_exec(bContext *C, wmOperator *UNUSED(op))
991 {
992   bAnimContext ac;
993
994   /* get editor data */
995   if (ANIM_animdata_get_context(C, &ac) == 0) {
996     return OPERATOR_CANCELLED;
997   }
998
999   /* perform select changes */
1000   select_moreless_action_keys(&ac, SELMAP_MORE);
1001
1002   /* set notifier that keyframe selection has changed */
1003   WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
1004
1005   return OPERATOR_FINISHED;
1006 }
1007
1008 void ACTION_OT_select_more(wmOperatorType *ot)
1009 {
1010   /* identifiers */
1011   ot->name = "Select More";
1012   ot->idname = "ACTION_OT_select_more";
1013   ot->description = "Select keyframes beside already selected ones";
1014
1015   /* api callbacks */
1016   ot->exec = actkeys_select_more_exec;
1017   ot->poll = ED_operator_action_active;
1018
1019   /* flags */
1020   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1021 }
1022
1023 /* ----------------- */
1024
1025 static int actkeys_select_less_exec(bContext *C, wmOperator *UNUSED(op))
1026 {
1027   bAnimContext ac;
1028
1029   /* get editor data */
1030   if (ANIM_animdata_get_context(C, &ac) == 0) {
1031     return OPERATOR_CANCELLED;
1032   }
1033
1034   /* perform select changes */
1035   select_moreless_action_keys(&ac, SELMAP_LESS);
1036
1037   /* set notifier that keyframe selection has changed */
1038   WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
1039
1040   return OPERATOR_FINISHED;
1041 }
1042
1043 void ACTION_OT_select_less(wmOperatorType *ot)
1044 {
1045   /* identifiers */
1046   ot->name = "Select Less";
1047   ot->idname = "ACTION_OT_select_less";
1048   ot->description = "Deselect keyframes on ends of selection islands";
1049
1050   /* api callbacks */
1051   ot->exec = actkeys_select_less_exec;
1052   ot->poll = ED_operator_action_active;
1053
1054   /* flags */
1055   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1056 }
1057
1058 /* ******************** Select Left/Right Operator ************************* */
1059 /* Select keyframes left/right of the current frame indicator */
1060
1061 /* defines for left-right select tool */
1062 static const EnumPropertyItem prop_actkeys_leftright_select_types[] = {
1063     {ACTKEYS_LRSEL_TEST, "CHECK", 0, "Check if Select Left or Right", ""},
1064     {ACTKEYS_LRSEL_LEFT, "LEFT", 0, "Before current frame", ""},
1065     {ACTKEYS_LRSEL_RIGHT, "RIGHT", 0, "After current frame", ""},
1066     {0, NULL, 0, NULL, NULL},
1067 };
1068
1069 /* --------------------------------- */
1070
1071 static void actkeys_select_leftright(bAnimContext *ac, short leftright, short select_mode)
1072 {
1073   ListBase anim_data = {NULL, NULL};
1074   bAnimListElem *ale;
1075   int filter;
1076
1077   KeyframeEditFunc ok_cb, select_cb;
1078   KeyframeEditData ked = {{NULL}};
1079   Scene *scene = ac->scene;
1080
1081   /* if select mode is replace, deselect all keyframes (and channels) first */
1082   if (select_mode == SELECT_REPLACE) {
1083     select_mode = SELECT_ADD;
1084
1085     /* - deselect all other keyframes, so that just the newly selected remain
1086      * - channels aren't deselected, since we don't re-select any as a consequence
1087      */
1088     deselect_action_keys(ac, 0, SELECT_SUBTRACT);
1089   }
1090
1091   /* set callbacks and editing data */
1092   ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAMERANGE);
1093   select_cb = ANIM_editkeyframes_select(select_mode);
1094
1095   if (leftright == ACTKEYS_LRSEL_LEFT) {
1096     ked.f1 = MINAFRAMEF;
1097     ked.f2 = (float)(CFRA + 0.1f);
1098   }
1099   else {
1100     ked.f1 = (float)(CFRA - 0.1f);
1101     ked.f2 = MAXFRAMEF;
1102   }
1103
1104   /* filter data */
1105   if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
1106     filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS);
1107   }
1108   else {
1109     filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/ |
1110               ANIMFILTER_NODUPLIS);
1111   }
1112   ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1113
1114   /* select keys */
1115   for (ale = anim_data.first; ale; ale = ale->next) {
1116     AnimData *adt = ANIM_nla_mapping_get(ac, ale);
1117
1118     if (adt) {
1119       ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1);
1120       ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
1121       ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
1122     }
1123     else if (ale->type == ANIMTYPE_GPLAYER) {
1124       ED_gplayer_frames_select_box(ale->data, ked.f1, ked.f2, select_mode);
1125       ale->update |= ANIM_UPDATE_DEPS;
1126     }
1127     else if (ale->type == ANIMTYPE_MASKLAYER) {
1128       ED_masklayer_frames_select_box(ale->data, ked.f1, ked.f2, select_mode);
1129     }
1130     else {
1131       ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
1132     }
1133   }
1134
1135   /* Sync marker support */
1136   if (select_mode == SELECT_ADD) {
1137     SpaceAction *saction = (SpaceAction *)ac->sl;
1138
1139     if ((saction) && (saction->flag & SACTION_MARKERS_MOVE)) {
1140       ListBase *markers = ED_animcontext_get_markers(ac);
1141       TimeMarker *marker;
1142
1143       for (marker = markers->first; marker; marker = marker->next) {
1144         if (((leftright == ACTKEYS_LRSEL_LEFT) && (marker->frame < CFRA)) ||
1145             ((leftright == ACTKEYS_LRSEL_RIGHT) && (marker->frame >= CFRA))) {
1146           marker->flag |= SELECT;
1147         }
1148         else {
1149           marker->flag &= ~SELECT;
1150         }
1151       }
1152     }
1153   }
1154
1155   /* Cleanup */
1156   ANIM_animdata_update(ac, &anim_data);
1157   ANIM_animdata_freelist(&anim_data);
1158 }
1159
1160 /* ----------------- */
1161
1162 static int actkeys_select_leftright_exec(bContext *C, wmOperator *op)
1163 {
1164   bAnimContext ac;
1165   short leftright = RNA_enum_get(op->ptr, "mode");
1166   short selectmode;
1167
1168   /* get editor data */
1169   if (ANIM_animdata_get_context(C, &ac) == 0) {
1170     return OPERATOR_CANCELLED;
1171   }
1172
1173   /* select mode is either replace (deselect all, then add) or add/extend */
1174   if (RNA_boolean_get(op->ptr, "extend")) {
1175     selectmode = SELECT_INVERT;
1176   }
1177   else {
1178     selectmode = SELECT_REPLACE;
1179   }
1180
1181   /* if "test" mode is set, we don't have any info to set this with */
1182   if (leftright == ACTKEYS_LRSEL_TEST) {
1183     return OPERATOR_CANCELLED;
1184   }
1185
1186   /* do the selecting now */
1187   actkeys_select_leftright(&ac, leftright, selectmode);
1188
1189   /* set notifier that keyframe selection (and channels too) have changed */
1190   WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
1191   WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL);
1192
1193   return OPERATOR_FINISHED;
1194 }
1195
1196 static int actkeys_select_leftright_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1197 {
1198   bAnimContext ac;
1199   short leftright = RNA_enum_get(op->ptr, "mode");
1200
1201   /* get editor data */
1202   if (ANIM_animdata_get_context(C, &ac) == 0) {
1203     return OPERATOR_CANCELLED;
1204   }
1205
1206   /* handle mode-based testing */
1207   if (leftright == ACTKEYS_LRSEL_TEST) {
1208     Scene *scene = ac.scene;
1209     ARegion *ar = ac.ar;
1210     View2D *v2d = &ar->v2d;
1211     float x;
1212
1213     /* determine which side of the current frame mouse is on */
1214     x = UI_view2d_region_to_view_x(v2d, event->mval[0]);
1215     if (x < CFRA) {
1216       RNA_enum_set(op->ptr, "mode", ACTKEYS_LRSEL_LEFT);
1217     }
1218     else {
1219       RNA_enum_set(op->ptr, "mode", ACTKEYS_LRSEL_RIGHT);
1220     }
1221   }
1222
1223   /* perform selection */
1224   return actkeys_select_leftright_exec(C, op);
1225 }
1226
1227 void ACTION_OT_select_leftright(wmOperatorType *ot)
1228 {
1229   PropertyRNA *prop;
1230
1231   /* identifiers */
1232   ot->name = "Select Left/Right";
1233   ot->idname = "ACTION_OT_select_leftright";
1234   ot->description = "Select keyframes to the left or the right of the current frame";
1235
1236   /* api callbacks  */
1237   ot->invoke = actkeys_select_leftright_invoke;
1238   ot->exec = actkeys_select_leftright_exec;
1239   ot->poll = ED_operator_action_active;
1240
1241   /* flags */
1242   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1243
1244   /* properties */
1245   ot->prop = RNA_def_enum(
1246       ot->srna, "mode", prop_actkeys_leftright_select_types, ACTKEYS_LRSEL_TEST, "Mode", "");
1247   RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE);
1248
1249   prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", "");
1250   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1251 }
1252
1253 /* ******************** Mouse-Click Select Operator *********************** */
1254 /* This operator works in one of three ways:
1255  * - 1) keyframe under mouse - no special modifiers
1256  * - 2) all keyframes on the same side of current frame indicator as mouse - ALT modifier
1257  * - 3) column select all keyframes in frame under mouse - CTRL modifier
1258  * - 4) all keyframes in channel under mouse - CTRL+ALT modifiers
1259  *
1260  * In addition to these basic options, the SHIFT modifier can be used to toggle the
1261  * selection mode between replacing the selection (without) and inverting the selection (with).
1262  */
1263
1264 /* ------------------- */
1265
1266 /* option 1) select keyframe directly under mouse */
1267 static void actkeys_mselect_single(bAnimContext *ac,
1268                                    bAnimListElem *ale,
1269                                    short select_mode,
1270                                    float selx)
1271 {
1272   KeyframeEditData ked = {{NULL}};
1273   KeyframeEditFunc select_cb, ok_cb;
1274
1275   /* get functions for selecting keyframes */
1276   select_cb = ANIM_editkeyframes_select(select_mode);
1277   ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAME);
1278   ked.f1 = selx;
1279   ked.iterflags |= KED_F1_NLA_UNMAP;
1280
1281   /* select the nominated keyframe on the given frame */
1282   if (ale->type == ANIMTYPE_GPLAYER) {
1283     ED_gpencil_select_frame(ale->data, selx, select_mode);
1284     ale->update |= ANIM_UPDATE_DEPS;
1285   }
1286   else if (ale->type == ANIMTYPE_MASKLAYER) {
1287     ED_mask_select_frame(ale->data, selx, select_mode);
1288   }
1289   else {
1290     if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK) && (ale->type == ANIMTYPE_SUMMARY) &&
1291         (ale->datatype == ALE_ALL)) {
1292       ListBase anim_data = {NULL, NULL};
1293       int filter;
1294
1295       filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY */ |
1296                 ANIMFILTER_NODUPLIS);
1297       ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1298
1299       for (ale = anim_data.first; ale; ale = ale->next) {
1300         if (ale->type == ANIMTYPE_GPLAYER) {
1301           ED_gpencil_select_frame(ale->data, selx, select_mode);
1302           ale->update |= ANIM_UPDATE_DEPS;
1303         }
1304         else if (ale->type == ANIMTYPE_MASKLAYER) {
1305           ED_mask_select_frame(ale->data, selx, select_mode);
1306         }
1307       }
1308
1309       ANIM_animdata_update(ac, &anim_data);
1310       ANIM_animdata_freelist(&anim_data);
1311     }
1312     else {
1313       ANIM_animchannel_keyframes_loop(&ked, ac->ads, ale, ok_cb, select_cb, NULL);
1314     }
1315   }
1316 }
1317
1318 /* Option 2) Selects all the keyframes on either side of the current frame
1319  * (depends on which side the mouse is on) */
1320 /* (see actkeys_select_leftright) */
1321
1322 /* Option 3) Selects all visible keyframes in the same frame as the mouse click */
1323 static void actkeys_mselect_column(bAnimContext *ac, short select_mode, float selx)
1324 {
1325   ListBase anim_data = {NULL, NULL};
1326   bAnimListElem *ale;
1327   int filter;
1328
1329   KeyframeEditFunc select_cb, ok_cb;
1330   KeyframeEditData ked = {{NULL}};
1331
1332   /* set up BezTriple edit callbacks */
1333   select_cb = ANIM_editkeyframes_select(select_mode);
1334   ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAME);
1335
1336   /* loop through all of the keys and select additional keyframes
1337    * based on the keys found to be selected above
1338    */
1339   if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
1340     filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY */ |
1341               ANIMFILTER_NODUPLIS);
1342   }
1343   else {
1344     filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS);
1345   }
1346   ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1347
1348   for (ale = anim_data.first; ale; ale = ale->next) {
1349     AnimData *adt = ANIM_nla_mapping_get(ac, ale);
1350
1351     /* set frame for validation callback to refer to */
1352     if (adt) {
1353       ked.f1 = BKE_nla_tweakedit_remap(adt, selx, NLATIME_CONVERT_UNMAP);
1354     }
1355     else {
1356       ked.f1 = selx;
1357     }
1358
1359     /* select elements with frame number matching cfra */
1360     if (ale->type == ANIMTYPE_GPLAYER) {
1361       ED_gpencil_select_frame(ale->key_data, selx, select_mode);
1362       ale->update |= ANIM_UPDATE_DEPS;
1363     }
1364     else if (ale->type == ANIMTYPE_MASKLAYER) {
1365       ED_mask_select_frame(ale->key_data, selx, select_mode);
1366     }
1367     else {
1368       ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
1369     }
1370   }
1371
1372   /* free elements */
1373   BLI_freelistN(&ked.list);
1374
1375   ANIM_animdata_update(ac, &anim_data);
1376   ANIM_animdata_freelist(&anim_data);
1377 }
1378
1379 /* option 4) select all keyframes in same channel */
1380 static void actkeys_mselect_channel_only(bAnimContext *ac, bAnimListElem *ale, short select_mode)
1381 {
1382   KeyframeEditFunc select_cb;
1383
1384   /* get functions for selecting keyframes */
1385   select_cb = ANIM_editkeyframes_select(select_mode);
1386
1387   /* select all keyframes in this channel */
1388   if (ale->type == ANIMTYPE_GPLAYER) {
1389     ED_gpencil_select_frames(ale->data, select_mode);
1390     ale->update = ANIM_UPDATE_DEPS;
1391   }
1392   else if (ale->type == ANIMTYPE_MASKLAYER) {
1393     ED_mask_select_frames(ale->data, select_mode);
1394   }
1395   else {
1396     if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK) && (ale->type == ANIMTYPE_SUMMARY) &&
1397         (ale->datatype == ALE_ALL)) {
1398       ListBase anim_data = {NULL, NULL};
1399       int filter;
1400
1401       filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY */ |
1402                 ANIMFILTER_NODUPLIS);
1403       ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1404
1405       for (ale = anim_data.first; ale; ale = ale->next) {
1406         if (ale->type == ANIMTYPE_GPLAYER) {
1407           ED_gpencil_select_frames(ale->data, select_mode);
1408           ale->update |= ANIM_UPDATE_DEPS;
1409         }
1410         else if (ale->type == ANIMTYPE_MASKLAYER) {
1411           ED_mask_select_frames(ale->data, select_mode);
1412         }
1413       }
1414
1415       ANIM_animdata_update(ac, &anim_data);
1416       ANIM_animdata_freelist(&anim_data);
1417     }
1418     else {
1419       ANIM_animchannel_keyframes_loop(NULL, ac->ads, ale, NULL, select_cb, NULL);
1420     }
1421   }
1422 }
1423
1424 /* ------------------- */
1425
1426 static void mouse_action_keys(
1427     bAnimContext *ac, const int mval[2], short select_mode, bool column, bool same_channel)
1428 {
1429   ListBase anim_data = {NULL, NULL};
1430   DLRBT_Tree anim_keys;
1431   bAnimListElem *ale;
1432   int filter;
1433
1434   View2D *v2d = &ac->ar->v2d;
1435   bDopeSheet *ads = NULL;
1436   int channel_index;
1437   bool found = false;
1438   float frame = 0.0f; /* frame of keyframe under mouse - NLA corrections not applied/included */
1439   float selx = 0.0f;  /* frame of keyframe under mouse */
1440   float key_hsize;
1441   float x, y;
1442   rctf rectf;
1443
1444   /* get dopesheet info */
1445   if (ELEM(ac->datatype, ANIMCONT_DOPESHEET, ANIMCONT_TIMELINE)) {
1446     ads = ac->data;
1447   }
1448
1449   /* use View2D to determine the index of the channel (i.e a row in the list) where keyframe was */
1450   UI_view2d_region_to_view(v2d, mval[0], mval[1], &x, &y);
1451   UI_view2d_listview_view_to_cell(
1452       v2d, 0, ACHANNEL_STEP(ac), 0, (float)ACHANNEL_HEIGHT_HALF(ac), x, y, NULL, &channel_index);
1453
1454   /* x-range to check is +/- 7px for standard keyframe under standard dpi/y-scale
1455    * (in screen/region-space), on either side of mouse click (size of keyframe icon).
1456    */
1457
1458   /* standard channel height (to allow for some slop) */
1459   key_hsize = ACHANNEL_HEIGHT(ac) * 0.8f;
1460   /* half-size (for either side), but rounded up to nearest int (for easier targeting) */
1461   key_hsize = roundf(key_hsize / 2.0f);
1462
1463   UI_view2d_region_to_view(v2d, mval[0] - (int)key_hsize, mval[1], &rectf.xmin, &rectf.ymin);
1464   UI_view2d_region_to_view(v2d, mval[0] + (int)key_hsize, mval[1], &rectf.xmax, &rectf.ymax);
1465
1466   /* filter data */
1467   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
1468   ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1469
1470   /* try to get channel */
1471   ale = BLI_findlink(&anim_data, channel_index);
1472   if (ale == NULL) {
1473     /* channel not found */
1474     printf("Error: animation channel (index = %d) not found in mouse_action_keys()\n",
1475            channel_index);
1476     ANIM_animdata_freelist(&anim_data);
1477     return;
1478   }
1479   else {
1480     /* found match - must return here... */
1481     AnimData *adt = ANIM_nla_mapping_get(ac, ale);
1482     ActKeyColumn *ak, *akn = NULL;
1483
1484     /* make list of keyframes */
1485     BLI_dlrbTree_init(&anim_keys);
1486
1487     if (ale->key_data) {
1488       switch (ale->datatype) {
1489         case ALE_SCE: {
1490           Scene *scene = (Scene *)ale->key_data;
1491           scene_to_keylist(ads, scene, &anim_keys, 0);
1492           break;
1493         }
1494         case ALE_OB: {
1495           Object *ob = (Object *)ale->key_data;
1496           ob_to_keylist(ads, ob, &anim_keys, 0);
1497           break;
1498         }
1499         case ALE_ACT: {
1500           bAction *act = (bAction *)ale->key_data;
1501           action_to_keylist(adt, act, &anim_keys, 0);
1502           break;
1503         }
1504         case ALE_FCURVE: {
1505           FCurve *fcu = (FCurve *)ale->key_data;
1506           fcurve_to_keylist(adt, fcu, &anim_keys, 0);
1507           break;
1508         }
1509       }
1510     }
1511     else if (ale->type == ANIMTYPE_SUMMARY) {
1512       /* dopesheet summary covers everything */
1513       summary_to_keylist(ac, &anim_keys, 0);
1514     }
1515     else if (ale->type == ANIMTYPE_GROUP) {
1516       // TODO: why don't we just give groups key_data too?
1517       bActionGroup *agrp = (bActionGroup *)ale->data;
1518       agroup_to_keylist(adt, agrp, &anim_keys, 0);
1519     }
1520     else if (ale->type == ANIMTYPE_GPLAYER) {
1521       // TODO: why don't we just give gplayers key_data too?
1522       bGPDlayer *gpl = (bGPDlayer *)ale->data;
1523       gpl_to_keylist(ads, gpl, &anim_keys);
1524     }
1525     else if (ale->type == ANIMTYPE_MASKLAYER) {
1526       // TODO: why don't we just give masklayers key_data too?
1527       MaskLayer *masklay = (MaskLayer *)ale->data;
1528       mask_to_keylist(ads, masklay, &anim_keys);
1529     }
1530
1531     /* start from keyframe at root of BST,
1532      * traversing until we find one within the range that was clicked on */
1533     for (ak = anim_keys.root; ak; ak = akn) {
1534       if (IN_RANGE(ak->cfra, rectf.xmin, rectf.xmax)) {
1535         /* set the frame to use, and apply inverse-correction for NLA-mapping
1536          * so that the frame will get selected by the selection functions without
1537          * requiring to map each frame once again...
1538          */
1539         selx = BKE_nla_tweakedit_remap(adt, ak->cfra, NLATIME_CONVERT_UNMAP);
1540         frame = ak->cfra;
1541         found = true;
1542         break;
1543       }
1544       else if (ak->cfra < rectf.xmin) {
1545         akn = ak->right;
1546       }
1547       else {
1548         akn = ak->left;
1549       }
1550     }
1551
1552     /* Remove active channel from list of channels for separate treatment
1553      * (since it's needed later on). */
1554     BLI_remlink(&anim_data, ale);
1555     ale->next = ale->prev = NULL;
1556
1557     /* cleanup temporary lists */
1558     BLI_dlrbTree_free(&anim_keys);
1559
1560     /* free list of channels, since it's not used anymore */
1561     ANIM_animdata_freelist(&anim_data);
1562   }
1563
1564   /* for replacing selection, firstly need to clear existing selection */
1565   if (select_mode == SELECT_REPLACE) {
1566     /* reset selection mode for next steps */
1567     select_mode = SELECT_ADD;
1568
1569     /* deselect all keyframes */
1570     deselect_action_keys(ac, 0, SELECT_SUBTRACT);
1571
1572     /* highlight channel clicked on */
1573     if (ELEM(ac->datatype, ANIMCONT_ACTION, ANIMCONT_DOPESHEET, ANIMCONT_TIMELINE)) {
1574       /* deselect all other channels first */
1575       ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
1576
1577       /* Highlight Action-Group or F-Curve? */
1578       if (ale && ale->data) {
1579         if (ale->type == ANIMTYPE_GROUP) {
1580           bActionGroup *agrp = ale->data;
1581
1582           agrp->flag |= AGRP_SELECTED;
1583           ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, agrp, ANIMTYPE_GROUP);
1584         }
1585         else if (ELEM(ale->type, ANIMTYPE_FCURVE, ANIMTYPE_NLACURVE)) {
1586           FCurve *fcu = ale->data;
1587
1588           fcu->flag |= FCURVE_SELECTED;
1589           ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, fcu, ale->type);
1590         }
1591       }
1592     }
1593     else if (ac->datatype == ANIMCONT_GPENCIL) {
1594       /* deselect all other channels first */
1595       ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
1596
1597       /* Highlight GPencil Layer */
1598       if ((ale && ale->data) && (ale->type == ANIMTYPE_GPLAYER)) {
1599         bGPDlayer *gpl = ale->data;
1600
1601         gpl->flag |= GP_LAYER_SELECT;
1602         //gpencil_layer_setactive(gpd, gpl);
1603       }
1604     }
1605     else if (ac->datatype == ANIMCONT_MASK) {
1606       /* deselect all other channels first */
1607       ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
1608
1609       /* Highlight GPencil Layer */
1610       if ((ale && ale->data) && (ale->type == ANIMTYPE_MASKLAYER)) {
1611         MaskLayer *masklay = ale->data;
1612
1613         masklay->flag |= MASK_LAYERFLAG_SELECT;
1614         //gpencil_layer_setactive(gpd, gpl);
1615       }
1616     }
1617   }
1618
1619   /* only select keyframes if we clicked on a valid channel and hit something */
1620   if (ale) {
1621     if (found) {
1622       /* apply selection to keyframes */
1623       if (column) {
1624         /* select all keyframes in the same frame as the one we hit on the active channel
1625          * [T41077]: "frame" not "selx" here (i.e. no NLA corrections yet) as the code here
1626          *            does that itself again as it needs to work on multiple datablocks
1627          */
1628         actkeys_mselect_column(ac, select_mode, frame);
1629       }
1630       else if (same_channel) {
1631         /* select all keyframes in the active channel */
1632         actkeys_mselect_channel_only(ac, ale, select_mode);
1633       }
1634       else {
1635         /* select the nominated keyframe on the given frame */
1636         actkeys_mselect_single(ac, ale, select_mode, selx);
1637       }
1638     }
1639
1640     /* flush tagged updates
1641      * NOTE: We temporarily add this channel back to the list so that this can happen
1642      */
1643     anim_data.first = anim_data.last = ale;
1644     ANIM_animdata_update(ac, &anim_data);
1645
1646     /* free this channel */
1647     MEM_freeN(ale);
1648   }
1649 }
1650
1651 /* handle clicking */
1652 static int actkeys_clickselect_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1653 {
1654   bAnimContext ac;
1655   /* ARegion *ar; */ /* UNUSED */
1656   short selectmode;
1657   bool column, channel;
1658
1659   /* get editor data */
1660   if (ANIM_animdata_get_context(C, &ac) == 0) {
1661     return OPERATOR_CANCELLED;
1662   }
1663
1664   /* get useful pointers from animation context data */
1665   /* ar = ac.ar; */ /* UNUSED */
1666
1667   /* select mode is either replace (deselect all, then add) or add/extend */
1668   if (RNA_boolean_get(op->ptr, "extend")) {
1669     selectmode = SELECT_INVERT;
1670   }
1671   else {
1672     selectmode = SELECT_REPLACE;
1673   }
1674
1675   /* column selection */
1676   column = RNA_boolean_get(op->ptr, "column");
1677   channel = RNA_boolean_get(op->ptr, "channel");
1678
1679   /* select keyframe(s) based upon mouse position*/
1680   mouse_action_keys(&ac, event->mval, selectmode, column, channel);
1681
1682   /* set notifier that keyframe selection (and channels too) have changed */
1683   WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
1684   WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL);
1685
1686   /* for tweak grab to work */
1687   return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
1688 }
1689
1690 void ACTION_OT_clickselect(wmOperatorType *ot)
1691 {
1692   PropertyRNA *prop;
1693
1694   /* identifiers */
1695   ot->name = "Select Keyframes";
1696   ot->idname = "ACTION_OT_clickselect";
1697   ot->description = "Select keyframes by clicking on them";
1698
1699   /* callbacks */
1700   ot->invoke = actkeys_clickselect_invoke;
1701   ot->poll = ED_operator_action_active;
1702
1703   /* flags */
1704   ot->flag = OPTYPE_UNDO;
1705
1706   /* properties */
1707   prop = RNA_def_boolean(
1708       ot->srna,
1709       "extend",
1710       0,
1711       "Extend Select",
1712       "Toggle keyframe selection instead of leaving newly selected keyframes only");  // SHIFTKEY
1713   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1714
1715   prop = RNA_def_boolean(
1716       ot->srna,
1717       "column",
1718       0,
1719       "Column Select",
1720       "Select all keyframes that occur on the same frame as the one under the mouse");  // ALTKEY
1721   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1722
1723   prop = RNA_def_boolean(
1724       ot->srna,
1725       "channel",
1726       0,
1727       "Only Channel",
1728       "Select all the keyframes in the channel under the mouse");  // CTRLKEY + ALTKEY
1729   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1730 }
1731
1732 /* ************************************************************************** */