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