Merging r50049 through r50076 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_RCT_SIZE_X(&rect) >= BLI_RCT_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 /* sensitivity factor for frame-selections */
916 #define FRAME_CLICK_THRESH      0.1f
917
918 /* ------------------- */
919  
920 /* option 1) select keyframe directly under mouse */
921 static void actkeys_mselect_single(bAnimContext *ac, bAnimListElem *ale, short select_mode, float selx)
922 {
923         KeyframeEditData ked = {{NULL}};
924         KeyframeEditFunc select_cb, ok_cb;
925         
926         /* get functions for selecting keyframes */
927         select_cb = ANIM_editkeyframes_select(select_mode);
928         ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAME);
929         ked.f1 = selx;
930         
931         /* select the nominated keyframe on the given frame */
932         if (ale->type == ANIMTYPE_GPLAYER)
933                 ED_gpencil_select_frame(ale->data, selx, select_mode);
934         else if (ale->type == ANIMTYPE_MASKLAYER)
935                 ED_mask_select_frame(ale->data, selx, select_mode);
936         else {
937                 if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK) &&
938                     (ale->type == ANIMTYPE_SUMMARY) && (ale->datatype == ALE_ALL))
939                 {
940                         ListBase anim_data = {NULL, NULL};
941                         int filter;
942                         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY */ | ANIMFILTER_NODUPLIS);
943                         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
944                         for (ale = anim_data.first; ale; ale = ale->next) {
945                                 if (ale->type == ANIMTYPE_GPLAYER)
946                                         ED_gpencil_select_frame(ale->data, selx, select_mode);
947                                 else if (ale->type == ANIMTYPE_MASKLAYER)
948                                         ED_mask_select_frame(ale->data, selx, select_mode);
949                         }
950                 }
951                 else {
952                         ANIM_animchannel_keyframes_loop(&ked, ac->ads, ale, ok_cb, select_cb, NULL);
953                 }
954         }
955 }
956
957 /* Option 2) Selects all the keyframes on either side of the current frame (depends on which side the mouse is on) */
958 /* (see actkeys_select_leftright) */
959
960 /* Option 3) Selects all visible keyframes in the same frame as the mouse click */
961 static void actkeys_mselect_column(bAnimContext *ac, short select_mode, float selx)
962 {
963         ListBase anim_data = {NULL, NULL};
964         bAnimListElem *ale;
965         int filter;
966         
967         KeyframeEditFunc select_cb, ok_cb;
968         KeyframeEditData ked = {{NULL}};
969         
970         /* set up BezTriple edit callbacks */
971         select_cb = ANIM_editkeyframes_select(select_mode);
972         ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAME);
973         
974         /* loop through all of the keys and select additional keyframes
975          * based on the keys found to be selected above
976          */
977         if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
978                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY */ | ANIMFILTER_NODUPLIS);
979         else
980                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS);
981         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
982         
983         for (ale = anim_data.first; ale; ale = ale->next) {
984                 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
985                 
986                 /* set frame for validation callback to refer to */
987                 if (adt)
988                         ked.f1 = BKE_nla_tweakedit_remap(adt, selx, NLATIME_CONVERT_UNMAP);
989                 else
990                         ked.f1 = selx;
991                 
992                 /* select elements with frame number matching cfra */
993                 if (ale->type == ANIMTYPE_GPLAYER)
994                         ED_gpencil_select_frame(ale->key_data, selx, select_mode);
995                 else if (ale->type == ANIMTYPE_MASKLAYER)
996                         ED_mask_select_frame(ale->key_data, selx, select_mode);
997                 else
998                         ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
999         }
1000         
1001         /* free elements */
1002         BLI_freelistN(&ked.list);
1003         BLI_freelistN(&anim_data);
1004 }
1005  
1006 /* ------------------- */
1007
1008 static void mouse_action_keys(bAnimContext *ac, const int mval[2], short select_mode, short column)
1009 {
1010         ListBase anim_data = {NULL, NULL};
1011         DLRBT_Tree anim_keys;
1012         bAnimListElem *ale;
1013         int filter;
1014         
1015         View2D *v2d = &ac->ar->v2d;
1016         bDopeSheet *ads = NULL;
1017         int channel_index;
1018         short found = 0;
1019         float selx = 0.0f;
1020         float x, y;
1021         rctf rectf;
1022         
1023         /* get dopesheet info */
1024         if (ac->datatype == ANIMCONT_DOPESHEET)
1025                 ads = ac->data;
1026         
1027         /* use View2D to determine the index of the channel (i.e a row in the list) where keyframe was */
1028         UI_view2d_region_to_view(v2d, mval[0], mval[1], &x, &y);
1029         UI_view2d_listview_view_to_cell(v2d, 0, ACHANNEL_STEP, 0, (float)ACHANNEL_HEIGHT_HALF, x, y, NULL, &channel_index);
1030         
1031         /* x-range to check is +/- 7 (in screen/region-space) on either side of mouse click (size of keyframe icon) */
1032         UI_view2d_region_to_view(v2d, mval[0] - 7, mval[1], &rectf.xmin, &rectf.ymin);
1033         UI_view2d_region_to_view(v2d, mval[0] + 7, mval[1], &rectf.xmax, &rectf.ymax);
1034         
1035         /* filter data */
1036         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
1037         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1038         
1039         /* try to get channel */
1040         ale = BLI_findlink(&anim_data, channel_index);
1041         if (ale == NULL) {
1042                 /* channel not found */
1043                 printf("Error: animation channel (index = %d) not found in mouse_action_keys()\n", channel_index);
1044                 BLI_freelistN(&anim_data);
1045                 return;
1046         }
1047         else {
1048                 /* found match - must return here... */
1049                 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
1050                 ActKeyColumn *ak, *akn = NULL;
1051                 
1052                 /* make list of keyframes */
1053                 BLI_dlrbTree_init(&anim_keys);
1054                 
1055                 if (ale->key_data) {
1056                         switch (ale->datatype) {
1057                                 case ALE_SCE:
1058                                 {
1059                                         Scene *scene = (Scene *)ale->key_data;
1060                                         scene_to_keylist(ads, scene, &anim_keys, NULL);
1061                                 }
1062                                 break;
1063                                 case ALE_OB:
1064                                 {
1065                                         Object *ob = (Object *)ale->key_data;
1066                                         ob_to_keylist(ads, ob, &anim_keys, NULL);
1067                                 }
1068                                 break;
1069                                 case ALE_ACT:
1070                                 {
1071                                         bAction *act = (bAction *)ale->key_data;
1072                                         action_to_keylist(adt, act, &anim_keys, NULL);
1073                                 }
1074                                 break;
1075                                 case ALE_FCURVE:
1076                                 {
1077                                         FCurve *fcu = (FCurve *)ale->key_data;
1078                                         fcurve_to_keylist(adt, fcu, &anim_keys, NULL);
1079                                 }
1080                                 break;
1081                         }
1082                 }
1083                 else if (ale->type == ANIMTYPE_SUMMARY) {
1084                         /* dopesheet summary covers everything */
1085                         summary_to_keylist(ac, &anim_keys, NULL);
1086                 }
1087                 else if (ale->type == ANIMTYPE_GROUP) { 
1088                         // TODO: why don't we just give groups key_data too?
1089                         bActionGroup *agrp = (bActionGroup *)ale->data;
1090                         agroup_to_keylist(adt, agrp, &anim_keys, NULL);
1091                 }
1092                 else if (ale->type == ANIMTYPE_GPLAYER) {
1093                         // TODO: why don't we just give gplayers key_data too?
1094                         bGPDlayer *gpl = (bGPDlayer *)ale->data;
1095                         gpl_to_keylist(ads, gpl, &anim_keys);
1096                 }
1097                 else if (ale->type == ANIMTYPE_MASKLAYER) {
1098                         // TODO: why don't we just give masklayers key_data too?
1099                         MaskLayer *masklay = (MaskLayer *)ale->data;
1100                         mask_to_keylist(ads, masklay, &anim_keys);
1101                 }
1102
1103                 /* start from keyframe at root of BST, traversing until we find one within the range that was clicked on */
1104                 for (ak = anim_keys.root; ak; ak = akn) {
1105                         if (IN_RANGE(ak->cfra, rectf.xmin, rectf.xmax)) {
1106                                 /* set the frame to use, and apply inverse-correction for NLA-mapping 
1107                                  * so that the frame will get selected by the selection functions without
1108                                  * requiring to map each frame once again...
1109                                  */
1110                                 selx = BKE_nla_tweakedit_remap(adt, ak->cfra, NLATIME_CONVERT_UNMAP);
1111                                 found = 1;
1112                                 break;
1113                         }
1114                         else if (ak->cfra < rectf.xmin)
1115                                 akn = ak->right;
1116                         else
1117                                 akn = ak->left;
1118                 }
1119                 
1120                 /* remove active channel from list of channels for separate treatment (since it's needed later on) */
1121                 BLI_remlink(&anim_data, ale);
1122                 
1123                 /* cleanup temporary lists */
1124                 BLI_dlrbTree_free(&anim_keys);
1125                 
1126                 /* free list of channels, since it's not used anymore */
1127                 BLI_freelistN(&anim_data);
1128         }
1129         
1130         /* for replacing selection, firstly need to clear existing selection */
1131         if (select_mode == SELECT_REPLACE) {
1132                 /* reset selection mode for next steps */
1133                 select_mode = SELECT_ADD;
1134                 
1135                 /* deselect all keyframes */
1136                 deselect_action_keys(ac, 0, SELECT_SUBTRACT);
1137                 
1138                 /* highlight channel clicked on */
1139                 if (ELEM(ac->datatype, ANIMCONT_ACTION, ANIMCONT_DOPESHEET)) {
1140                         /* deselect all other channels first */
1141                         ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
1142                         
1143                         /* Highlight Action-Group or F-Curve? */
1144                         if (ale && ale->data) {
1145                                 if (ale->type == ANIMTYPE_GROUP) {
1146                                         bActionGroup *agrp = ale->data;
1147                                         
1148                                         agrp->flag |= AGRP_SELECTED;
1149                                         ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, agrp, ANIMTYPE_GROUP);
1150                                 }       
1151                                 else if (ale->type == ANIMTYPE_FCURVE) {
1152                                         FCurve *fcu = ale->data;
1153                                         
1154                                         fcu->flag |= FCURVE_SELECTED;
1155                                         ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, fcu, ANIMTYPE_FCURVE);
1156                                 }
1157                         }
1158                 }
1159                 else if (ac->datatype == ANIMCONT_GPENCIL) {
1160                         /* deselect all other channels first */
1161                         ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
1162                         
1163                         /* Highlight GPencil Layer */
1164                         if ((ale && ale->data) && (ale->type == ANIMTYPE_GPLAYER)) {
1165                                 bGPDlayer *gpl = ale->data;
1166                                 
1167                                 gpl->flag |= GP_LAYER_SELECT;
1168                                 //gpencil_layer_setactive(gpd, gpl);
1169                         }
1170                 }
1171                 else if (ac->datatype == ANIMCONT_MASK) {
1172                         /* deselect all other channels first */
1173                         ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
1174
1175                         /* Highlight GPencil Layer */
1176                         if ((ale && ale->data) && (ale->type == ANIMTYPE_MASKLAYER)) {
1177                                 MaskLayer *masklay = ale->data;
1178
1179                                 masklay->flag |= MASK_LAYERFLAG_SELECT;
1180                                 //gpencil_layer_setactive(gpd, gpl);
1181                         }
1182                 }
1183         }
1184         
1185         /* only select keyframes if we clicked on a valid channel and hit something */
1186         if (ale) {
1187                 if (found) {
1188                         /* apply selection to keyframes */
1189                         if (column) {
1190                                 /* select all keyframes in the same frame as the one we hit on the active channel */
1191                                 actkeys_mselect_column(ac, select_mode, selx);
1192                         }
1193                         else {
1194                                 /* select the nominated keyframe on the given frame */
1195                                 actkeys_mselect_single(ac, ale, select_mode, selx);
1196                         }
1197                 }
1198                 
1199                 /* free this channel */
1200                 MEM_freeN(ale);
1201         }
1202 }
1203
1204 /* handle clicking */
1205 static int actkeys_clickselect_invoke(bContext *C, wmOperator *op, wmEvent *event)
1206 {
1207         bAnimContext ac;
1208         /* ARegion *ar; */ /* UNUSED */
1209         short selectmode, column;
1210         
1211         /* get editor data */
1212         if (ANIM_animdata_get_context(C, &ac) == 0)
1213                 return OPERATOR_CANCELLED;
1214                 
1215         /* get useful pointers from animation context data */
1216         /* ar= ac.ar; */ /* UNUSED */
1217
1218         /* select mode is either replace (deselect all, then add) or add/extend */
1219         if (RNA_boolean_get(op->ptr, "extend"))
1220                 selectmode = SELECT_INVERT;
1221         else
1222                 selectmode = SELECT_REPLACE;
1223                 
1224         /* column selection */
1225         column = RNA_boolean_get(op->ptr, "column");
1226         
1227         /* select keyframe(s) based upon mouse position*/
1228         mouse_action_keys(&ac, event->mval, selectmode, column);
1229         
1230         /* set notifier that keyframe selection (and channels too) have changed */
1231         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | ND_ANIMCHAN | NA_SELECTED, NULL);
1232         
1233         /* for tweak grab to work */
1234         return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
1235 }
1236  
1237 void ACTION_OT_clickselect(wmOperatorType *ot)
1238 {
1239         PropertyRNA *prop;
1240         
1241         /* identifiers */
1242         ot->name = "Mouse Select Keys";
1243         ot->idname = "ACTION_OT_clickselect";
1244         ot->description = "Select keyframes by clicking on them";
1245         
1246         /* api callbacks - absolutely no exec() this yet... */
1247         ot->invoke = actkeys_clickselect_invoke;
1248         ot->poll = ED_operator_action_active;
1249         
1250         /* flags */
1251         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1252         
1253         /* properties */
1254         prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", ""); // SHIFTKEY
1255         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1256         prop = RNA_def_boolean(ot->srna, "column", 0, "Column Select", ""); // ALTKEY
1257         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1258 }
1259
1260 /* ************************************************************************** */