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