First merge with 2.5 branch.
[blender.git] / source / blender / editors / animation / keyframes_edit.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 <stdlib.h>
28 #include <string.h>
29 #include <math.h>
30 #include <float.h>
31
32 #include "MEM_guardedalloc.h"
33
34 #include "BLI_blenlib.h"
35 #include "BLI_arithb.h"
36
37 #include "DNA_anim_types.h"
38 #include "DNA_action_types.h"
39 #include "DNA_curve_types.h"
40 #include "DNA_key_types.h"
41 #include "DNA_object_types.h"
42 #include "DNA_space_types.h"
43 #include "DNA_scene_types.h"
44 #include "DNA_world_types.h"
45
46 #include "BKE_action.h"
47 #include "BKE_fcurve.h"
48 #include "BKE_key.h"
49 #include "BKE_utildefines.h"
50
51 #include "ED_anim_api.h"
52 #include "ED_keyframes_edit.h"
53 #include "ED_markers.h"
54
55 /* This file defines an API and set of callback-operators for non-destructive editing of keyframe data.
56  *
57  * Two API functions are defined for actually performing the operations on the data:
58  *                      ANIM_fcurve_keys_bezier_loop()
59  * which take the data they operate on, a few callbacks defining what operations to perform.
60  *
61  * As operators which work on keyframes usually apply the same operation on all BezTriples in 
62  * every channel, the code has been optimised providing a set of functions which will get the 
63  * appropriate bezier-modify function to set. These functions (ANIM_editkeyframes_*) will need
64  * to be called before getting any channels.
65  * 
66  * A set of 'validation' callbacks are provided for checking if a BezTriple should be operated on.
67  * These should only be used when using a 'general' BezTriple editor (i.e. selection setters which 
68  * don't check existing selection status).
69  * 
70  * - Joshua Leung, Dec 2008
71  */
72
73 /* ************************************************************************** */
74 /* Keyframe Editing Loops - Exposed API */
75
76 /* --------------------------- Base Functions ------------------------------------ */
77
78 /* This function is used to loop over BezTriples in the given F-Curve, applying a given 
79  * operation on them, and optionally applies an F-Curve validation function afterwards.
80  */
81 short ANIM_fcurve_keys_bezier_loop(BeztEditData *bed, FCurve *fcu, BeztEditFunc bezt_ok, BeztEditFunc bezt_cb, FcuEditFunc fcu_cb) 
82 {
83     BezTriple *bezt;
84         int b;
85         
86         /* sanity check */
87         if (ELEM(NULL, fcu, fcu->bezt))
88                 return 0;
89         
90         /* if function to apply to bezier curves is set, then loop through executing it on beztriples */
91     if (bezt_cb) {
92                 /* if there's a validation func, include that check in the loop 
93                  * (this is should be more efficient than checking for it in every loop)
94                  */
95                 if (bezt_ok) {
96                         for (b=0, bezt=fcu->bezt; b < fcu->totvert; b++, bezt++) {
97                                 /* Only operate on this BezTriple if it fullfills the criteria of the validation func */
98                                 if (bezt_ok(bed, bezt)) {
99                                         /* Exit with return-code '1' if function returns positive
100                                          * This is useful if finding if some BezTriple satisfies a condition.
101                                          */
102                                 if (bezt_cb(bed, bezt)) return 1;
103                                 }
104                         }
105                 }
106                 else {
107                         for (b=0, bezt=fcu->bezt; b < fcu->totvert; b++, bezt++) {
108                                 /* Exit with return-code '1' if function returns positive
109                                  * This is useful if finding if some BezTriple satisfies a condition.
110                                  */
111                         if (bezt_cb(bed, bezt)) return 1;
112                         }
113                 }
114     }
115
116     /* if fcu_cb (F-Curve post-editing callback) has been specified then execute it */
117     if (fcu_cb)
118         fcu_cb(fcu);
119         
120         /* done */      
121     return 0;
122 }
123
124 /* -------------------------------- Further Abstracted (Not Exposed Directly) ----------------------------- */
125
126 /* This function is used to loop over the keyframe data in an Action Group */
127 static short agrp_keys_bezier_loop(BeztEditData *bed, bActionGroup *agrp, BeztEditFunc bezt_ok, BeztEditFunc bezt_cb, FcuEditFunc fcu_cb)
128 {
129         FCurve *fcu;
130         
131         /* only iterate over the F-Curves that are in this group */
132         for (fcu= agrp->channels.first; fcu && fcu->grp==agrp; fcu= fcu->next) {
133                 if (ANIM_fcurve_keys_bezier_loop(bed, fcu, bezt_ok, bezt_cb, fcu_cb))
134                         return 1;
135         }
136         
137         return 0;
138 }
139
140 /* This function is used to loop over the keyframe data in an Action */
141 static short act_keys_bezier_loop(BeztEditData *bed, bAction *act, BeztEditFunc bezt_ok, BeztEditFunc bezt_cb, FcuEditFunc fcu_cb)
142 {
143         FCurve *fcu;
144         
145         /* just loop through all F-Curves */
146         for (fcu= act->curves.first; fcu; fcu= fcu->next) {
147                 if (ANIM_fcurve_keys_bezier_loop(bed, fcu, bezt_ok, bezt_cb, fcu_cb))
148                         return 1;
149         }
150         
151         return 0;
152 }
153
154 /* This function is used to loop over the keyframe data of an AnimData block */
155 static short adt_keys_bezier_loop(BeztEditData *bed, AnimData *adt, BeztEditFunc bezt_ok, BeztEditFunc bezt_cb, FcuEditFunc fcu_cb, int filterflag)
156 {
157         /* drivers or actions? */
158         if (filterflag & ADS_FILTER_ONLYDRIVERS) {
159                 FCurve *fcu;
160                 
161                 /* just loop through all F-Curves acting as Drivers */
162                 for (fcu= adt->drivers.first; fcu; fcu= fcu->next) {
163                         if (ANIM_fcurve_keys_bezier_loop(bed, fcu, bezt_ok, bezt_cb, fcu_cb))
164                                 return 1;
165                 }
166         }
167         else if (adt->action) {
168                 /* call the function for actions */
169                 if (act_keys_bezier_loop(bed, adt->action, bezt_ok, bezt_cb, fcu_cb))
170                         return 1;
171         }
172         
173         return 0;
174 }
175
176 /* This function is used to loop over the keyframe data in an Object */
177 static short ob_keys_bezier_loop(BeztEditData *bed, Object *ob, BeztEditFunc bezt_ok, BeztEditFunc bezt_cb, FcuEditFunc fcu_cb, int filterflag)
178 {
179         Key *key= ob_get_key(ob);
180         
181         /* firstly, Object's own AnimData */
182         if (ob->adt) 
183                 adt_keys_bezier_loop(bed, ob->adt, bezt_ok, bezt_cb, fcu_cb, filterflag);
184         
185         /* shapekeys */
186         if ((key && key->adt) && !(filterflag & ADS_FILTER_NOSHAPEKEYS))
187                 adt_keys_bezier_loop(bed, key->adt, bezt_ok, bezt_cb, fcu_cb, filterflag);
188                 
189         // FIXME: add materials, etc. (but drawing code doesn't do it yet too! :)
190         
191         return 0;
192 }
193
194 /* This function is used to loop over the keyframe data in a Scene */
195 static short scene_keys_bezier_loop(BeztEditData *bed, Scene *sce, BeztEditFunc bezt_ok, BeztEditFunc bezt_cb, FcuEditFunc fcu_cb, int filterflag)
196 {
197         World *wo= sce->world;
198         
199         /* Scene's own animation */
200         if (sce->adt)
201                 adt_keys_bezier_loop(bed, sce->adt, bezt_ok, bezt_cb, fcu_cb, filterflag);
202         
203         /* World */
204         if (wo && wo->adt)
205                 adt_keys_bezier_loop(bed, wo->adt, bezt_ok, bezt_cb, fcu_cb, filterflag);
206         
207         return 0;
208 }
209
210 /* --- */
211
212 /* This function is used to apply operation to all keyframes, regardless of the type */
213 short ANIM_animchannel_keys_bezier_loop(BeztEditData *bed, bAnimListElem *ale, BeztEditFunc bezt_ok, BeztEditFunc bezt_cb, FcuEditFunc fcu_cb, int filterflag)
214 {
215         /* sanity checks */
216         if (ale == NULL)
217                 return 0;
218         
219         /* method to use depends on the type of keyframe data */
220         switch (ale->datatype) {
221                 /* direct keyframe data (these loops are exposed) */
222                 case ALE_FCURVE: /* F-Curve */
223                         return ANIM_fcurve_keys_bezier_loop(bed, ale->key_data, bezt_ok, bezt_cb, fcu_cb);
224                 
225                 /* indirect 'summaries' (these are not exposed directly) 
226                  * NOTE: must keep this code in sync with the drawing code and also the filtering code!
227                  */
228                 case ALE_GROUP: /* action group */
229                         return agrp_keys_bezier_loop(bed, (bActionGroup *)ale->data, bezt_ok, bezt_cb, fcu_cb);
230                 case ALE_ACT: /* action */
231                         return act_keys_bezier_loop(bed, (bAction *)ale->data, bezt_ok, bezt_cb, fcu_cb);
232                         
233                 case ALE_OB: /* object */
234                         return ob_keys_bezier_loop(bed, (Object *)ale->data, bezt_ok, bezt_cb, fcu_cb, filterflag);
235                 case ALE_SCE: /* scene */
236                         return scene_keys_bezier_loop(bed, (Scene *)ale->data, bezt_ok, bezt_cb, fcu_cb, filterflag);
237         }
238         
239         return 0;
240 }
241
242 /* This function is used to apply operation to all keyframes, regardless of the type without needed an AnimListElem wrapper */
243 short ANIM_animchanneldata_keys_bezier_loop(BeztEditData *bed, void *data, int keytype, BeztEditFunc bezt_ok, BeztEditFunc bezt_cb, FcuEditFunc fcu_cb, int filterflag)
244 {
245         /* sanity checks */
246         if (data == NULL)
247                 return 0;
248         
249         /* method to use depends on the type of keyframe data */
250         switch (keytype) {
251                 /* direct keyframe data (these loops are exposed) */
252                 case ALE_FCURVE: /* F-Curve */
253                         return ANIM_fcurve_keys_bezier_loop(bed, data, bezt_ok, bezt_cb, fcu_cb);
254                 
255                 /* indirect 'summaries' (these are not exposed directly) 
256                  * NOTE: must keep this code in sync with the drawing code and also the filtering code!
257                  */
258                 case ALE_GROUP: /* action group */
259                         return agrp_keys_bezier_loop(bed, (bActionGroup *)data, bezt_ok, bezt_cb, fcu_cb);
260                 case ALE_ACT: /* action */
261                         return act_keys_bezier_loop(bed, (bAction *)data, bezt_ok, bezt_cb, fcu_cb);
262                         
263                 case ALE_OB: /* object */
264                         return ob_keys_bezier_loop(bed, (Object *)data, bezt_ok, bezt_cb, fcu_cb, filterflag);
265                 case ALE_SCE: /* scene */
266                         return scene_keys_bezier_loop(bed, (Scene *)data, bezt_ok, bezt_cb, fcu_cb, filterflag);
267         }
268         
269         return 0;
270 }
271
272 /* ************************************************************************** */
273 /* Keyframe Integrity Tools */
274
275 /* Rearrange keyframes if some are out of order */
276 // used to be recalc_*_ipos() where * was object or action
277 void ANIM_editkeyframes_refresh(bAnimContext *ac)
278 {
279         ListBase anim_data = {NULL, NULL};
280         bAnimListElem *ale;
281         int filter;
282         
283         /* filter animation data */
284         filter= ANIMFILTER_CURVESONLY; 
285         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
286         
287         /* loop over ipo-curves that are likely to have been edited, and check them */
288         for (ale= anim_data.first; ale; ale= ale->next) {
289                 FCurve *fcu= ale->key_data;
290                 
291                 /* make sure keyframes in F-curve are all in order, and handles are in valid positions */
292                 sort_time_fcurve(fcu);
293                 testhandles_fcurve(fcu);
294         }
295         
296         /* free temp data */
297         BLI_freelistN(&anim_data);
298 }
299
300 /* ************************************************************************** */
301 /* BezTriple Validation Callbacks */
302
303 static short ok_bezier_frame(BeztEditData *bed, BezTriple *bezt)
304 {
305         /* frame is stored in f1 property (this float accuracy check may need to be dropped?) */
306         return IS_EQ(bezt->vec[1][0], bed->f1);
307 }
308
309 static short ok_bezier_framerange(BeztEditData *bed, BezTriple *bezt)
310 {
311         /* frame range is stored in float properties */
312         return ((bezt->vec[1][0] > bed->f1) && (bezt->vec[1][0] < bed->f2));
313 }
314
315 static short ok_bezier_selected(BeztEditData *bed, BezTriple *bezt)
316 {
317         /* this macro checks all beztriple handles for selection... */
318         return BEZSELECTED(bezt);
319 }
320
321 static short ok_bezier_value(BeztEditData *bed, BezTriple *bezt)
322 {
323         /* value is stored in f1 property 
324          *      - this float accuracy check may need to be dropped?
325          *      - should value be stored in f2 instead so that we won't have conflicts when using f1 for frames too?
326          */
327         return IS_EQ(bezt->vec[1][1], bed->f1);
328 }
329
330 static short ok_bezier_valuerange(BeztEditData *bed, BezTriple *bezt)
331 {
332         /* value range is stored in float properties */
333         return ((bezt->vec[1][1] > bed->f1) && (bezt->vec[1][1] < bed->f2));
334 }
335
336 static short ok_bezier_region(BeztEditData *bed, BezTriple *bezt)
337 {
338         /* rect is stored in data property (it's of type rectf, but may not be set) */
339         if (bed->data)
340                 return BLI_in_rctf(bed->data, bezt->vec[1][0], bezt->vec[1][1]);
341         else 
342                 return 0;
343 }
344
345
346 BeztEditFunc ANIM_editkeyframes_ok(short mode)
347 {
348         /* eEditKeyframes_Validate */
349         switch (mode) {
350                 case BEZT_OK_FRAME: /* only if bezt falls on the right frame (float) */
351                         return ok_bezier_frame;
352                 case BEZT_OK_FRAMERANGE: /* only if bezt falls within the specified frame range (floats) */
353                         return ok_bezier_framerange;
354                 case BEZT_OK_SELECTED:  /* only if bezt is selected (self) */
355                         return ok_bezier_selected;
356                 case BEZT_OK_VALUE: /* only if bezt value matches (float) */
357                         return ok_bezier_value;
358                 case BEZT_OK_VALUERANGE: /* only if bezier falls within the specified value range (floats) */
359                         return ok_bezier_valuerange;
360                 case BEZT_OK_REGION: /* only if bezier falls within the specified rect (data -> rectf) */
361                         return ok_bezier_region;
362                 default: /* nothing was ok */
363                         return NULL;
364         }
365 }
366
367 /* ******************************************* */
368 /* Assorted Utility Functions */
369
370 /* helper callback for <animeditor>_cfrasnap_exec() -> used to help get the average time of all selected beztriples */
371 short bezt_calc_average(BeztEditData *bed, BezTriple *bezt)
372 {
373         /* only if selected */
374         if (bezt->f2 & SELECT) {
375                 /* store average time in float (only do rounding at last step */
376                 bed->f1 += bezt->vec[1][0];
377                 
378                 /* increment number of items */
379                 bed->i1++;
380         }
381         
382         return 0;
383 }
384
385 /* ******************************************* */
386 /* Transform */
387
388 static short snap_bezier_nearest(BeztEditData *bed, BezTriple *bezt)
389 {
390         if (bezt->f2 & SELECT)
391                 bezt->vec[1][0]= (float)(floor(bezt->vec[1][0]+0.5));
392         return 0;
393 }
394
395 static short snap_bezier_nearestsec(BeztEditData *bed, BezTriple *bezt)
396 {
397         const Scene *scene= bed->scene;
398         const float secf = (float)FPS;
399         
400         if (bezt->f2 & SELECT)
401                 bezt->vec[1][0]= ((float)floor(bezt->vec[1][0]/secf + 0.5f) * secf);
402         return 0;
403 }
404
405 static short snap_bezier_cframe(BeztEditData *bed, BezTriple *bezt)
406 {
407         const Scene *scene= bed->scene;
408         if (bezt->f2 & SELECT)
409                 bezt->vec[1][0]= (float)CFRA;
410         return 0;
411 }
412
413 static short snap_bezier_nearmarker(BeztEditData *bed, BezTriple *bezt)
414 {
415         //if (bezt->f2 & SELECT)
416         //      bezt->vec[1][0]= (float)find_nearest_marker_time(bezt->vec[1][0]);  // XXX missing function!
417         return 0;
418 }
419
420 static short snap_bezier_horizontal(BeztEditData *bed, BezTriple *bezt)
421 {
422         if (bezt->f2 & SELECT) {
423                 bezt->vec[0][1]= bezt->vec[2][1]= bezt->vec[1][1];
424                 if ((bezt->h1==HD_AUTO) || (bezt->h1==HD_VECT)) bezt->h1= HD_ALIGN;
425                 if ((bezt->h2==HD_AUTO) || (bezt->h2==HD_VECT)) bezt->h2= HD_ALIGN;
426         }
427         return 0;       
428 }
429
430
431 BeztEditFunc ANIM_editkeyframes_snap(short type)
432 {
433         /* eEditKeyframes_Snap */
434         switch (type) {
435                 case SNAP_KEYS_NEARFRAME: /* snap to nearest frame */
436                         return snap_bezier_nearest;
437                 case SNAP_KEYS_CURFRAME: /* snap to current frame */
438                         return snap_bezier_cframe;
439                 case SNAP_KEYS_NEARMARKER: /* snap to nearest marker */
440                         return snap_bezier_nearmarker;
441                 case SNAP_KEYS_NEARSEC: /* snap to nearest second */
442                         return snap_bezier_nearestsec;
443                 case SNAP_KEYS_HORIZONTAL: /* snap handles to same value */
444                         return snap_bezier_horizontal;
445                 default: /* just in case */
446                         return snap_bezier_nearest;
447         }
448 }
449
450 /* --------- */
451
452 static short mirror_bezier_cframe(BeztEditData *bed, BezTriple *bezt)
453 {
454         const Scene *scene= bed->scene;
455         float diff;
456         
457         if (bezt->f2 & SELECT) {
458                 diff= ((float)CFRA - bezt->vec[1][0]);
459                 bezt->vec[1][0]= ((float)CFRA + diff);
460         }
461         
462         return 0;
463 }
464
465 static short mirror_bezier_yaxis(BeztEditData *bed, BezTriple *bezt)
466 {
467         float diff;
468         
469         if (bezt->f2 & SELECT) {
470                 diff= (0.0f - bezt->vec[1][0]);
471                 bezt->vec[1][0]= (0.0f + diff);
472         }
473         
474         return 0;
475 }
476
477 static short mirror_bezier_xaxis(BeztEditData *bed, BezTriple *bezt)
478 {
479         float diff;
480         
481         if (bezt->f2 & SELECT) {
482                 diff= (0.0f - bezt->vec[1][1]);
483                 bezt->vec[1][1]= (0.0f + diff);
484         }
485         
486         return 0;
487 }
488
489 static short mirror_bezier_marker(BeztEditData *bed, BezTriple *bezt)
490 {
491         /* mirroring time stored in f1 */
492         if (bezt->f2 & SELECT) {
493                 const float diff= (bed->f1 - bezt->vec[1][0]);
494                 bezt->vec[1][0]= (bed->f1 + diff);
495         }
496         
497         return 0;
498 }
499
500 /* Note: for markers case, need to set global vars (eww...) */
501 // calchandles_fcurve
502 BeztEditFunc ANIM_editkeyframes_mirror(short type)
503 {
504         switch (type) {
505                 case MIRROR_KEYS_CURFRAME: /* mirror over current frame */
506                         return mirror_bezier_cframe;
507                 case MIRROR_KEYS_YAXIS: /* mirror over frame 0 */
508                         return mirror_bezier_yaxis;
509                 case MIRROR_KEYS_XAXIS: /* mirror over value 0 */
510                         return mirror_bezier_xaxis;
511                 case MIRROR_KEYS_MARKER: /* mirror over marker */
512                         return mirror_bezier_marker; 
513                 default: /* just in case */
514                         return mirror_bezier_yaxis;
515                         break;
516         }
517 }
518
519 /* ******************************************* */
520 /* Settings */
521
522 /* Sets the selected bezier handles to type 'auto' */
523 static short set_bezier_auto(BeztEditData *bed, BezTriple *bezt) 
524 {
525         if((bezt->f1  & SELECT) || (bezt->f3 & SELECT)) {
526                 if (bezt->f1 & SELECT) bezt->h1= 1; /* the secret code for auto */
527                 if (bezt->f3 & SELECT) bezt->h2= 1;
528                 
529                 /* if the handles are not of the same type, set them
530                  * to type free
531                  */
532                 if (bezt->h1 != bezt->h2) {
533                         if ELEM(bezt->h1, HD_ALIGN, HD_AUTO) bezt->h1= HD_FREE;
534                         if ELEM(bezt->h2, HD_ALIGN, HD_AUTO) bezt->h2= HD_FREE;
535                 }
536         }
537         return 0;
538 }
539
540 /* Sets the selected bezier handles to type 'vector'  */
541 static short set_bezier_vector(BeztEditData *bed, BezTriple *bezt) 
542 {
543         if ((bezt->f1 & SELECT) || (bezt->f3 & SELECT)) {
544                 if (bezt->f1 & SELECT) bezt->h1= HD_VECT;
545                 if (bezt->f3 & SELECT) bezt->h2= HD_VECT;
546                 
547                 /* if the handles are not of the same type, set them
548                  * to type free
549                  */
550                 if (bezt->h1 != bezt->h2) {
551                         if ELEM(bezt->h1, HD_ALIGN, HD_AUTO) bezt->h1= HD_FREE;
552                         if ELEM(bezt->h2, HD_ALIGN, HD_AUTO) bezt->h2= HD_FREE;
553                 }
554         }
555         return 0;
556 }
557
558 /* Queries if the handle should be set to 'free' or 'align' */
559 static short bezier_isfree(BeztEditData *bed, BezTriple *bezt) 
560 {
561         if ((bezt->f1 & SELECT) && (bezt->h1)) return 1;
562         if ((bezt->f3 & SELECT) && (bezt->h2)) return 1;
563         return 0;
564 }
565
566 /* Sets selected bezier handles to type 'align' */
567 static short set_bezier_align(BeztEditData *bed, BezTriple *bezt) 
568 {       
569         if (bezt->f1 & SELECT) bezt->h1= HD_ALIGN;
570         if (bezt->f3 & SELECT) bezt->h2= HD_ALIGN;
571         return 0;
572 }
573
574 /* Sets selected bezier handles to type 'free'  */
575 static short set_bezier_free(BeztEditData *bed, BezTriple *bezt) 
576 {
577         if (bezt->f1 & SELECT) bezt->h1= HD_FREE;
578         if (bezt->f3 & SELECT) bezt->h2= HD_FREE;
579         return 0;
580 }
581
582 /* Set all Bezier Handles to a single type */
583 // calchandles_fcurve
584 BeztEditFunc ANIM_editkeyframes_handles(short code)
585 {
586         switch (code) {
587                 case HD_AUTO: /* auto */
588                         return set_bezier_auto;
589                 case HD_VECT: /* vector */
590                         return set_bezier_vector;
591                 case HD_FREE: /* free */
592                         return set_bezier_free;
593                 case HD_ALIGN: /* align */
594                         return set_bezier_align;
595                 
596                 default: /* free or align? */
597                         return bezier_isfree;
598         }
599 }
600
601 /* ------- */
602
603 static short set_bezt_constant(BeztEditData *bed, BezTriple *bezt) 
604 {
605         if (bezt->f2 & SELECT) 
606                 bezt->ipo= BEZT_IPO_CONST;
607         return 0;
608 }
609
610 static short set_bezt_linear(BeztEditData *bed, BezTriple *bezt) 
611 {
612         if (bezt->f2 & SELECT) 
613                 bezt->ipo= BEZT_IPO_LIN;
614         return 0;
615 }
616
617 static short set_bezt_bezier(BeztEditData *bed, BezTriple *bezt) 
618 {
619         if (bezt->f2 & SELECT) 
620                 bezt->ipo= BEZT_IPO_BEZ;
621         return 0;
622 }
623
624 /* Set the interpolation type of the selected BezTriples in each IPO curve to the specified one */
625 // ANIM_editkeyframes_ipocurve_ipotype() !
626 BeztEditFunc ANIM_editkeyframes_ipo(short code)
627 {
628         switch (code) {
629                 case BEZT_IPO_CONST: /* constant */
630                         return set_bezt_constant;
631                 case BEZT_IPO_LIN: /* linear */ 
632                         return set_bezt_linear;
633                 default: /* bezier */
634                         return set_bezt_bezier;
635         }
636 }
637
638 /* ******************************************* */
639 /* Selection */
640
641 static short select_bezier_add(BeztEditData *bed, BezTriple *bezt) 
642 {
643         /* Select the bezier triple */
644         BEZ_SEL(bezt);
645         return 0;
646 }
647
648 static short select_bezier_subtract(BeztEditData *bed, BezTriple *bezt) 
649 {
650         /* Deselect the bezier triple */
651         BEZ_DESEL(bezt);
652         return 0;
653 }
654
655 static short select_bezier_invert(BeztEditData *bed, BezTriple *bezt) 
656 {
657         /* Invert the selection for the bezier triple */
658         bezt->f2 ^= SELECT;
659         if (bezt->f2 & SELECT) {
660                 bezt->f1 |= SELECT;
661                 bezt->f3 |= SELECT;
662         }
663         else {
664                 bezt->f1 &= ~SELECT;
665                 bezt->f3 &= ~SELECT;
666         }
667         return 0;
668 }
669
670 // NULL
671 BeztEditFunc ANIM_editkeyframes_select(short selectmode)
672 {
673         switch (selectmode) {
674                 case SELECT_ADD: /* add */
675                         return select_bezier_add;
676                 case SELECT_SUBTRACT: /* subtract */
677                         return select_bezier_subtract;
678                 case SELECT_INVERT: /* invert */
679                         return select_bezier_invert;
680                 default: /* replace (need to clear all, then add) */
681                         return select_bezier_add;
682         }
683 }