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