Merging r50625 through r51896 from trunk into soc-2011-tomato
[blender.git] / source / blender / editors / space_action / action_select.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2008 Blender Foundation
19  *
20  * Contributor(s): Joshua Leung
21  *
22  * ***** END GPL LICENSE BLOCK *****
23  */
24
25 /** \file blender/editors/space_action/action_select.c
26  *  \ingroup spaction
27  */
28
29
30 #include <math.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <float.h>
34
35 #include "MEM_guardedalloc.h"
36
37 #include "BLI_blenlib.h"
38 #include "BLI_math.h"
39 #include "BLI_dlrbTree.h"
40 #include "BLI_utildefines.h"
41
42 #include "DNA_anim_types.h"
43 #include "DNA_gpencil_types.h"
44 #include "DNA_object_types.h"
45 #include "DNA_scene_types.h"
46 #include "DNA_mask_types.h"
47
48 #include "RNA_access.h"
49 #include "RNA_define.h"
50
51 #include "BKE_fcurve.h"
52 #include "BKE_nla.h"
53 #include "BKE_context.h"
54
55 #include "UI_view2d.h"
56
57 #include "ED_anim_api.h"
58 #include "ED_gpencil.h"
59 #include "ED_mask.h"
60 #include "ED_keyframes_draw.h"
61 #include "ED_keyframes_edit.h"
62 #include "ED_markers.h"
63 #include "ED_screen.h"
64
65 #include "WM_api.h"
66 #include "WM_types.h"
67
68 #include "action_intern.h"
69
70
71 /* ************************************************************************** */
72 /* KEYFRAMES STUFF */
73
74 /* ******************** Deselect All Operator ***************************** */
75 /* This operator works in one of three ways:
76  *      1) (de)select all (AKEY) - test if select all or deselect all
77  *      2) invert all (CTRL-IKEY) - invert selection of all keyframes
78  *      3) (de)select all - no testing is done; only for use internal tools as normal function...
79  */
80
81 /* Deselects keyframes in the action editor
82  *      - This is called by the deselect all operator, as well as other ones!
83  *
84  *  - test: check if select or deselect all
85  *      - sel: how to select keyframes (SELECT_*)
86  */
87 static void deselect_action_keys(bAnimContext *ac, short test, short sel)
88 {
89         ListBase anim_data = {NULL, NULL};
90         bAnimListElem *ale;
91         int filter;
92         
93         KeyframeEditData ked = {{NULL}};
94         KeyframeEditFunc test_cb, sel_cb;
95         
96         /* determine type-based settings */
97         if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
98                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS);
99         else
100                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
101         
102         /* filter data */
103         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
104         
105         /* init BezTriple looping data */
106         test_cb = ANIM_editkeyframes_ok(BEZT_OK_SELECTED);
107         
108         /* See if we should be selecting or deselecting */
109         if (test) {
110                 for (ale = anim_data.first; ale; ale = ale->next) {
111                         if (ale->type == ANIMTYPE_GPLAYER) {
112                                 if (ED_gplayer_frame_select_check(ale->data)) {
113                                         sel = SELECT_SUBTRACT;
114                                         break;
115                                 }
116                         }
117                         else if (ale->type == ANIMTYPE_MASKLAYER) {
118                                 if (ED_masklayer_frame_select_check(ale->data)) {
119                                         sel = SELECT_SUBTRACT;
120                                         break;
121                                 }
122                         }
123                         else {
124                                 if (ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, test_cb, NULL)) {
125                                         sel = SELECT_SUBTRACT;
126                                         break;
127                                 }
128                         }
129                 }
130         }
131         
132         /* convert sel to selectmode, and use that to get editor */
133         sel_cb = ANIM_editkeyframes_select(sel);
134         
135         /* Now set the flags */
136         for (ale = anim_data.first; ale; ale = ale->next) {
137                 if (ale->type == ANIMTYPE_GPLAYER)
138                         ED_gplayer_frame_select_set(ale->data, sel);
139                 else if (ale->type == ANIMTYPE_MASKLAYER)
140                         ED_masklayer_frame_select_set(ale->data, sel);
141                 else
142                         ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, sel_cb, NULL); 
143         }
144         
145         /* Cleanup */
146         BLI_freelistN(&anim_data);
147 }
148
149 /* ------------------- */
150
151 static int actkeys_deselectall_exec(bContext *C, wmOperator *op)
152 {
153         bAnimContext ac;
154         
155         /* get editor data */
156         if (ANIM_animdata_get_context(C, &ac) == 0)
157                 return OPERATOR_CANCELLED;
158                 
159         /* 'standard' behavior - check if selected, then apply relevant selection */
160         if (RNA_boolean_get(op->ptr, "invert"))
161                 deselect_action_keys(&ac, 0, SELECT_INVERT);
162         else
163                 deselect_action_keys(&ac, 1, SELECT_ADD);
164         
165         /* set notifier that keyframe selection have changed */
166         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
167         
168         return OPERATOR_FINISHED;
169 }
170  
171 void ACTION_OT_select_all_toggle(wmOperatorType *ot)
172 {
173         /* identifiers */
174         ot->name = "Select All";
175         ot->idname = "ACTION_OT_select_all_toggle";
176         ot->description = "Toggle selection of all keyframes";
177         
178         /* api callbacks */
179         ot->exec = actkeys_deselectall_exec;
180         ot->poll = ED_operator_action_active;
181         
182         /* flags */
183         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
184         
185         /* props */
186         ot->prop = RNA_def_boolean(ot->srna, "invert", 0, "Invert", "");
187         RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE);
188 }
189
190 /* ******************** Border Select Operator **************************** */
191 /* This operator currently works in one of three ways:
192  *      -> BKEY     - 1) all keyframes within region are selected (ACTKEYS_BORDERSEL_ALLKEYS)
193  *      -> ALT-BKEY - depending on which axis of the region was larger...
194  *              -> 2) x-axis, so select all frames within frame range (ACTKEYS_BORDERSEL_FRAMERANGE)
195  *              -> 3) y-axis, so select all frames within channels that region included (ACTKEYS_BORDERSEL_CHANNELS)
196  */
197
198 /* defines for borderselect mode */
199 enum {
200         ACTKEYS_BORDERSEL_ALLKEYS   = 0,
201         ACTKEYS_BORDERSEL_FRAMERANGE,
202         ACTKEYS_BORDERSEL_CHANNELS,
203 } /*eActKeys_BorderSelect_Mode*/;
204
205
206 static void borderselect_action(bAnimContext *ac, rcti rect, short mode, short selectmode)
207 {
208         ListBase anim_data = {NULL, NULL};
209         bAnimListElem *ale;
210         int filter;
211         
212         KeyframeEditData ked;
213         KeyframeEditFunc ok_cb, select_cb;
214         View2D *v2d = &ac->ar->v2d;
215         rctf rectf;
216         float ymin = 0, ymax = (float)(-ACHANNEL_HEIGHT_HALF);
217         
218         /* convert mouse coordinates to frame ranges and channel coordinates corrected for view pan/zoom */
219         UI_view2d_region_to_view(v2d, rect.xmin, rect.ymin + 2, &rectf.xmin, &rectf.ymin);
220         UI_view2d_region_to_view(v2d, rect.xmax, rect.ymax - 2, &rectf.xmax, &rectf.ymax);
221         
222         /* filter data */
223         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_NODUPLIS);
224         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
225         
226         /* get beztriple editing/validation funcs  */
227         select_cb = ANIM_editkeyframes_select(selectmode);
228         
229         if (ELEM(mode, ACTKEYS_BORDERSEL_FRAMERANGE, ACTKEYS_BORDERSEL_ALLKEYS))
230                 ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAMERANGE);
231         else
232                 ok_cb = NULL;
233                 
234         /* init editing data */
235         memset(&ked, 0, sizeof(KeyframeEditData));
236         
237         /* loop over data, doing border select */
238         for (ale = anim_data.first; ale; ale = ale->next) {
239                 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
240                 
241                 /* get new vertical minimum extent of channel */
242                 ymin = ymax - ACHANNEL_STEP;
243                 
244                 /* set horizontal range (if applicable) */
245                 if (ELEM(mode, ACTKEYS_BORDERSEL_FRAMERANGE, ACTKEYS_BORDERSEL_ALLKEYS)) {
246                         /* if channel is mapped in NLA, apply correction */
247                         if (adt) {
248                                 ked.f1 = BKE_nla_tweakedit_remap(adt, rectf.xmin, NLATIME_CONVERT_UNMAP);
249                                 ked.f2 = BKE_nla_tweakedit_remap(adt, rectf.xmax, NLATIME_CONVERT_UNMAP);
250                         }
251                         else {
252                                 ked.f1 = rectf.xmin;
253                                 ked.f2 = rectf.xmax;
254                         }
255                 }
256                 
257                 /* perform vertical suitability check (if applicable) */
258                 if ((mode == ACTKEYS_BORDERSEL_FRAMERANGE) ||
259                     !((ymax < rectf.ymin) || (ymin > rectf.ymax)))
260                 {
261                         /* loop over data selecting */
262                         if (ale->type == ANIMTYPE_GPLAYER)
263                                 ED_gplayer_frames_select_border(ale->data, rectf.xmin, rectf.xmax, selectmode);
264                         else if (ale->type == ANIMTYPE_MASKLAYER)
265                                 ED_masklayer_frames_select_border(ale->data, rectf.xmin, rectf.xmax, selectmode);
266                         else
267                                 ANIM_animchannel_keyframes_loop(&ked, ac->ads, ale, ok_cb, select_cb, NULL);
268                 }
269                 
270                 /* set minimum extent to be the maximum of the next channel */
271                 ymax = ymin;
272         }
273         
274         /* cleanup */
275         BLI_freelistN(&anim_data);
276 }
277
278 /* ------------------- */
279
280 static int actkeys_borderselect_exec(bContext *C, wmOperator *op)
281 {
282         bAnimContext ac;
283         rcti rect;
284         short mode = 0, selectmode = 0;
285         int gesture_mode, extend;
286         
287         /* get editor data */
288         if (ANIM_animdata_get_context(C, &ac) == 0)
289                 return OPERATOR_CANCELLED;
290
291         /* clear all selection if not extending selection */
292         extend = RNA_boolean_get(op->ptr, "extend");
293         if (!extend)
294                 deselect_action_keys(&ac, 1, SELECT_SUBTRACT);
295         
296         /* get settings from operator */
297         WM_operator_properties_border_to_rcti(op, &rect);
298                 
299         gesture_mode = RNA_int_get(op->ptr, "gesture_mode");
300         if (gesture_mode == GESTURE_MODAL_SELECT)
301                 selectmode = SELECT_ADD;
302         else
303                 selectmode = SELECT_SUBTRACT;
304         
305         /* selection 'mode' depends on whether borderselect region only matters on one axis */
306         if (RNA_boolean_get(op->ptr, "axis_range")) {
307                 /* mode depends on which axis of the range is larger to determine which axis to use 
308                  *      - checking this in region-space is fine, as it's fundamentally still going to be a different rect size
309                  *      - the frame-range select option is favored over the channel one (x over y), as frame-range one is often
310                  *        used for tweaking timing when "blocking", while channels is not that useful...
311                  */
312                 if (BLI_rcti_size_x(&rect) >= BLI_rcti_size_y(&rect))
313                         mode = ACTKEYS_BORDERSEL_FRAMERANGE;
314                 else
315                         mode = ACTKEYS_BORDERSEL_CHANNELS;
316         }
317         else 
318                 mode = ACTKEYS_BORDERSEL_ALLKEYS;
319         
320         /* apply borderselect action */
321         borderselect_action(&ac, rect, mode, selectmode);
322         
323         /* set notifier that keyframe selection have changed */
324         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
325         
326         return OPERATOR_FINISHED;
327
328
329 void ACTION_OT_select_border(wmOperatorType *ot)
330 {
331         /* identifiers */
332         ot->name = "Border Select";
333         ot->idname = "ACTION_OT_select_border";
334         ot->description = "Select all keyframes within the specified region";
335         
336         /* api callbacks */
337         ot->invoke = WM_border_select_invoke;
338         ot->exec = actkeys_borderselect_exec;
339         ot->modal = WM_border_select_modal;
340         ot->cancel = WM_border_select_cancel;
341         
342         ot->poll = ED_operator_action_active;
343         
344         /* flags */
345         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
346         
347         /* rna */
348         WM_operator_properties_gesture_border(ot, TRUE);
349         
350         ot->prop = RNA_def_boolean(ot->srna, "axis_range", 0, "Axis Range", "");
351 }
352
353 /* ******************** Column Select Operator **************************** */
354 /* This operator works in one of four ways:
355  *      - 1) select all keyframes in the same frame as a selected one  (KKEY)
356  *      - 2) select all keyframes in the same frame as the current frame marker (CTRL-KKEY)
357  *      - 3) select all keyframes in the same frame as a selected markers (SHIFT-KKEY)
358  *      - 4) select all keyframes that occur between selected markers (ALT-KKEY)
359  */
360
361 /* defines for column-select mode */
362 static EnumPropertyItem prop_column_select_types[] = {
363         {ACTKEYS_COLUMNSEL_KEYS, "KEYS", 0, "On Selected Keyframes", ""},
364         {ACTKEYS_COLUMNSEL_CFRA, "CFRA", 0, "On Current Frame", ""},
365         {ACTKEYS_COLUMNSEL_MARKERS_COLUMN, "MARKERS_COLUMN", 0, "On Selected Markers", ""},
366         {ACTKEYS_COLUMNSEL_MARKERS_BETWEEN, "MARKERS_BETWEEN", 0, "Between Min/Max Selected Markers", ""},
367         {0, NULL, 0, NULL, NULL}
368 };
369
370 /* ------------------- */ 
371
372 /* Selects all visible keyframes between the specified markers */
373 /* TODO, this is almost an _exact_ duplicate of a function of the same name in graph_select.c
374  * should de-duplicate - campbell */
375 static void markers_selectkeys_between(bAnimContext *ac)
376 {
377         ListBase anim_data = {NULL, NULL};
378         bAnimListElem *ale;
379         int filter;
380         
381         KeyframeEditFunc ok_cb, select_cb;
382         KeyframeEditData ked = {{NULL}};
383         float min, max;
384         
385         /* get extreme markers */
386         ED_markers_get_minmax(ac->markers, 1, &min, &max);
387         min -= 0.5f;
388         max += 0.5f;
389         
390         /* get editing funcs + data */
391         ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAMERANGE);
392         select_cb = ANIM_editkeyframes_select(SELECT_ADD);
393
394         ked.f1 = min;
395         ked.f2 = max;
396         
397         /* filter data */
398         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY */ | ANIMFILTER_NODUPLIS);
399         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
400         
401         /* select keys in-between */
402         for (ale = anim_data.first; ale; ale = ale->next) {
403                 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
404                 
405                 if (adt) {
406                         ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1);
407                         ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
408                         ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
409                 }
410                 else if (ale->type == ANIMTYPE_GPLAYER) {
411                         ED_gplayer_frames_select_border(ale->data, min, max, SELECT_ADD);
412                 }
413                 else if (ale->type == ANIMTYPE_MASKLAYER) {
414                         ED_masklayer_frames_select_border(ale->data, min, max, SELECT_ADD);
415                 }
416                 else {
417                         ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
418                 }
419         }
420         
421         /* Cleanup */
422         BLI_freelistN(&anim_data);
423 }
424
425
426 /* Selects all visible keyframes in the same frames as the specified elements */
427 static void columnselect_action_keys(bAnimContext *ac, short mode)
428 {
429         ListBase anim_data = {NULL, NULL};
430         bAnimListElem *ale;
431         int filter;
432         
433         Scene *scene = ac->scene;
434         CfraElem *ce;
435         KeyframeEditFunc select_cb, ok_cb;
436         KeyframeEditData ked = {{NULL}};
437         
438         /* initialize keyframe editing data */
439         
440         /* build list of columns */
441         switch (mode) {
442                 case ACTKEYS_COLUMNSEL_KEYS: /* list of selected keys */
443                         if (ac->datatype == ANIMCONT_GPENCIL) {
444                                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE);
445                                 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
446                                 
447                                 for (ale = anim_data.first; ale; ale = ale->next)
448                                         ED_gplayer_make_cfra_list(ale->data, &ked.list, 1);
449                         }
450                         else {
451                                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/);
452                                 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
453                                 
454                                 for (ale = anim_data.first; ale; ale = ale->next)
455                                         ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, bezt_to_cfraelem, NULL);
456                         }
457                         BLI_freelistN(&anim_data);
458                         break;
459                         
460                 case ACTKEYS_COLUMNSEL_CFRA: /* current frame */
461                         /* make a single CfraElem for storing this */
462                         ce = MEM_callocN(sizeof(CfraElem), "cfraElem");
463                         BLI_addtail(&ked.list, ce);
464                         
465                         ce->cfra = (float)CFRA;
466                         break;
467                         
468                 case ACTKEYS_COLUMNSEL_MARKERS_COLUMN: /* list of selected markers */
469                         ED_markers_make_cfra_list(ac->markers, &ked.list, SELECT);
470                         break;
471                         
472                 default: /* invalid option */
473                         return;
474         }
475         
476         /* set up BezTriple edit callbacks */
477         select_cb = ANIM_editkeyframes_select(SELECT_ADD);
478         ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAME);
479         
480         /* loop through all of the keys and select additional keyframes
481          * based on the keys found to be selected above
482          */
483         if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
484                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE);
485         else
486                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/);
487         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
488         
489         for (ale = anim_data.first; ale; ale = ale->next) {
490                 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
491                 
492                 /* loop over cfraelems (stored in the KeyframeEditData->list)
493                  *      - we need to do this here, as we can apply fewer NLA-mapping conversions
494                  */
495                 for (ce = ked.list.first; ce; ce = ce->next) {
496                         /* set frame for validation callback to refer to */
497                         if (adt)
498                                 ked.f1 = BKE_nla_tweakedit_remap(adt, ce->cfra, NLATIME_CONVERT_UNMAP);
499                         else
500                                 ked.f1 = ce->cfra;
501                         
502                         /* select elements with frame number matching cfraelem */
503                         if (ale->type == ANIMTYPE_GPLAYER)
504                                 ED_gpencil_select_frame(ale->data, ce->cfra, SELECT_ADD);
505                         else if (ale->type == ANIMTYPE_MASKLAYER)
506                                 ED_mask_select_frame(ale->data, ce->cfra, SELECT_ADD);
507                         else
508                                 ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
509                 }
510         }
511         
512         /* free elements */
513         BLI_freelistN(&ked.list);
514         BLI_freelistN(&anim_data);
515 }
516
517 /* ------------------- */
518
519 static int actkeys_columnselect_exec(bContext *C, wmOperator *op)
520 {
521         bAnimContext ac;
522         short mode;
523         
524         /* get editor data */
525         if (ANIM_animdata_get_context(C, &ac) == 0)
526                 return OPERATOR_CANCELLED;
527                 
528         /* action to take depends on the mode */
529         mode = RNA_enum_get(op->ptr, "mode");
530         
531         if (mode == ACTKEYS_COLUMNSEL_MARKERS_BETWEEN)
532                 markers_selectkeys_between(&ac);
533         else
534                 columnselect_action_keys(&ac, mode);
535         
536         /* set notifier that keyframe selection have changed */
537         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
538         
539         return OPERATOR_FINISHED;
540 }
541  
542 void ACTION_OT_select_column(wmOperatorType *ot)
543 {
544         /* identifiers */
545         ot->name = "Select All";
546         ot->idname = "ACTION_OT_select_column";
547         ot->description = "Select all keyframes on the specified frame(s)";
548         
549         /* api callbacks */
550         ot->exec = actkeys_columnselect_exec;
551         ot->poll = ED_operator_action_active;
552         
553         /* flags */
554         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
555         
556         /* props */
557         ot->prop = RNA_def_enum(ot->srna, "mode", prop_column_select_types, 0, "Mode", "");
558 }
559
560 /* ******************** Select Linked Operator *********************** */
561
562 static int actkeys_select_linked_exec(bContext *C, wmOperator *UNUSED(op))
563 {
564         bAnimContext ac;
565         
566         ListBase anim_data = {NULL, NULL};
567         bAnimListElem *ale;
568         int filter;
569         
570         KeyframeEditFunc ok_cb = ANIM_editkeyframes_ok(BEZT_OK_SELECTED);
571         KeyframeEditFunc sel_cb = ANIM_editkeyframes_select(SELECT_ADD);
572         
573         /* get editor data */
574         if (ANIM_animdata_get_context(C, &ac) == 0)
575                 return OPERATOR_CANCELLED;
576         
577         /* loop through all of the keys and select additional keyframes based on these */
578         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
579         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
580         
581         for (ale = anim_data.first; ale; ale = ale->next) {
582                 FCurve *fcu = (FCurve *)ale->key_data;
583                 
584                 /* check if anything selected? */
585                 if (ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, ok_cb, NULL)) {
586                         /* select every keyframe in this curve then */
587                         ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, sel_cb, NULL);
588                 }
589         }
590         
591         /* Cleanup */
592         BLI_freelistN(&anim_data);
593         
594         /* set notifier that keyframe selection has changed */
595         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
596         
597         return OPERATOR_FINISHED;
598 }
599
600 void ACTION_OT_select_linked(wmOperatorType *ot)
601 {
602         /* identifiers */
603         ot->name = "Select Linked";
604         ot->idname = "ACTION_OT_select_linked";
605         ot->description = "Select keyframes occurring in the same F-Curves as selected ones";
606         
607         /* api callbacks */
608         ot->exec = actkeys_select_linked_exec;
609         ot->poll = ED_operator_action_active;
610         
611         /* flags */
612         ot->flag = OPTYPE_REGISTER /*|OPTYPE_UNDO*/;
613 }
614
615 /* ******************** Select More/Less Operators *********************** */
616
617 /* Common code to perform selection */
618 static void select_moreless_action_keys(bAnimContext *ac, short mode)
619 {
620         ListBase anim_data = {NULL, NULL};
621         bAnimListElem *ale;
622         int filter;
623         
624         KeyframeEditData ked = {{NULL}};
625         KeyframeEditFunc build_cb;
626         
627         
628         /* init selmap building data */
629         build_cb = ANIM_editkeyframes_buildselmap(mode);
630         
631         /* loop through all of the keys and select additional keyframes based on these */
632         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
633         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
634         
635         for (ale = anim_data.first; ale; ale = ale->next) {
636                 FCurve *fcu = (FCurve *)ale->key_data;
637                 
638                 /* only continue if F-Curve has keyframes */
639                 if (fcu->bezt == NULL)
640                         continue;
641                 
642                 /* build up map of whether F-Curve's keyframes should be selected or not */
643                 ked.data = MEM_callocN(fcu->totvert, "selmap actEdit more");
644                 ANIM_fcurve_keyframes_loop(&ked, fcu, NULL, build_cb, NULL);
645                 
646                 /* based on this map, adjust the selection status of the keyframes */
647                 ANIM_fcurve_keyframes_loop(&ked, fcu, NULL, bezt_selmap_flush, NULL);
648                 
649                 /* free the selmap used here */
650                 MEM_freeN(ked.data);
651                 ked.data = NULL;
652         }
653         
654         /* Cleanup */
655         BLI_freelistN(&anim_data);
656 }
657
658 /* ----------------- */
659
660 static int actkeys_select_more_exec(bContext *C, wmOperator *UNUSED(op))
661 {
662         bAnimContext ac;
663         
664         /* get editor data */
665         if (ANIM_animdata_get_context(C, &ac) == 0)
666                 return OPERATOR_CANCELLED;
667         
668         /* perform select changes */
669         select_moreless_action_keys(&ac, SELMAP_MORE);
670         
671         /* set notifier that keyframe selection has changed */
672         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
673         
674         return OPERATOR_FINISHED;
675 }
676
677 void ACTION_OT_select_more(wmOperatorType *ot)
678 {
679         /* identifiers */
680         ot->name = "Select More";
681         ot->idname = "ACTION_OT_select_more";
682         ot->description = "Select keyframes beside already selected ones";
683         
684         /* api callbacks */
685         ot->exec = actkeys_select_more_exec;
686         ot->poll = ED_operator_action_active;
687         
688         /* flags */
689         ot->flag = OPTYPE_REGISTER /*|OPTYPE_UNDO*/;
690 }
691
692 /* ----------------- */
693
694 static int actkeys_select_less_exec(bContext *C, wmOperator *UNUSED(op))
695 {
696         bAnimContext ac;
697         
698         /* get editor data */
699         if (ANIM_animdata_get_context(C, &ac) == 0)
700                 return OPERATOR_CANCELLED;
701         
702         /* perform select changes */
703         select_moreless_action_keys(&ac, SELMAP_LESS);
704         
705         /* set notifier that keyframe selection has changed */
706         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
707         
708         return OPERATOR_FINISHED;
709 }
710
711 void ACTION_OT_select_less(wmOperatorType *ot)
712 {
713         /* identifiers */
714         ot->name = "Select Less";
715         ot->idname = "ACTION_OT_select_less";
716         ot->description = "Deselect keyframes on ends of selection islands";
717         
718         /* api callbacks */
719         ot->exec = actkeys_select_less_exec;
720         ot->poll = ED_operator_action_active;
721         
722         /* flags */
723         ot->flag = OPTYPE_REGISTER /*|OPTYPE_UNDO*/;
724 }
725
726 /* ******************** Select Left/Right Operator ************************* */
727 /* Select keyframes left/right of the current frame indicator */
728
729 /* defines for left-right select tool */
730 static EnumPropertyItem prop_actkeys_leftright_select_types[] = {
731         {ACTKEYS_LRSEL_TEST, "CHECK", 0, "Check if Select Left or Right", ""},
732         {ACTKEYS_LRSEL_LEFT, "LEFT", 0, "Before current frame", ""},
733         {ACTKEYS_LRSEL_RIGHT, "RIGHT", 0, "After current frame", ""},
734         {0, NULL, 0, NULL, NULL}
735 };
736
737 /* --------------------------------- */
738
739 static void actkeys_select_leftright(bAnimContext *ac, short leftright, short select_mode)
740 {
741         ListBase anim_data = {NULL, NULL};
742         bAnimListElem *ale;
743         int filter;
744         
745         KeyframeEditFunc ok_cb, select_cb;
746         KeyframeEditData ked = {{NULL}};
747         Scene *scene = ac->scene;
748         
749         /* if select mode is replace, deselect all keyframes (and channels) first */
750         if (select_mode == SELECT_REPLACE) {
751                 select_mode = SELECT_ADD;
752                 
753                 /* - deselect all other keyframes, so that just the newly selected remain
754                  * - channels aren't deselected, since we don't re-select any as a consequence
755                  */
756                 deselect_action_keys(ac, 0, SELECT_SUBTRACT);
757         }
758         
759         /* set callbacks and editing data */
760         ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAMERANGE);
761         select_cb = ANIM_editkeyframes_select(select_mode);
762         
763         if (leftright == ACTKEYS_LRSEL_LEFT) {
764                 ked.f1 = MINAFRAMEF;
765                 ked.f2 = (float)(CFRA + 0.1f);
766         }
767         else {
768                 ked.f1 = (float)(CFRA - 0.1f);
769                 ked.f2 = MAXFRAMEF;
770         }
771         
772         /* filter data */
773         if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
774                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS);
775         else
776                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
777         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
778                 
779         /* select keys */
780         for (ale = anim_data.first; ale; ale = ale->next) {
781                 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
782                 
783                 if (adt) {
784                         ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1);
785                         ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
786                         ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
787                 }
788                 else if (ale->type == ANIMTYPE_GPLAYER)
789                         ED_gplayer_frames_select_border(ale->data, ked.f1, ked.f2, select_mode);
790                 else if (ale->type == ANIMTYPE_MASKLAYER)
791                         ED_masklayer_frames_select_border(ale->data, ked.f1, ked.f2, select_mode);
792                 else
793                         ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
794         }
795         
796         /* Sync marker support */
797         if (select_mode == SELECT_ADD) {
798                 SpaceAction *saction = (SpaceAction *)ac->sl;
799                 
800                 if ((saction) && (saction->flag & SACTION_MARKERS_MOVE)) {
801                         ListBase *markers = ED_animcontext_get_markers(ac);
802                         TimeMarker *marker;
803                         
804                         for (marker = markers->first; marker; marker = marker->next) {
805                                 if (((leftright == ACTKEYS_LRSEL_LEFT)  && (marker->frame <  CFRA)) ||
806                                     ((leftright == ACTKEYS_LRSEL_RIGHT) && (marker->frame >= CFRA)))
807                                 {
808                                         marker->flag |= SELECT;
809                                 }
810                                 else {
811                                         marker->flag &= ~SELECT;
812                                 }
813                         }
814                 }
815         }
816
817         /* Cleanup */
818         BLI_freelistN(&anim_data);
819 }
820
821 /* ----------------- */
822
823 static int actkeys_select_leftright_exec(bContext *C, wmOperator *op)
824 {
825         bAnimContext ac;
826         short leftright = RNA_enum_get(op->ptr, "mode");
827         short selectmode;
828         
829         /* get editor data */
830         if (ANIM_animdata_get_context(C, &ac) == 0)
831                 return OPERATOR_CANCELLED;
832         
833         /* select mode is either replace (deselect all, then add) or add/extend */
834         if (RNA_boolean_get(op->ptr, "extend"))
835                 selectmode = SELECT_INVERT;
836         else
837                 selectmode = SELECT_REPLACE;
838                 
839         /* if "test" mode is set, we don't have any info to set this with */
840         if (leftright == ACTKEYS_LRSEL_TEST)
841                 return OPERATOR_CANCELLED;
842         
843         /* do the selecting now */
844         actkeys_select_leftright(&ac, leftright, selectmode);
845         
846         /* set notifier that keyframe selection (and channels too) have changed */
847         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | ND_ANIMCHAN | NA_SELECTED, NULL);
848         
849         return OPERATOR_FINISHED;
850 }
851
852 static int actkeys_select_leftright_invoke(bContext *C, wmOperator *op, wmEvent *event)
853 {
854         bAnimContext ac;
855         short leftright = RNA_enum_get(op->ptr, "mode");
856         
857         /* get editor data */
858         if (ANIM_animdata_get_context(C, &ac) == 0)
859                 return OPERATOR_CANCELLED;
860                 
861         /* handle mode-based testing */
862         if (leftright == ACTKEYS_LRSEL_TEST) {
863                 Scene *scene = ac.scene;
864                 ARegion *ar = ac.ar;
865                 View2D *v2d = &ar->v2d;
866                 float x;
867
868                 /* determine which side of the current frame mouse is on */
869                 UI_view2d_region_to_view(v2d, event->mval[0], event->mval[1], &x, NULL);
870                 if (x < CFRA)
871                         RNA_enum_set(op->ptr, "mode", ACTKEYS_LRSEL_LEFT);
872                 else
873                         RNA_enum_set(op->ptr, "mode", ACTKEYS_LRSEL_RIGHT);
874         }
875         
876         /* perform selection */
877         return actkeys_select_leftright_exec(C, op);
878 }
879
880 void ACTION_OT_select_leftright(wmOperatorType *ot)
881 {
882         PropertyRNA *prop;
883         
884         /* identifiers */
885         ot->name = "Select Left/Right";
886         ot->idname = "ACTION_OT_select_leftright";
887         ot->description = "Select keyframes to the left or the right of the current frame";
888         
889         /* api callbacks  */
890         ot->invoke = actkeys_select_leftright_invoke;
891         ot->exec = actkeys_select_leftright_exec;
892         ot->poll = ED_operator_action_active;
893         
894         /* flags */
895         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
896         
897         /* properties */
898         ot->prop = RNA_def_enum(ot->srna, "mode", prop_actkeys_leftright_select_types, ACTKEYS_LRSEL_TEST, "Mode", "");
899         RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE);
900         
901         prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", "");
902         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
903 }
904
905 /* ******************** Mouse-Click Select Operator *********************** */
906 /* This operator works in one of three ways:
907  *      - 1) keyframe under mouse - no special modifiers
908  *      - 2) all keyframes on the same side of current frame indicator as mouse - ALT modifier
909  *      - 3) column select all keyframes in frame under mouse - CTRL modifier
910  *
911  * In addition to these basic options, the SHIFT modifier can be used to toggle the 
912  * selection mode between replacing the selection (without) and inverting the selection (with).
913  */
914
915 /* ------------------- */
916  
917 /* option 1) select keyframe directly under mouse */
918 static void actkeys_mselect_single(bAnimContext *ac, bAnimListElem *ale, short select_mode, float selx)
919 {
920         KeyframeEditData ked = {{NULL}};
921         KeyframeEditFunc select_cb, ok_cb;
922         
923         /* get functions for selecting keyframes */
924         select_cb = ANIM_editkeyframes_select(select_mode);
925         ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAME);
926         ked.f1 = selx;
927         
928         /* select the nominated keyframe on the given frame */
929         if (ale->type == ANIMTYPE_GPLAYER)
930                 ED_gpencil_select_frame(ale->data, selx, select_mode);
931         else if (ale->type == ANIMTYPE_MASKLAYER)
932                 ED_mask_select_frame(ale->data, selx, select_mode);
933         else {
934                 if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK) &&
935                     (ale->type == ANIMTYPE_SUMMARY) && (ale->datatype == ALE_ALL))
936                 {
937                         ListBase anim_data = {NULL, NULL};
938                         int filter;
939                         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY */ | ANIMFILTER_NODUPLIS);
940                         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
941                         for (ale = anim_data.first; ale; ale = ale->next) {
942                                 if (ale->type == ANIMTYPE_GPLAYER)
943                                         ED_gpencil_select_frame(ale->data, selx, select_mode);
944                                 else if (ale->type == ANIMTYPE_MASKLAYER)
945                                         ED_mask_select_frame(ale->data, selx, select_mode);
946                         }
947                 }
948                 else {
949                         ANIM_animchannel_keyframes_loop(&ked, ac->ads, ale, ok_cb, select_cb, NULL);
950                 }
951         }
952 }
953
954 /* Option 2) Selects all the keyframes on either side of the current frame (depends on which side the mouse is on) */
955 /* (see actkeys_select_leftright) */
956
957 /* Option 3) Selects all visible keyframes in the same frame as the mouse click */
958 static void actkeys_mselect_column(bAnimContext *ac, short select_mode, float selx)
959 {
960         ListBase anim_data = {NULL, NULL};
961         bAnimListElem *ale;
962         int filter;
963         
964         KeyframeEditFunc select_cb, ok_cb;
965         KeyframeEditData ked = {{NULL}};
966         
967         /* set up BezTriple edit callbacks */
968         select_cb = ANIM_editkeyframes_select(select_mode);
969         ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAME);
970         
971         /* loop through all of the keys and select additional keyframes
972          * based on the keys found to be selected above
973          */
974         if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
975                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY */ | ANIMFILTER_NODUPLIS);
976         else
977                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS);
978         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
979         
980         for (ale = anim_data.first; ale; ale = ale->next) {
981                 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
982                 
983                 /* set frame for validation callback to refer to */
984                 if (adt)
985                         ked.f1 = BKE_nla_tweakedit_remap(adt, selx, NLATIME_CONVERT_UNMAP);
986                 else
987                         ked.f1 = selx;
988                 
989                 /* select elements with frame number matching cfra */
990                 if (ale->type == ANIMTYPE_GPLAYER)
991                         ED_gpencil_select_frame(ale->key_data, selx, select_mode);
992                 else if (ale->type == ANIMTYPE_MASKLAYER)
993                         ED_mask_select_frame(ale->key_data, selx, select_mode);
994                 else
995                         ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
996         }
997         
998         /* free elements */
999         BLI_freelistN(&ked.list);
1000         BLI_freelistN(&anim_data);
1001 }
1002  
1003 /* ------------------- */
1004
1005 static void mouse_action_keys(bAnimContext *ac, const int mval[2], short select_mode, short column)
1006 {
1007         ListBase anim_data = {NULL, NULL};
1008         DLRBT_Tree anim_keys;
1009         bAnimListElem *ale;
1010         int filter;
1011         
1012         View2D *v2d = &ac->ar->v2d;
1013         bDopeSheet *ads = NULL;
1014         int channel_index;
1015         short found = 0;
1016         float selx = 0.0f;
1017         float x, y;
1018         rctf rectf;
1019         
1020         /* get dopesheet info */
1021         if (ac->datatype == ANIMCONT_DOPESHEET)
1022                 ads = ac->data;
1023         
1024         /* use View2D to determine the index of the channel (i.e a row in the list) where keyframe was */
1025         UI_view2d_region_to_view(v2d, mval[0], mval[1], &x, &y);
1026         UI_view2d_listview_view_to_cell(v2d, 0, ACHANNEL_STEP, 0, (float)ACHANNEL_HEIGHT_HALF, x, y, NULL, &channel_index);
1027         
1028         /* x-range to check is +/- 7 (in screen/region-space) on either side of mouse click (size of keyframe icon) */
1029         UI_view2d_region_to_view(v2d, mval[0] - 7, mval[1], &rectf.xmin, &rectf.ymin);
1030         UI_view2d_region_to_view(v2d, mval[0] + 7, mval[1], &rectf.xmax, &rectf.ymax);
1031         
1032         /* filter data */
1033         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
1034         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1035         
1036         /* try to get channel */
1037         ale = BLI_findlink(&anim_data, channel_index);
1038         if (ale == NULL) {
1039                 /* channel not found */
1040                 printf("Error: animation channel (index = %d) not found in mouse_action_keys()\n", channel_index);
1041                 BLI_freelistN(&anim_data);
1042                 return;
1043         }
1044         else {
1045                 /* found match - must return here... */
1046                 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
1047                 ActKeyColumn *ak, *akn = NULL;
1048                 
1049                 /* make list of keyframes */
1050                 BLI_dlrbTree_init(&anim_keys);
1051                 
1052                 if (ale->key_data) {
1053                         switch (ale->datatype) {
1054                                 case ALE_SCE:
1055                                 {
1056                                         Scene *scene = (Scene *)ale->key_data;
1057                                         scene_to_keylist(ads, scene, &anim_keys, NULL);
1058                                 }
1059                                 break;
1060                                 case ALE_OB:
1061                                 {
1062                                         Object *ob = (Object *)ale->key_data;
1063                                         ob_to_keylist(ads, ob, &anim_keys, NULL);
1064                                 }
1065                                 break;
1066                                 case ALE_ACT:
1067                                 {
1068                                         bAction *act = (bAction *)ale->key_data;
1069                                         action_to_keylist(adt, act, &anim_keys, NULL);
1070                                 }
1071                                 break;
1072                                 case ALE_FCURVE:
1073                                 {
1074                                         FCurve *fcu = (FCurve *)ale->key_data;
1075                                         fcurve_to_keylist(adt, fcu, &anim_keys, NULL);
1076                                 }
1077                                 break;
1078                         }
1079                 }
1080                 else if (ale->type == ANIMTYPE_SUMMARY) {
1081                         /* dopesheet summary covers everything */
1082                         summary_to_keylist(ac, &anim_keys, NULL);
1083                 }
1084                 else if (ale->type == ANIMTYPE_GROUP) {
1085                         // TODO: why don't we just give groups key_data too?
1086                         bActionGroup *agrp = (bActionGroup *)ale->data;
1087                         agroup_to_keylist(adt, agrp, &anim_keys, NULL);
1088                 }
1089                 else if (ale->type == ANIMTYPE_GPLAYER) {
1090                         // TODO: why don't we just give gplayers key_data too?
1091                         bGPDlayer *gpl = (bGPDlayer *)ale->data;
1092                         gpl_to_keylist(ads, gpl, &anim_keys);
1093                 }
1094                 else if (ale->type == ANIMTYPE_MASKLAYER) {
1095                         // TODO: why don't we just give masklayers key_data too?
1096                         MaskLayer *masklay = (MaskLayer *)ale->data;
1097                         mask_to_keylist(ads, masklay, &anim_keys);
1098                 }
1099
1100                 /* start from keyframe at root of BST, traversing until we find one within the range that was clicked on */
1101                 for (ak = anim_keys.root; ak; ak = akn) {
1102                         if (IN_RANGE(ak->cfra, rectf.xmin, rectf.xmax)) {
1103                                 /* set the frame to use, and apply inverse-correction for NLA-mapping 
1104                                  * so that the frame will get selected by the selection functions without
1105                                  * requiring to map each frame once again...
1106                                  */
1107                                 selx = BKE_nla_tweakedit_remap(adt, ak->cfra, NLATIME_CONVERT_UNMAP);
1108                                 found = 1;
1109                                 break;
1110                         }
1111                         else if (ak->cfra < rectf.xmin)
1112                                 akn = ak->right;
1113                         else
1114                                 akn = ak->left;
1115                 }
1116                 
1117                 /* remove active channel from list of channels for separate treatment (since it's needed later on) */
1118                 BLI_remlink(&anim_data, ale);
1119                 
1120                 /* cleanup temporary lists */
1121                 BLI_dlrbTree_free(&anim_keys);
1122                 
1123                 /* free list of channels, since it's not used anymore */
1124                 BLI_freelistN(&anim_data);
1125         }
1126         
1127         /* for replacing selection, firstly need to clear existing selection */
1128         if (select_mode == SELECT_REPLACE) {
1129                 /* reset selection mode for next steps */
1130                 select_mode = SELECT_ADD;
1131                 
1132                 /* deselect all keyframes */
1133                 deselect_action_keys(ac, 0, SELECT_SUBTRACT);
1134                 
1135                 /* highlight channel clicked on */
1136                 if (ELEM(ac->datatype, ANIMCONT_ACTION, ANIMCONT_DOPESHEET)) {
1137                         /* deselect all other channels first */
1138                         ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
1139                         
1140                         /* Highlight Action-Group or F-Curve? */
1141                         if (ale && ale->data) {
1142                                 if (ale->type == ANIMTYPE_GROUP) {
1143                                         bActionGroup *agrp = ale->data;
1144                                         
1145                                         agrp->flag |= AGRP_SELECTED;
1146                                         ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, agrp, ANIMTYPE_GROUP);
1147                                 }
1148                                 else if (ale->type == ANIMTYPE_FCURVE) {
1149                                         FCurve *fcu = ale->data;
1150                                         
1151                                         fcu->flag |= FCURVE_SELECTED;
1152                                         ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, fcu, ANIMTYPE_FCURVE);
1153                                 }
1154                         }
1155                 }
1156                 else if (ac->datatype == ANIMCONT_GPENCIL) {
1157                         /* deselect all other channels first */
1158                         ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
1159                         
1160                         /* Highlight GPencil Layer */
1161                         if ((ale && ale->data) && (ale->type == ANIMTYPE_GPLAYER)) {
1162                                 bGPDlayer *gpl = ale->data;
1163                                 
1164                                 gpl->flag |= GP_LAYER_SELECT;
1165                                 //gpencil_layer_setactive(gpd, gpl);
1166                         }
1167                 }
1168                 else if (ac->datatype == ANIMCONT_MASK) {
1169                         /* deselect all other channels first */
1170                         ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
1171
1172                         /* Highlight GPencil Layer */
1173                         if ((ale && ale->data) && (ale->type == ANIMTYPE_MASKLAYER)) {
1174                                 MaskLayer *masklay = ale->data;
1175
1176                                 masklay->flag |= MASK_LAYERFLAG_SELECT;
1177                                 //gpencil_layer_setactive(gpd, gpl);
1178                         }
1179                 }
1180         }
1181         
1182         /* only select keyframes if we clicked on a valid channel and hit something */
1183         if (ale) {
1184                 if (found) {
1185                         /* apply selection to keyframes */
1186                         if (column) {
1187                                 /* select all keyframes in the same frame as the one we hit on the active channel */
1188                                 actkeys_mselect_column(ac, select_mode, selx);
1189                         }
1190                         else {
1191                                 /* select the nominated keyframe on the given frame */
1192                                 actkeys_mselect_single(ac, ale, select_mode, selx);
1193                         }
1194                 }
1195                 
1196                 /* free this channel */
1197                 MEM_freeN(ale);
1198         }
1199 }
1200
1201 /* handle clicking */
1202 static int actkeys_clickselect_invoke(bContext *C, wmOperator *op, wmEvent *event)
1203 {
1204         bAnimContext ac;
1205         /* ARegion *ar; */ /* UNUSED */
1206         short selectmode, column;
1207         
1208         /* get editor data */
1209         if (ANIM_animdata_get_context(C, &ac) == 0)
1210                 return OPERATOR_CANCELLED;
1211                 
1212         /* get useful pointers from animation context data */
1213         /* ar = ac.ar; */ /* UNUSED */
1214
1215         /* select mode is either replace (deselect all, then add) or add/extend */
1216         if (RNA_boolean_get(op->ptr, "extend"))
1217                 selectmode = SELECT_INVERT;
1218         else
1219                 selectmode = SELECT_REPLACE;
1220                 
1221         /* column selection */
1222         column = RNA_boolean_get(op->ptr, "column");
1223         
1224         /* select keyframe(s) based upon mouse position*/
1225         mouse_action_keys(&ac, event->mval, selectmode, column);
1226         
1227         /* set notifier that keyframe selection (and channels too) have changed */
1228         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | ND_ANIMCHAN | NA_SELECTED, NULL);
1229         
1230         /* for tweak grab to work */
1231         return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
1232 }
1233  
1234 void ACTION_OT_clickselect(wmOperatorType *ot)
1235 {
1236         PropertyRNA *prop;
1237         
1238         /* identifiers */
1239         ot->name = "Mouse Select Keys";
1240         ot->idname = "ACTION_OT_clickselect";
1241         ot->description = "Select keyframes by clicking on them";
1242         
1243         /* api callbacks - absolutely no exec() this yet... */
1244         ot->invoke = actkeys_clickselect_invoke;
1245         ot->poll = ED_operator_action_active;
1246         
1247         /* flags */
1248         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1249         
1250         /* properties */
1251         prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", ""); // SHIFTKEY
1252         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1253         prop = RNA_def_boolean(ot->srna, "column", 0, "Column Select", ""); // ALTKEY
1254         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1255 }
1256
1257 /* ************************************************************************** */