NLA SoC: Notifier Fixes for Animation Editors
[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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, 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 #include <math.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <float.h>
31
32 #ifdef HAVE_CONFIG_H
33 #include <config.h>
34 #endif
35
36 #include "MEM_guardedalloc.h"
37
38 #include "BLI_blenlib.h"
39 #include "BLI_arithb.h"
40
41 #include "DNA_anim_types.h"
42 #include "DNA_action_types.h"
43 #include "DNA_armature_types.h"
44 #include "DNA_camera_types.h"
45 #include "DNA_curve_types.h"
46 #include "DNA_object_types.h"
47 #include "DNA_screen_types.h"
48 #include "DNA_scene_types.h"
49 #include "DNA_space_types.h"
50 #include "DNA_constraint_types.h"
51 #include "DNA_key_types.h"
52 #include "DNA_lamp_types.h"
53 #include "DNA_material_types.h"
54 #include "DNA_userdef_types.h"
55 #include "DNA_gpencil_types.h"
56 #include "DNA_windowmanager_types.h"
57 #include "DNA_world_types.h"
58
59 #include "RNA_access.h"
60 #include "RNA_define.h"
61
62 #include "BKE_action.h"
63 #include "BKE_depsgraph.h"
64 #include "BKE_fcurve.h"
65 #include "BKE_key.h"
66 #include "BKE_material.h"
67 #include "BKE_nla.h"
68 #include "BKE_object.h"
69 #include "BKE_context.h"
70 #include "BKE_utildefines.h"
71
72 #include "UI_view2d.h"
73
74 #include "ED_anim_api.h"
75 #include "ED_keyframing.h"
76 #include "ED_keyframes_draw.h"
77 #include "ED_keyframes_edit.h"
78 #include "ED_markers.h"
79 #include "ED_screen.h"
80 #include "ED_space_api.h"
81
82 #include "WM_api.h"
83 #include "WM_types.h"
84
85 #include "action_intern.h"
86
87 /* ************************************************************************** */
88 /* KEYFRAMES STUFF */
89
90 /* ******************** Deselect All Operator ***************************** */
91 /* This operator works in one of three ways:
92  *      1) (de)select all (AKEY) - test if select all or deselect all
93  *      2) invert all (CTRL-IKEY) - invert selection of all keyframes
94  *      3) (de)select all - no testing is done; only for use internal tools as normal function...
95  */
96
97 /* Deselects keyframes in the action editor
98  *      - This is called by the deselect all operator, as well as other ones!
99  *
100  *      - test: check if select or deselect all
101  *      - sel: how to select keyframes 
102  *              0 = deselect
103  *              1 = select
104  *              2 = invert
105  */
106 static void deselect_action_keys (bAnimContext *ac, short test, short sel)
107 {
108         ListBase anim_data = {NULL, NULL};
109         bAnimListElem *ale;
110         int filter;
111         
112         BeztEditData bed;
113         BeztEditFunc test_cb, sel_cb;
114         
115         /* determine type-based settings */
116         if (ac->datatype == ANIMCONT_GPENCIL)
117                 filter= (ANIMFILTER_VISIBLE);
118         else
119                 filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVESONLY);
120         
121         /* filter data */
122         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
123         
124         /* init BezTriple looping data */
125         memset(&bed, 0, sizeof(BeztEditData));
126         test_cb= ANIM_editkeyframes_ok(BEZT_OK_SELECTED);
127         
128         /* See if we should be selecting or deselecting */
129         if (test) {
130                 for (ale= anim_data.first; ale; ale= ale->next) {
131                         if (ale->type == ANIMTYPE_GPLAYER) {
132                                 //if (is_gplayer_frame_selected(ale->data)) {
133                                 //      sel= 0;
134                                 //      break;
135                                 //}
136                         }
137                         else {
138                                 if (ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, NULL, test_cb, NULL)) {
139                                         sel= SELECT_SUBTRACT;
140                                         break;
141                                 }
142                         }
143                 }
144         }
145         
146         /* convert sel to selectmode, and use that to get editor */
147         sel_cb= ANIM_editkeyframes_select(sel);
148         
149         /* Now set the flags */
150         for (ale= anim_data.first; ale; ale= ale->next) {
151                 //if (ale->type == ACTTYPE_GPLAYER)
152                 //      set_gplayer_frame_selection(ale->data, sel);
153                 //else
154                         ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, NULL, sel_cb, NULL);
155         }
156         
157         /* Cleanup */
158         BLI_freelistN(&anim_data);
159 }
160
161 /* ------------------- */
162
163 static int actkeys_deselectall_exec(bContext *C, wmOperator *op)
164 {
165         bAnimContext ac;
166         
167         /* get editor data */
168         if (ANIM_animdata_get_context(C, &ac) == 0)
169                 return OPERATOR_CANCELLED;
170                 
171         /* 'standard' behaviour - check if selected, then apply relevant selection */
172         if (RNA_boolean_get(op->ptr, "invert"))
173                 deselect_action_keys(&ac, 0, SELECT_INVERT);
174         else
175                 deselect_action_keys(&ac, 1, SELECT_ADD);
176         
177         /* set notifier that keyframe selection have changed */
178         WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_SELECT, NULL);
179         
180         return OPERATOR_FINISHED;
181 }
182  
183 void ACT_OT_select_all_toggle (wmOperatorType *ot)
184 {
185         /* identifiers */
186         ot->name= "Select All";
187         ot->idname= "ACT_OT_select_all_toggle";
188         
189         /* api callbacks */
190         ot->exec= actkeys_deselectall_exec;
191         ot->poll= ED_operator_action_active;
192         
193         /* flags */
194         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
195         
196         /* props */
197         RNA_def_boolean(ot->srna, "invert", 0, "Invert", "");
198 }
199
200 /* ******************** Border Select Operator **************************** */
201 /* This operator currently works in one of three ways:
202  *      -> BKEY         - 1) all keyframes within region are selected (ACTKEYS_BORDERSEL_ALLKEYS)
203  *      -> ALT-BKEY - depending on which axis of the region was larger...
204  *              -> 2) x-axis, so select all frames within frame range (ACTKEYS_BORDERSEL_FRAMERANGE)
205  *              -> 3) y-axis, so select all frames within channels that region included (ACTKEYS_BORDERSEL_CHANNELS)
206  */
207
208 /* defines for borderselect mode */
209 enum {
210         ACTKEYS_BORDERSEL_ALLKEYS       = 0,
211         ACTKEYS_BORDERSEL_FRAMERANGE,
212         ACTKEYS_BORDERSEL_CHANNELS,
213 } eActKeys_BorderSelect_Mode;
214
215
216 static void borderselect_action (bAnimContext *ac, rcti rect, short mode, short selectmode)
217 {
218         ListBase anim_data = {NULL, NULL};
219         bAnimListElem *ale;
220         int filter;
221         
222         BeztEditData bed;
223         BeztEditFunc ok_cb, select_cb;
224         View2D *v2d= &ac->ar->v2d;
225         rctf rectf;
226         //float ymin=0, ymax=(float)(-ACHANNEL_HEIGHT);
227         float ymin=0, ymax=(float)(-ACHANNEL_HEIGHT_HALF);
228         
229         /* convert mouse coordinates to frame ranges and channel coordinates corrected for view pan/zoom */
230         UI_view2d_region_to_view(v2d, rect.xmin, rect.ymin+2, &rectf.xmin, &rectf.ymin);
231         UI_view2d_region_to_view(v2d, rect.xmax, rect.ymax-2, &rectf.xmax, &rectf.ymax);
232         
233         /* filter data */
234         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CHANNELS);
235         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
236         
237         /* get beztriple editing/validation funcs  */
238         select_cb= ANIM_editkeyframes_select(selectmode);
239         
240         if (ELEM(mode, ACTKEYS_BORDERSEL_FRAMERANGE, ACTKEYS_BORDERSEL_ALLKEYS))
241                 ok_cb= ANIM_editkeyframes_ok(BEZT_OK_FRAMERANGE);
242         else
243                 ok_cb= NULL;
244                 
245         /* init editing data */
246         memset(&bed, 0, sizeof(BeztEditData));
247         
248         /* loop over data, doing border select */
249         for (ale= anim_data.first; ale; ale= ale->next) {
250                 AnimData *adt= ANIM_nla_mapping_get(ac, ale);
251                 
252                 /* get new vertical minimum extent of channel */
253                 ymin= ymax - ACHANNEL_STEP;
254                 
255                 /* set horizontal range (if applicable) */
256                 if (ELEM(mode, ACTKEYS_BORDERSEL_FRAMERANGE, ACTKEYS_BORDERSEL_ALLKEYS)) {
257                         /* if channel is mapped in NLA, apply correction */
258                         if (adt) {
259                                 bed.f1= BKE_nla_tweakedit_remap(adt, rectf.xmin, NLATIME_CONVERT_UNMAP);
260                                 bed.f2= BKE_nla_tweakedit_remap(adt, rectf.xmax, NLATIME_CONVERT_UNMAP);
261                         }
262                         else {
263                                 bed.f1= rectf.xmin;
264                                 bed.f2= rectf.xmax;
265                         }
266                 }
267                 
268                 /* perform vertical suitability check (if applicable) */
269                 if ( (mode == ACTKEYS_BORDERSEL_FRAMERANGE) || 
270                         !((ymax < rectf.ymin) || (ymin > rectf.ymax)) )
271                 {
272                         /* loop over data selecting */
273                         if (ale->key_data) {
274                                 if (ale->datatype == ALE_FCURVE)
275                                         ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, ok_cb, select_cb, NULL);
276                         }
277                         else if (ale->type == ANIMTYPE_GROUP) {
278                                 bActionGroup *agrp= ale->data;
279                                 FCurve *fcu;
280                                 
281                                 for (fcu= agrp->channels.first; fcu && fcu->grp==agrp; fcu= fcu->next) 
282                                         ANIM_fcurve_keys_bezier_loop(&bed, fcu, ok_cb, select_cb, NULL);
283                         }
284                         //else if (ale->type == ANIMTYPE_GPLAYER) {
285                         //      borderselect_gplayer_frames(ale->data, rectf.xmin, rectf.xmax, selectmode);
286                         //}
287                 }
288                 
289                 /* set minimum extent to be the maximum of the next channel */
290                 ymax=ymin;
291         }
292         
293         /* cleanup */
294         BLI_freelistN(&anim_data);
295 }
296
297 /* ------------------- */
298
299 static int actkeys_borderselect_exec(bContext *C, wmOperator *op)
300 {
301         bAnimContext ac;
302         rcti rect;
303         short mode=0, selectmode=0;
304         int event;
305         
306         /* get editor data */
307         if (ANIM_animdata_get_context(C, &ac) == 0)
308                 return OPERATOR_CANCELLED;
309         
310         /* get settings from operator */
311         rect.xmin= RNA_int_get(op->ptr, "xmin");
312         rect.ymin= RNA_int_get(op->ptr, "ymin");
313         rect.xmax= RNA_int_get(op->ptr, "xmax");
314         rect.ymax= RNA_int_get(op->ptr, "ymax");
315                 
316         event= RNA_int_get(op->ptr, "event_type");
317         if (event == LEFTMOUSE) // FIXME... hardcoded
318                 selectmode = SELECT_ADD;
319         else
320                 selectmode = SELECT_SUBTRACT;
321         
322         /* selection 'mode' depends on whether borderselect region only matters on one axis */
323         if (RNA_boolean_get(op->ptr, "axis_range")) {
324                 /* mode depends on which axis of the range is larger to determine which axis to use 
325                  *      - checking this in region-space is fine, as it's fundamentally still going to be a different rect size
326                  *      - the frame-range select option is favoured over the channel one (x over y), as frame-range one is often
327                  *        used for tweaking timing when "blocking", while channels is not that useful...
328                  */
329                 if ((rect.xmax - rect.xmin) >= (rect.ymax - rect.ymin))
330                         mode= ACTKEYS_BORDERSEL_FRAMERANGE;
331                 else
332                         mode= ACTKEYS_BORDERSEL_CHANNELS;
333         }
334         else 
335                 mode= ACTKEYS_BORDERSEL_ALLKEYS;
336         
337         /* apply borderselect action */
338         borderselect_action(&ac, rect, mode, selectmode);
339         
340         /* set notifier that keyframe selection have changed */
341         WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_SELECT, NULL);
342         
343         return OPERATOR_FINISHED;
344
345
346 void ACT_OT_select_border(wmOperatorType *ot)
347 {
348         /* identifiers */
349         ot->name= "Border Select";
350         ot->idname= "ACT_OT_select_border";
351         
352         /* api callbacks */
353         ot->invoke= WM_border_select_invoke;
354         ot->exec= actkeys_borderselect_exec;
355         ot->modal= WM_border_select_modal;
356         
357         ot->poll= ED_operator_action_active;
358         
359         /* flags */
360         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
361         
362         /* rna */
363         RNA_def_int(ot->srna, "event_type", 0, INT_MIN, INT_MAX, "Event Type", "", INT_MIN, INT_MAX);
364         RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
365         RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
366         RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
367         RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
368         
369         RNA_def_boolean(ot->srna, "axis_range", 0, "Axis Range", "");
370 }
371
372 /* ******************** Column Select Operator **************************** */
373 /* This operator works in one of four ways:
374  *      - 1) select all keyframes in the same frame as a selected one  (KKEY)
375  *      - 2) select all keyframes in the same frame as the current frame marker (CTRL-KKEY)
376  *      - 3) select all keyframes in the same frame as a selected markers (SHIFT-KKEY)
377  *      - 4) select all keyframes that occur between selected markers (ALT-KKEY)
378  */
379
380 /* defines for column-select mode */
381 static EnumPropertyItem prop_column_select_types[] = {
382         {ACTKEYS_COLUMNSEL_KEYS, "KEYS", 0, "On Selected Keyframes", ""},
383         {ACTKEYS_COLUMNSEL_CFRA, "CFRA", 0, "On Current Frame", ""},
384         {ACTKEYS_COLUMNSEL_MARKERS_COLUMN, "MARKERS_COLUMN", 0, "On Selected Markers", ""},
385         {ACTKEYS_COLUMNSEL_MARKERS_BETWEEN, "MARKERS_BETWEEN", 0, "Between Min/Max Selected Markers", ""},
386         {0, NULL, 0, NULL, NULL}
387 };
388
389 /* ------------------- */ 
390
391 /* Selects all visible keyframes between the specified markers */
392 static void markers_selectkeys_between (bAnimContext *ac)
393 {
394         ListBase anim_data = {NULL, NULL};
395         bAnimListElem *ale;
396         int filter;
397         
398         BeztEditFunc ok_cb, select_cb;
399         BeztEditData bed;
400         float min, max;
401         
402         /* get extreme markers */
403         ED_markers_get_minmax(ac->markers, 1, &min, &max);
404         min -= 0.5f;
405         max += 0.5f;
406         
407         /* get editing funcs + data */
408         ok_cb= ANIM_editkeyframes_ok(BEZT_OK_FRAMERANGE);
409         select_cb= ANIM_editkeyframes_select(SELECT_ADD);
410         
411         memset(&bed, 0, sizeof(BeztEditData));
412         bed.f1= min; 
413         bed.f2= max;
414         
415         /* filter data */
416         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVESONLY);
417         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
418         
419         /* select keys in-between */
420         for (ale= anim_data.first; ale; ale= ale->next) {
421                 AnimData *adt= ANIM_nla_mapping_get(ac, ale);
422                 
423                 if (adt) {      
424                         ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1);
425                         ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, ok_cb, select_cb, NULL);
426                         ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
427                 }
428                 else {
429                         ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, ok_cb, select_cb, NULL);
430                 }
431         }
432         
433         /* Cleanup */
434         BLI_freelistN(&anim_data);
435 }
436
437
438 /* Selects all visible keyframes in the same frames as the specified elements */
439 static void columnselect_action_keys (bAnimContext *ac, short mode)
440 {
441         ListBase anim_data= {NULL, NULL};
442         bAnimListElem *ale;
443         int filter;
444         
445         Scene *scene= ac->scene;
446         CfraElem *ce;
447         BeztEditFunc select_cb, ok_cb;
448         BeztEditData bed;
449         
450         /* initialise keyframe editing data */
451         memset(&bed, 0, sizeof(BeztEditData));
452         
453         /* build list of columns */
454         switch (mode) {
455                 case ACTKEYS_COLUMNSEL_KEYS: /* list of selected keys */
456                         if (ac->datatype == ANIMCONT_GPENCIL) {
457                                 filter= (ANIMFILTER_VISIBLE);
458                                 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
459                                 
460                                 //for (ale= anim_data.first; ale; ale= ale->next)
461                                 //      gplayer_make_cfra_list(ale->data, &elems, 1);
462                         }
463                         else {
464                                 filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVESONLY);
465                                 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
466                                 
467                                 for (ale= anim_data.first; ale; ale= ale->next)
468                                         ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, NULL, bezt_to_cfraelem, NULL);
469                         }
470                         BLI_freelistN(&anim_data);
471                         break;
472                         
473                 case ACTKEYS_COLUMNSEL_CFRA: /* current frame */
474                         /* make a single CfraElem for storing this */
475                         ce= MEM_callocN(sizeof(CfraElem), "cfraElem");
476                         BLI_addtail(&bed.list, ce);
477                         
478                         ce->cfra= (float)CFRA;
479                         break;
480                         
481                 case ACTKEYS_COLUMNSEL_MARKERS_COLUMN: /* list of selected markers */
482                         ED_markers_make_cfra_list(ac->markers, &bed.list, 1);
483                         break;
484                         
485                 default: /* invalid option */
486                         return;
487         }
488         
489         /* set up BezTriple edit callbacks */
490         select_cb= ANIM_editkeyframes_select(SELECT_ADD);
491         ok_cb= ANIM_editkeyframes_ok(BEZT_OK_FRAME);
492         
493         /* loop through all of the keys and select additional keyframes
494          * based on the keys found to be selected above
495          */
496         if (ac->datatype == ANIMCONT_GPENCIL)
497                 filter= (ANIMFILTER_VISIBLE);
498         else
499                 filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVESONLY);
500         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
501         
502         for (ale= anim_data.first; ale; ale= ale->next) {
503                 AnimData *adt= ANIM_nla_mapping_get(ac, ale);
504                 
505                 /* loop over cfraelems (stored in the BeztEditData->list)
506                  *      - we need to do this here, as we can apply fewer NLA-mapping conversions
507                  */
508                 for (ce= bed.list.first; ce; ce= ce->next) {
509                         /* set frame for validation callback to refer to */
510                         if (adt)
511                                 bed.f1= BKE_nla_tweakedit_remap(adt, ce->cfra, NLATIME_CONVERT_UNMAP);
512                         else
513                                 bed.f1= ce->cfra;
514                         
515                         /* select elements with frame number matching cfraelem */
516                         ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, ok_cb, select_cb, NULL);
517                         
518 #if 0 // XXX reenable when Grease Pencil stuff is back
519                         if (ale->type == ANIMTYPE_GPLAYER) {
520                                 bGPDlayer *gpl= (bGPDlayer *)ale->data;
521                                 bGPDframe *gpf;
522                                 
523                                 for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
524                                         if (ecfra == gpf->framenum) 
525                                                 gpf->flag |= GP_FRAME_SELECT;
526                                 }
527                         }
528                         //else... 
529 #endif // XXX reenable when Grease Pencil stuff is back
530                 }
531         }
532         
533         /* free elements */
534         BLI_freelistN(&bed.list);
535         BLI_freelistN(&anim_data);
536 }
537
538 /* ------------------- */
539
540 static int actkeys_columnselect_exec(bContext *C, wmOperator *op)
541 {
542         bAnimContext ac;
543         short mode;
544         
545         /* get editor data */
546         if (ANIM_animdata_get_context(C, &ac) == 0)
547                 return OPERATOR_CANCELLED;
548                 
549         /* action to take depends on the mode */
550         mode= RNA_enum_get(op->ptr, "mode");
551         
552         if (mode == ACTKEYS_COLUMNSEL_MARKERS_BETWEEN)
553                 markers_selectkeys_between(&ac);
554         else
555                 columnselect_action_keys(&ac, mode);
556         
557         /* set notifier that keyframe selection have changed */
558         WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_SELECT, NULL);
559         
560         return OPERATOR_FINISHED;
561 }
562  
563 void ACT_OT_select_column (wmOperatorType *ot)
564 {
565         /* identifiers */
566         ot->name= "Select All";
567         ot->idname= "ACT_OT_select_column";
568         
569         /* api callbacks */
570         ot->exec= actkeys_columnselect_exec;
571         ot->poll= ED_operator_action_active;
572         
573         /* flags */
574         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
575         
576         /* props */
577         RNA_def_enum(ot->srna, "mode", prop_column_select_types, 0, "Mode", "");
578 }
579
580 /* ******************** Mouse-Click Select Operator *********************** */
581 /* This operator works in one of three ways:
582  *      - 1) keyframe under mouse - no special modifiers
583  *      - 2) all keyframes on the same side of current frame indicator as mouse - ALT modifier
584  *      - 3) column select all keyframes in frame under mouse - CTRL modifier
585  *
586  * In addition to these basic options, the SHIFT modifier can be used to toggle the 
587  * selection mode between replacing the selection (without) and inverting the selection (with).
588  */
589
590 /* defines for left-right select tool */
591 static EnumPropertyItem prop_actkeys_leftright_select_types[] = {
592         {ACTKEYS_LRSEL_TEST, "CHECK", 0, "Check if Select Left or Right", ""},
593         {ACTKEYS_LRSEL_NONE, "OFF", 0, "Don't select", ""},
594         {ACTKEYS_LRSEL_LEFT, "LEFT", 0, "Before current frame", ""},
595         {ACTKEYS_LRSEL_RIGHT, "RIGHT", 0, "After current frame", ""},
596         {0, NULL, 0, NULL, NULL}
597 };
598
599 /* sensitivity factor for frame-selections */
600 #define FRAME_CLICK_THRESH              0.1f
601
602 /* ------------------- */
603  
604 /* option 1) select keyframe directly under mouse */
605 static void actkeys_mselect_single (bAnimContext *ac, bAnimListElem *ale, short select_mode, float selx)
606 {
607         bDopeSheet *ads= (ac->datatype == ANIMCONT_DOPESHEET) ? ac->data : NULL;
608         int ds_filter = ((ads) ? (ads->filterflag) : (0));
609         
610         BeztEditData bed;
611         BeztEditFunc select_cb, ok_cb;
612         
613         /* get functions for selecting keyframes */
614         select_cb= ANIM_editkeyframes_select(select_mode);
615         ok_cb= ANIM_editkeyframes_ok(BEZT_OK_FRAME);
616         memset(&bed, 0, sizeof(BeztEditData)); 
617         bed.f1= selx;
618         
619         /* select the nominated keyframe on the given frame */
620         ANIM_animchannel_keys_bezier_loop(&bed, ale, ok_cb, select_cb, NULL, ds_filter);
621 }
622
623 /* Option 2) Selects all the keyframes on either side of the current frame (depends on which side the mouse is on) */
624 static void actkeys_mselect_leftright (bAnimContext *ac, short leftright, short select_mode)
625 {
626         ListBase anim_data = {NULL, NULL};
627         bAnimListElem *ale;
628         int filter;
629         
630         BeztEditFunc ok_cb, select_cb;
631         BeztEditData bed;
632         Scene *scene= ac->scene;
633         
634         /* if select mode is replace, deselect all keyframes (and channels) first */
635         if (select_mode==SELECT_REPLACE) {
636                 select_mode= SELECT_ADD;
637                 
638                 /* deselect all other channels and keyframes */
639                 ANIM_deselect_anim_channels(ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
640                 deselect_action_keys(ac, 0, SELECT_SUBTRACT);
641         }
642         
643         /* set callbacks and editing data */
644         ok_cb= ANIM_editkeyframes_ok(BEZT_OK_FRAMERANGE);
645         select_cb= ANIM_editkeyframes_select(select_mode);
646         
647         memset(&bed, 0, sizeof(BeztEditFunc));
648         if (leftright == ACTKEYS_LRSEL_LEFT) {
649                 bed.f1 = MINAFRAMEF;
650                 bed.f2 = (float)(CFRA + FRAME_CLICK_THRESH);
651         } 
652         else {
653                 bed.f1 = (float)(CFRA - FRAME_CLICK_THRESH);
654                 bed.f2 = MAXFRAMEF;
655         }
656         
657         /* filter data */
658         if (ac->datatype == ANIMCONT_GPENCIL)
659                 filter= (ANIMFILTER_VISIBLE);
660         else
661                 filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVESONLY);
662         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
663                 
664         /* select keys on the side where most data occurs */
665         for (ale= anim_data.first; ale; ale= ale->next) {
666                 AnimData *adt= ANIM_nla_mapping_get(ac, ale);
667                 
668                 if (adt) {
669                         ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1);
670                         ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, ok_cb, select_cb, NULL);
671                         ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
672                 }
673                 //else if (ale->type == ANIMTYPE_GPLAYER)
674                 //      borderselect_gplayer_frames(ale->data, min, max, SELECT_ADD);
675                 else
676                         ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, ok_cb, select_cb, NULL);
677         }
678         
679         /* Cleanup */
680         BLI_freelistN(&anim_data);
681 }
682
683 /* Option 3) Selects all visible keyframes in the same frame as the mouse click */
684 static void actkeys_mselect_column(bAnimContext *ac, short select_mode, float selx)
685 {
686         ListBase anim_data= {NULL, NULL};
687         bAnimListElem *ale;
688         int filter;
689         
690         BeztEditFunc select_cb, ok_cb;
691         BeztEditData bed;
692         
693         /* initialise keyframe editing data */
694         memset(&bed, 0, sizeof(BeztEditData));
695         
696         /* set up BezTriple edit callbacks */
697         select_cb= ANIM_editkeyframes_select(select_mode);
698         ok_cb= ANIM_editkeyframes_ok(BEZT_OK_FRAME);
699         
700         /* loop through all of the keys and select additional keyframes
701          * based on the keys found to be selected above
702          */
703         if (ac->datatype == ANIMCONT_GPENCIL)
704                 filter= (ANIMFILTER_VISIBLE);
705         else
706                 filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVESONLY);
707         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
708         
709         for (ale= anim_data.first; ale; ale= ale->next) {
710                 AnimData *adt= ANIM_nla_mapping_get(ac, ale);
711                 
712                 /* set frame for validation callback to refer to */
713                 if (adt)
714                         bed.f1= BKE_nla_tweakedit_remap(adt, selx, NLATIME_CONVERT_UNMAP);
715                 else
716                         bed.f1= selx;
717                 
718                 /* select elements with frame number matching cfra */
719                 ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, ok_cb, select_cb, NULL);
720                         
721 #if 0 // XXX reenable when Grease Pencil stuff is back
722                         if (ale->type == ANIMTYPE_GPLAYER) {
723                                 bGPDlayer *gpl= (bGPDlayer *)ale->data;
724                                 bGPDframe *gpf;
725                                 
726                                 for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
727                                         if (ecfra == gpf->framenum) 
728                                                 gpf->flag |= GP_FRAME_SELECT;
729                                 }
730                         }
731                         //else... 
732 #endif // XXX reenable when Grease Pencil stuff is back
733         }
734         
735         /* free elements */
736         BLI_freelistN(&bed.list);
737         BLI_freelistN(&anim_data);
738 }
739  
740 /* ------------------- */
741
742 static void mouse_action_keys (bAnimContext *ac, int mval[2], short select_mode, short column)
743 {
744         ListBase anim_data = {NULL, NULL};
745         ListBase anim_keys = {NULL, NULL};
746         bAnimListElem *ale;
747         int filter;
748         
749         View2D *v2d= &ac->ar->v2d;
750         bDopeSheet *ads = NULL;
751         int channel_index;
752         short found = 0;
753         float selx = 0.0f;
754         float x, y;
755         rctf rectf;
756         
757         /* get dopesheet info */
758         if (ac->datatype == ANIMCONT_DOPESHEET)
759                 ads= ac->data;
760         
761         /* use View2D to determine the index of the channel (i.e a row in the list) where keyframe was */
762         UI_view2d_region_to_view(v2d, mval[0], mval[1], &x, &y);
763         UI_view2d_listview_view_to_cell(v2d, 0, ACHANNEL_STEP, 0, (float)ACHANNEL_HEIGHT_HALF, x, y, NULL, &channel_index);
764         
765         /* x-range to check is +/- 7 (in screen/region-space) on either side of mouse click (size of keyframe icon) */
766         UI_view2d_region_to_view(v2d, mval[0]-7, mval[1], &rectf.xmin, &rectf.ymin);
767         UI_view2d_region_to_view(v2d, mval[0]+7, mval[1], &rectf.xmax, &rectf.ymax);
768         
769         /* filter data */
770         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CHANNELS);
771         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
772         
773         /* try to get channel */
774         ale= BLI_findlink(&anim_data, channel_index);
775         if (ale == NULL) {
776                 /* channel not found */
777                 printf("Error: animation channel (index = %d) not found in mouse_action_keys() \n", channel_index);
778                 BLI_freelistN(&anim_data);
779                 return;
780         }
781         else {
782                 /* found match - must return here... */
783                 AnimData *adt= ANIM_nla_mapping_get(ac, ale);
784                 ActKeyColumn *ak;
785                 
786                 /* make list of keyframes */
787                 // TODO: it would be great if we didn't have to apply this to all the keyframes to do this...
788                 if (ale->key_data) {
789                         switch (ale->datatype) {
790                                 case ALE_OB:
791                                 {
792                                         Object *ob= (Object *)ale->key_data;
793                                         ob_to_keylist(ads, ob, &anim_keys, NULL);
794                                 }
795                                         break;
796                                 case ALE_ACT:
797                                 {
798                                         bAction *act= (bAction *)ale->key_data;
799                                         action_to_keylist(adt, act, &anim_keys, NULL);
800                                 }
801                                         break;
802                                 case ALE_FCURVE:
803                                 {
804                                         FCurve *fcu= (FCurve *)ale->key_data;
805                                         fcurve_to_keylist(adt, fcu, &anim_keys, NULL);
806                                 }
807                                         break;
808                         }
809                 }
810                 else if (ale->type == ANIMTYPE_GROUP) {
811                         bActionGroup *agrp= (bActionGroup *)ale->data;
812                         agroup_to_keylist(adt, agrp, &anim_keys, NULL);
813                 }
814                 else if (ale->type == ANIMTYPE_GPDATABLOCK) {
815                         /* cleanup */
816                         // FIXME:...
817                         BLI_freelistN(&anim_data);
818                         return;
819                 }
820                 else if (ale->type == ANIMTYPE_GPLAYER) {
821                         bGPDlayer *gpl= (bGPDlayer *)ale->data;
822                         gpl_to_keylist(ads, gpl, &anim_keys, NULL);
823                 }
824                 
825                 /* loop through keyframes, finding one that was clicked on */
826                 for (ak= anim_keys.first; ak; ak= ak->next) {
827                         if (IN_RANGE(ak->cfra, rectf.xmin, rectf.xmax)) {
828                                 /* set the frame to use, and apply inverse-correction for NLA-mapping 
829                                  * so that the frame will get selected by the selection functiosn without
830                                  * requiring to map each frame once again...
831                                  */
832                                 selx= BKE_nla_tweakedit_remap(adt, ak->cfra, NLATIME_CONVERT_UNMAP);
833                                 found= 1;
834                                 break;
835                         }
836                 }
837                 
838                 /* remove active channel from list of channels for separate treatment (since it's needed later on) */
839                 BLI_remlink(&anim_data, ale);
840                 
841                 /* cleanup temporary lists */
842                 BLI_freelistN(&anim_keys);
843                 anim_keys.first = anim_keys.last = NULL;
844                 
845                 /* free list of channels, since it's not used anymore */
846                 BLI_freelistN(&anim_data);
847         }
848         
849         /* for replacing selection, firstly need to clear existing selection */
850         if (select_mode == SELECT_REPLACE) {
851                 /* reset selection mode for next steps */
852                 select_mode = SELECT_ADD;
853                 
854                 /* deselect all keyframes */
855                 deselect_action_keys(ac, 0, SELECT_SUBTRACT);
856                 
857                 /* highlight channel clicked on */
858                 if (ELEM(ac->datatype, ANIMCONT_ACTION, ANIMCONT_DOPESHEET)) {
859                         /* deselect all other channels first */
860                         ANIM_deselect_anim_channels(ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
861                         
862                         /* Highlight Action-Group or F-Curve? */
863                         if (ale && ale->data) {
864                                 if (ale->type == ANIMTYPE_GROUP) {
865                                         bActionGroup *agrp= ale->data;
866                                         
867                                         agrp->flag |= AGRP_SELECTED;
868                                         ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, agrp, ANIMTYPE_GROUP);
869                                 }       
870                                 else if (ale->type == ANIMTYPE_FCURVE) {
871                                         FCurve *fcu= ale->data;
872                                         
873                                         fcu->flag |= FCURVE_SELECTED;
874                                         ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, fcu, ANIMTYPE_FCURVE);
875                                 }
876                         }
877                 }
878                 else if (ac->datatype == ANIMCONT_GPENCIL) {
879                         ANIM_deselect_anim_channels(ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
880                         
881                         /* Highlight gpencil layer */
882                         //gpl->flag |= GP_LAYER_SELECT;
883                         //gpencil_layer_setactive(gpd, gpl);
884                 }
885         }
886         
887         /* only select keyframes if we clicked on a valid channel and hit something */
888         if (ale) {
889                 if (found) {
890                         /* apply selection to keyframes */
891                         if (/*gpl*/0) {
892                                 /* grease pencil */
893                                 //select_gpencil_frame(gpl, (int)selx, selectmode);
894                         }
895                         else if (column) {
896                                 /* select all keyframes in the same frame as the one we hit on the active channel */
897                                 actkeys_mselect_column(ac, select_mode, selx);
898                         }
899                         else {
900                                 /* select the nominated keyframe on the given frame */
901                                 actkeys_mselect_single(ac, ale, select_mode, selx);
902                         }
903                 }
904                 
905                 /* free this channel */
906                 MEM_freeN(ale);
907         }
908 }
909
910 /* handle clicking */
911 static int actkeys_clickselect_invoke(bContext *C, wmOperator *op, wmEvent *event)
912 {
913         bAnimContext ac;
914         Scene *scene;
915         ARegion *ar;
916         View2D *v2d;
917         short selectmode, column;
918         int mval[2];
919         
920         /* get editor data */
921         if (ANIM_animdata_get_context(C, &ac) == 0)
922                 return OPERATOR_CANCELLED;
923                 
924         /* get useful pointers from animation context data */
925         scene= ac.scene;
926         ar= ac.ar;
927         v2d= &ar->v2d;
928         
929         /* get mouse coordinates (in region coordinates) */
930         mval[0]= (event->x - ar->winrct.xmin);
931         mval[1]= (event->y - ar->winrct.ymin);
932         
933         /* select mode is either replace (deselect all, then add) or add/extend */
934         if (RNA_boolean_get(op->ptr, "extend"))
935                 selectmode= SELECT_INVERT;
936         else
937                 selectmode= SELECT_REPLACE;
938                 
939         /* column selection */
940         column= RNA_boolean_get(op->ptr, "column");
941         
942         /* figure out action to take */
943         if (RNA_enum_get(op->ptr, "left_right")) {
944                 /* select all keys on same side of current frame as mouse */
945                 float x;
946                 
947                 UI_view2d_region_to_view(v2d, mval[0], mval[1], &x, NULL);
948                 if (x < CFRA)
949                         RNA_int_set(op->ptr, "left_right", ACTKEYS_LRSEL_LEFT);
950                 else    
951                         RNA_int_set(op->ptr, "left_right", ACTKEYS_LRSEL_RIGHT);
952                 
953                 actkeys_mselect_leftright(&ac, RNA_enum_get(op->ptr, "left_right"), selectmode);
954         }
955         else {
956                 /* select keyframe(s) based upon mouse position*/
957                 mouse_action_keys(&ac, mval, selectmode, column);
958         }
959         
960         /* set notifier that keyframe selection (and channels too) have changed */
961         WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_SELECT|ND_ANIMCHAN_SELECT, NULL);
962         
963         /* for tweak grab to work */
964         return OPERATOR_FINISHED|OPERATOR_PASS_THROUGH;
965 }
966  
967 void ACT_OT_clickselect (wmOperatorType *ot)
968 {
969         /* identifiers */
970         ot->name= "Mouse Select Keys";
971         ot->idname= "ACT_OT_clickselect";
972         
973         /* api callbacks - absolutely no exec() this yet... */
974         ot->invoke= actkeys_clickselect_invoke;
975         ot->poll= ED_operator_action_active;
976         
977         /* flags */
978         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
979         
980         /* id-props */
981         // XXX should we make this into separate operators?
982         RNA_def_enum(ot->srna, "left_right", prop_actkeys_leftright_select_types, 0, "Left Right", ""); // CTRLKEY
983         RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", ""); // SHIFTKEY
984         RNA_def_boolean(ot->srna, "column", 0, "Column Select", ""); // ALTKEY
985 }
986
987 /* ************************************************************************** */