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