Blender  V2.59
graph_edit.c
Go to the documentation of this file.
00001 /*
00002  * $Id: graph_edit.c 37750 2011-06-23 09:27:56Z campbellbarton $
00003  *
00004  * ***** BEGIN GPL LICENSE BLOCK *****
00005  *
00006  * This program is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU General Public License
00008  * as published by the Free Software Foundation; either version 2
00009  * of the License, or (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software Foundation,
00018  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019  *
00020  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
00021  * All rights reserved.
00022  *
00023  * The Original Code is: all of this file.
00024  *
00025  * Contributor(s): Joshua Leung
00026  *
00027  * ***** END GPL LICENSE BLOCK *****
00028  */
00029 
00035 #include <math.h>
00036 #include <stdlib.h>
00037 #include <string.h>
00038 #include <float.h>
00039 
00040 #ifdef WITH_AUDASPACE
00041 #  include "AUD_C-API.h"
00042 #endif
00043 
00044 #include "MEM_guardedalloc.h"
00045 
00046 #include "BLI_blenlib.h"
00047 #include "BLI_math.h"
00048 #include "BLI_utildefines.h"
00049 
00050 #include "DNA_anim_types.h"
00051 #include "DNA_object_types.h"
00052 #include "DNA_scene_types.h"
00053 
00054 #include "RNA_access.h"
00055 #include "RNA_define.h"
00056 #include "RNA_enum_types.h"
00057 
00058 #include "BKE_fcurve.h"
00059 #include "BKE_nla.h"
00060 #include "BKE_context.h"
00061 #include "BKE_report.h"
00062 
00063 #include "UI_interface.h"
00064 #include "UI_resources.h"
00065 #include "UI_view2d.h"
00066 
00067 #include "ED_anim_api.h"
00068 #include "ED_keyframing.h"
00069 #include "ED_keyframes_edit.h"
00070 #include "ED_screen.h"
00071 #include "ED_transform.h"
00072 #include "ED_markers.h"
00073 
00074 #include "WM_api.h"
00075 #include "WM_types.h"
00076 
00077 #include "graph_intern.h"
00078 
00079 /* ************************************************************************** */
00080 /* KEYFRAME-RANGE STUFF */
00081 
00082 /* *************************** Calculate Range ************************** */
00083 
00084 /* Get the min/max keyframes*/
00085 /* note: it should return total boundbox, filter for selection only can be argument... */
00086 void get_graph_keyframe_extents (bAnimContext *ac, float *xmin, float *xmax, float *ymin, float *ymax, const short selOnly)
00087 {
00088         ListBase anim_data = {NULL, NULL};
00089         bAnimListElem *ale;
00090         int filter;
00091         
00092         /* get data to filter, from Dopesheet */
00093         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_CURVESONLY | ANIMFILTER_NODUPLIS);
00094         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
00095         
00096         /* set large values to try to override */
00097         if (xmin) *xmin= 999999999.0f;
00098         if (xmax) *xmax= -999999999.0f;
00099         if (ymin) *ymin= 999999999.0f;
00100         if (ymax) *ymax= -999999999.0f;
00101         
00102         /* check if any channels to set range with */
00103         if (anim_data.first) {
00104                 /* go through channels, finding max extents */
00105                 for (ale= anim_data.first; ale; ale= ale->next) {
00106                         AnimData *adt= ANIM_nla_mapping_get(ac, ale);
00107                         FCurve *fcu= (FCurve *)ale->key_data;
00108                         float txmin, txmax, tymin, tymax;
00109                         float unitFac;
00110                         
00111                         /* get range */
00112                         calc_fcurve_bounds(fcu, &txmin, &txmax, &tymin, &tymax, selOnly);
00113                         
00114                         /* apply NLA scaling */
00115                         if (adt) {
00116                                 txmin= BKE_nla_tweakedit_remap(adt, txmin, NLATIME_CONVERT_MAP);
00117                                 txmax= BKE_nla_tweakedit_remap(adt, txmax, NLATIME_CONVERT_MAP);
00118                         }
00119                         
00120                         /* apply unit corrections */
00121                         unitFac= ANIM_unit_mapping_get_factor(ac->scene, ale->id, fcu, 0);
00122                         tymin *= unitFac;
00123                         tymax *= unitFac;
00124                         
00125                         /* try to set cur using these values, if they're more extreme than previously set values */
00126                         if ((xmin) && (txmin < *xmin))          *xmin= txmin;
00127                         if ((xmax) && (txmax > *xmax))          *xmax= txmax;
00128                         if ((ymin) && (tymin < *ymin))          *ymin= tymin;
00129                         if ((ymax) && (tymax > *ymax))          *ymax= tymax;
00130                 }
00131                 
00132                 /* ensure that the extents are not too extreme that view implodes...*/
00133                 if ((xmin && xmax) && (fabsf(*xmax - *xmin) < 0.1f)) *xmax += 0.1f;
00134                 if ((ymin && ymax) && (fabsf(*ymax - *ymin) < 0.1f)) *ymax += 0.1f;
00135                 
00136                 /* free memory */
00137                 BLI_freelistN(&anim_data);
00138         }
00139         else {
00140                 /* set default range */
00141                 if (ac->scene) {
00142                         if (xmin) *xmin= (float)ac->scene->r.sfra;
00143                         if (xmax) *xmax= (float)ac->scene->r.efra;
00144                 }
00145                 else {
00146                         if (xmin) *xmin= -5;
00147                         if (xmax) *xmax= 100;
00148                 }
00149                 
00150                 if (ymin) *ymin= -5;
00151                 if (ymax) *ymax= 5;
00152         }
00153 }
00154 
00155 /* ****************** Automatic Preview-Range Operator ****************** */
00156 
00157 static int graphkeys_previewrange_exec(bContext *C, wmOperator *UNUSED(op))
00158 {
00159         bAnimContext ac;
00160         Scene *scene;
00161         float min, max;
00162         
00163         /* get editor data */
00164         if (ANIM_animdata_get_context(C, &ac) == 0)
00165                 return OPERATOR_CANCELLED;
00166         if (ac.scene == NULL)
00167                 return OPERATOR_CANCELLED;
00168         else
00169                 scene= ac.scene;
00170         
00171         /* set the range directly */
00172         get_graph_keyframe_extents(&ac, &min, &max, NULL, NULL, FALSE);
00173         scene->r.flag |= SCER_PRV_RANGE;
00174         scene->r.psfra= (int)floor(min + 0.5f);
00175         scene->r.pefra= (int)floor(max + 0.5f);
00176         
00177         /* set notifier that things have changed */
00178         // XXX err... there's nothing for frame ranges yet, but this should do fine too
00179         WM_event_add_notifier(C, NC_SCENE|ND_FRAME, ac.scene); 
00180         
00181         return OPERATOR_FINISHED;
00182 }
00183  
00184 void GRAPH_OT_previewrange_set (wmOperatorType *ot)
00185 {
00186         /* identifiers */
00187         ot->name= "Auto-Set Preview Range";
00188         ot->idname= "GRAPH_OT_previewrange_set";
00189         ot->description= "Automatically set Preview Range based on range of keyframes";
00190         
00191         /* api callbacks */
00192         ot->exec= graphkeys_previewrange_exec;
00193         ot->poll= ED_operator_graphedit_active; // XXX: unchecked poll to get fsamples working too, but makes modifier damage trickier...
00194         
00195         /* flags */
00196         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
00197 }
00198 
00199 /* ****************** View-All Operator ****************** */
00200 
00201 static int graphkeys_viewall(bContext *C, const short selOnly)
00202 {
00203         bAnimContext ac;
00204         View2D *v2d;
00205         float extra;
00206 
00207         /* get editor data */
00208         if (ANIM_animdata_get_context(C, &ac) == 0)
00209                 return OPERATOR_CANCELLED;
00210         v2d= &ac.ar->v2d;
00211 
00212         /* set the horizontal range, with an extra offset so that the extreme keys will be in view */
00213         get_graph_keyframe_extents(&ac, &v2d->cur.xmin, &v2d->cur.xmax, &v2d->cur.ymin, &v2d->cur.ymax, selOnly);
00214 
00215         extra= 0.1f * (v2d->cur.xmax - v2d->cur.xmin);
00216         v2d->cur.xmin -= extra;
00217         v2d->cur.xmax += extra;
00218 
00219         extra= 0.1f * (v2d->cur.ymax - v2d->cur.ymin);
00220         v2d->cur.ymin -= extra;
00221         v2d->cur.ymax += extra;
00222 
00223         /* do View2D syncing */
00224         UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
00225 
00226         /* set notifier that things have changed */
00227         ED_area_tag_redraw(CTX_wm_area(C));
00228 
00229         return OPERATOR_FINISHED;
00230 }
00231 
00232 /* ......... */
00233 
00234 static int graphkeys_viewall_exec(bContext *C, wmOperator *UNUSED(op))
00235 {
00236         /* whole range */
00237         return graphkeys_viewall(C, FALSE);
00238 }
00239  
00240 static int graphkeys_view_selected_exec(bContext *C, wmOperator *UNUSED(op))
00241 {
00242         /* only selected */
00243         return graphkeys_viewall(C, TRUE);
00244 }
00245 
00246 void GRAPH_OT_view_all (wmOperatorType *ot)
00247 {
00248         /* identifiers */
00249         ot->name= "View All";
00250         ot->idname= "GRAPH_OT_view_all";
00251         ot->description= "Reset viewable area to show full keyframe range";
00252         
00253         /* api callbacks */
00254         ot->exec= graphkeys_viewall_exec;
00255         ot->poll= ED_operator_graphedit_active; // XXX: unchecked poll to get fsamples working too, but makes modifier damage trickier...
00256         
00257         /* flags */
00258         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
00259 }
00260 
00261 void GRAPH_OT_view_selected (wmOperatorType *ot)
00262 {
00263         /* identifiers */
00264         ot->name= "View Selected";
00265         ot->idname= "GRAPH_OT_view_selected";
00266         ot->description= "Reset viewable area to show selected keyframe range";
00267 
00268         /* api callbacks */
00269         ot->exec= graphkeys_view_selected_exec;
00270         ot->poll= ED_operator_graphedit_active; // XXX: unchecked poll to get fsamples working too, but makes modifier damage trickier...
00271 
00272         /* flags */
00273         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
00274 }
00275 
00276 /* ******************** Create Ghost-Curves Operator *********************** */
00277 /* This operator samples the data of the selected F-Curves to F-Points, storing them
00278  * as 'ghost curves' in the active Graph Editor
00279  */
00280 
00281 /* Bake each F-Curve into a set of samples, and store as a ghost curve */
00282 static void create_ghost_curves (bAnimContext *ac, int start, int end)
00283 {       
00284         SpaceIpo *sipo= (SpaceIpo *)ac->sa->spacedata.first;
00285         ListBase anim_data = {NULL, NULL};
00286         bAnimListElem *ale;
00287         int filter;
00288         
00289         /* free existing ghost curves */
00290         free_fcurves(&sipo->ghostCurves);
00291         
00292         /* sanity check */
00293         if (start >= end) {
00294                 printf("Error: Frame range for Ghost F-Curve creation is inappropriate \n");
00295                 return;
00296         }
00297         
00298         /* filter data */
00299         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_SEL | ANIMFILTER_CURVESONLY | ANIMFILTER_NODUPLIS);
00300         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
00301         
00302         /* loop through filtered data and add keys between selected keyframes on every frame  */
00303         for (ale= anim_data.first; ale; ale= ale->next) {
00304                 FCurve *fcu= (FCurve *)ale->key_data;
00305                 FCurve *gcu= MEM_callocN(sizeof(FCurve), "Ghost FCurve");
00306                 AnimData *adt= ANIM_nla_mapping_get(ac, ale);
00307                 ChannelDriver *driver= fcu->driver;
00308                 FPoint *fpt;
00309                 float unitFac;
00310                 int cfra;               
00311                 
00312                 /* disable driver so that it don't muck up the sampling process */
00313                 fcu->driver= NULL;
00314                 
00315                 /* calculate unit-mapping factor */
00316                 unitFac= ANIM_unit_mapping_get_factor(ac->scene, ale->id, fcu, 0);
00317                 
00318                 /* create samples, but store them in a new curve 
00319                  *      - we cannot use fcurve_store_samples() as that will only overwrite the original curve 
00320                  */
00321                 gcu->fpt= fpt= MEM_callocN(sizeof(FPoint)*(end-start+1), "Ghost FPoint Samples");
00322                 gcu->totvert= end - start + 1;
00323                 
00324                 /* use the sampling callback at 1-frame intervals from start to end frames */
00325                 for (cfra= start; cfra <= end; cfra++, fpt++) {
00326                         float cfrae= BKE_nla_tweakedit_remap(adt, cfra, NLATIME_CONVERT_UNMAP);
00327                         
00328                         fpt->vec[0]= cfrae;
00329                         fpt->vec[1]= fcurve_samplingcb_evalcurve(fcu, NULL, cfrae) * unitFac;
00330                 }
00331                 
00332                 /* set color of ghost curve 
00333                  *      - make the color slightly darker
00334                  */
00335                 gcu->color[0]= fcu->color[0] - 0.07f;
00336                 gcu->color[1]= fcu->color[1] - 0.07f;
00337                 gcu->color[2]= fcu->color[2] - 0.07f;
00338                 
00339                 /* store new ghost curve */
00340                 BLI_addtail(&sipo->ghostCurves, gcu);
00341                 
00342                 /* restore driver */
00343                 fcu->driver= driver;
00344         }
00345         
00346         /* admin and redraws */
00347         BLI_freelistN(&anim_data);
00348 }
00349 
00350 /* ------------------- */
00351 
00352 static int graphkeys_create_ghostcurves_exec(bContext *C, wmOperator *UNUSED(op))
00353 {
00354         bAnimContext ac;
00355         View2D *v2d;
00356         int start, end;
00357         
00358         /* get editor data */
00359         if (ANIM_animdata_get_context(C, &ac) == 0)
00360                 return OPERATOR_CANCELLED;
00361                 
00362         /* ghost curves are snapshots of the visible portions of the curves, so set range to be the visible range */
00363         v2d= &ac.ar->v2d;
00364         start= (int)v2d->cur.xmin;
00365         end= (int)v2d->cur.xmax;
00366         
00367         /* bake selected curves into a ghost curve */
00368         create_ghost_curves(&ac, start, end);
00369         
00370         /* update this editor only */
00371         ED_area_tag_redraw(CTX_wm_area(C));
00372         
00373         return OPERATOR_FINISHED;
00374 }
00375  
00376 void GRAPH_OT_ghost_curves_create (wmOperatorType *ot)
00377 {
00378         /* identifiers */
00379         ot->name= "Create Ghost Curves";
00380         ot->idname= "GRAPH_OT_ghost_curves_create";
00381         ot->description= "Create snapshot (Ghosts) of selected F-Curves as background aid for active Graph Editor";
00382         
00383         /* api callbacks */
00384         ot->exec= graphkeys_create_ghostcurves_exec;
00385         ot->poll= graphop_visible_keyframes_poll;
00386         
00387         /* flags */
00388         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
00389         
00390         // todo: add props for start/end frames
00391 }
00392 
00393 /* ******************** Clear Ghost-Curves Operator *********************** */
00394 /* This operator clears the 'ghost curves' for the active Graph Editor */
00395 
00396 static int graphkeys_clear_ghostcurves_exec(bContext *C, wmOperator *UNUSED(op))
00397 {
00398         bAnimContext ac;
00399         SpaceIpo *sipo;
00400         
00401         /* get editor data */
00402         if (ANIM_animdata_get_context(C, &ac) == 0)
00403                 return OPERATOR_CANCELLED;
00404         sipo= (SpaceIpo *)ac.sa->spacedata.first;
00405                 
00406         /* if no ghost curves, don't do anything */
00407         if (sipo->ghostCurves.first == NULL)
00408                 return OPERATOR_CANCELLED;
00409         
00410         /* free ghost curves */
00411         free_fcurves(&sipo->ghostCurves);
00412         
00413         /* update this editor only */
00414         ED_area_tag_redraw(CTX_wm_area(C));
00415         
00416         return OPERATOR_FINISHED;
00417 }
00418  
00419 void GRAPH_OT_ghost_curves_clear (wmOperatorType *ot)
00420 {
00421         /* identifiers */
00422         ot->name= "Clear Ghost Curves";
00423         ot->idname= "GRAPH_OT_ghost_curves_clear";
00424         ot->description= "Clear F-Curve snapshots (Ghosts) for active Graph Editor";
00425         
00426         /* api callbacks */
00427         ot->exec= graphkeys_clear_ghostcurves_exec;
00428         ot->poll= ED_operator_graphedit_active;
00429         
00430         /* flags */
00431         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
00432 }
00433 
00434 /* ************************************************************************** */
00435 /* GENERAL STUFF */
00436 
00437 /* ******************** Insert Keyframes Operator ************************* */
00438 
00439 /* defines for insert keyframes tool */
00440 static EnumPropertyItem prop_graphkeys_insertkey_types[] = {
00441         {1, "ALL", 0, "All Channels", ""},
00442         {2, "SEL", 0, "Only Selected Channels", ""},
00443         {0, NULL, 0, NULL, NULL}
00444 };
00445 
00446 /* this function is responsible for snapping keyframes to frame-times */
00447 static void insert_graph_keys(bAnimContext *ac, short mode) 
00448 {
00449         ListBase anim_data = {NULL, NULL};
00450         bAnimListElem *ale;
00451         int filter;
00452         
00453         ReportList *reports = ac->reports;
00454         Scene *scene= ac->scene;
00455         short flag = 0;
00456         
00457         /* filter data */
00458         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVESONLY | ANIMFILTER_NODUPLIS);
00459         if (mode == 2) filter |= ANIMFILTER_SEL;
00460         
00461         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
00462         
00463         /* init keyframing flag */
00464         flag = ANIM_get_keyframing_flags(scene, 1);
00465         
00466         /* insert keyframes */
00467         for (ale= anim_data.first; ale; ale= ale->next) {
00468                 AnimData *adt= ANIM_nla_mapping_get(ac, ale);
00469                 FCurve *fcu= (FCurve *)ale->key_data;
00470                 float cfra;
00471                 
00472                 /* adjust current frame for NLA-mapping */
00473                 if (adt)
00474                         cfra= BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP);
00475                 else 
00476                         cfra= (float)CFRA;
00477                         
00478                 /* if there's an id */
00479                 if (ale->id)
00480                         insert_keyframe(reports, ale->id, NULL, ((fcu->grp)?(fcu->grp->name):(NULL)), fcu->rna_path, fcu->array_index, cfra, flag);
00481                 else
00482                         insert_vert_fcurve(fcu, cfra, fcu->curval, 0);
00483         }
00484         
00485         BLI_freelistN(&anim_data);
00486 }
00487 
00488 /* ------------------- */
00489 
00490 static int graphkeys_insertkey_exec(bContext *C, wmOperator *op)
00491 {
00492         bAnimContext ac;
00493         short mode;
00494         
00495         /* get editor data */
00496         if (ANIM_animdata_get_context(C, &ac) == 0)
00497                 return OPERATOR_CANCELLED;
00498                 
00499         /* which channels to affect? */
00500         mode= RNA_enum_get(op->ptr, "type");
00501         
00502         /* insert keyframes */
00503         insert_graph_keys(&ac, mode);
00504         
00505         /* validate keyframes after editing */
00506         ANIM_editkeyframes_refresh(&ac);
00507         
00508         /* set notifier that keyframes have changed */
00509         WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME|NA_EDITED, NULL);
00510         
00511         return OPERATOR_FINISHED;
00512 }
00513 
00514 void GRAPH_OT_keyframe_insert (wmOperatorType *ot)
00515 {
00516         /* identifiers */
00517         ot->name= "Insert Keyframes";
00518         ot->idname= "GRAPH_OT_keyframe_insert";
00519         ot->description= "Insert keyframes for the specified channels";
00520         
00521         /* api callbacks */
00522         ot->invoke= WM_menu_invoke;
00523         ot->exec= graphkeys_insertkey_exec;
00524         ot->poll= graphop_editable_keyframes_poll;
00525         
00526         /* flags */
00527         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
00528         
00529         /* id-props */
00530         ot->prop= RNA_def_enum(ot->srna, "type", prop_graphkeys_insertkey_types, 0, "Type", "");
00531 }
00532 
00533 /* ******************** Click-Insert Keyframes Operator ************************* */
00534 
00535 static int graphkeys_click_insert_exec (bContext *C, wmOperator *op)
00536 {
00537         bAnimContext ac;
00538         bAnimListElem *ale;
00539         AnimData *adt;
00540         FCurve *fcu;
00541         float frame, val;
00542         
00543         /* get animation context */
00544         if (ANIM_animdata_get_context(C, &ac) == 0)
00545                 return OPERATOR_CANCELLED;
00546         
00547         /* get active F-Curve 'anim-list-element' */
00548         ale= get_active_fcurve_channel(&ac);
00549         if (ELEM(NULL, ale, ale->data)) {
00550                 if (ale) MEM_freeN(ale);
00551                 return OPERATOR_CANCELLED;
00552         }
00553         fcu = ale->data;
00554         
00555         /* when there are F-Modifiers on the curve, only allow adding
00556          * keyframes if these will be visible after doing so...
00557          */
00558         if (fcurve_is_keyframable(fcu)) {
00559                 /* get frame and value from props */
00560                 frame= RNA_float_get(op->ptr, "frame");
00561                 val= RNA_float_get(op->ptr, "value");
00562                 
00563                 /* apply inverse NLA-mapping to frame to get correct time in un-scaled action */
00564                 adt= ANIM_nla_mapping_get(&ac, ale);
00565                 frame= BKE_nla_tweakedit_remap(adt, frame, NLATIME_CONVERT_UNMAP);
00566                 
00567                 /* apply inverse unit-mapping to value to get correct value for F-Curves */
00568                 val *= ANIM_unit_mapping_get_factor(ac.scene, ale->id, fcu, 1);
00569                 
00570                 /* insert keyframe on the specified frame + value */
00571                 insert_vert_fcurve(fcu, frame, val, 0);
00572         }
00573         else {
00574                 /* warn about why this can't happen */
00575                 if (fcu->fpt)
00576                         BKE_report(op->reports, RPT_ERROR, "Keyframes cannot be added to sampled F-Curves");
00577                 else if (fcu->flag & FCURVE_PROTECTED)
00578                         BKE_report(op->reports, RPT_ERROR, "Active F-Curve is not editable");
00579                 else
00580                         BKE_report(op->reports, RPT_ERROR, "Remove F-Modifiers from F-Curve to add keyframes");
00581         }
00582         
00583         /* free temp data */
00584         MEM_freeN(ale);
00585         
00586         /* set notifier that keyframes have changed */
00587         WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME|NA_EDITED, NULL);
00588         
00589         /* done */
00590         return OPERATOR_FINISHED;
00591 }
00592 
00593 static int graphkeys_click_insert_invoke (bContext *C, wmOperator *op, wmEvent *evt)
00594 {
00595         bAnimContext ac;
00596         ARegion *ar;
00597         View2D *v2d;
00598         int mval[2];
00599         float x, y;
00600         
00601         /* get animation context */
00602         if (ANIM_animdata_get_context(C, &ac) == 0)
00603                 return OPERATOR_CANCELLED;
00604         
00605         /* store mouse coordinates in View2D space, into the operator's properties */
00606         ar= ac.ar;
00607         v2d= &ar->v2d;
00608         
00609         mval[0]= (evt->x - ar->winrct.xmin);
00610         mval[1]= (evt->y - ar->winrct.ymin);
00611         
00612         UI_view2d_region_to_view(v2d, mval[0], mval[1], &x, &y);
00613         
00614         RNA_float_set(op->ptr, "frame", x);
00615         RNA_float_set(op->ptr, "value", y);
00616         
00617         /* run exec now */
00618         return graphkeys_click_insert_exec(C, op);
00619 }
00620 
00621 void GRAPH_OT_click_insert (wmOperatorType *ot)
00622 {
00623         /* identifiers */
00624         ot->name= "Click-Insert Keyframes";
00625         ot->idname= "GRAPH_OT_click_insert";
00626         ot->description= "Insert new keyframe at the cursor position for the active F-Curve";
00627         
00628         /* api callbacks */
00629         ot->invoke= graphkeys_click_insert_invoke;
00630         ot->exec= graphkeys_click_insert_exec;
00631         ot->poll= graphop_active_fcurve_poll;
00632         
00633         /* flags */
00634         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
00635         
00636         /* properties */
00637         RNA_def_float(ot->srna, "frame", 1.0f, -FLT_MAX, FLT_MAX, "Frame Number", "Frame to insert keyframe on", 0, 100);
00638         RNA_def_float(ot->srna, "value", 1.0f, -FLT_MAX, FLT_MAX, "Value", "Value for keyframe on", 0, 100);
00639 }
00640 
00641 /* ******************** Copy/Paste Keyframes Operator ************************* */
00642 /* NOTE: the backend code for this is shared with the dopesheet editor */
00643 
00644 static short copy_graph_keys (bAnimContext *ac)
00645 {       
00646         ListBase anim_data = {NULL, NULL};
00647         int filter, ok=0;
00648         
00649         /* clear buffer first */
00650         free_anim_copybuf();
00651         
00652         /* filter data */
00653         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_CURVESONLY | ANIMFILTER_NODUPLIS);
00654         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
00655         
00656         /* copy keyframes */
00657         ok= copy_animedit_keys(ac, &anim_data);
00658         
00659         /* clean up */
00660         BLI_freelistN(&anim_data);
00661 
00662         return ok;
00663 }
00664 
00665 static short paste_graph_keys (bAnimContext *ac,
00666         const eKeyPasteOffset offset_mode, const eKeyMergeMode merge_mode)
00667 {       
00668         ListBase anim_data = {NULL, NULL};
00669         int filter, ok=0;
00670         
00671         /* filter data */
00672         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_SEL | ANIMFILTER_FOREDIT | ANIMFILTER_CURVESONLY | ANIMFILTER_NODUPLIS);
00673         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
00674         
00675         /* paste keyframes */
00676         ok= paste_animedit_keys(ac, &anim_data, offset_mode, merge_mode);
00677         
00678         /* clean up */
00679         BLI_freelistN(&anim_data);
00680 
00681         return ok;
00682 }
00683 
00684 /* ------------------- */
00685 
00686 static int graphkeys_copy_exec(bContext *C, wmOperator *op)
00687 {
00688         bAnimContext ac;
00689         
00690         /* get editor data */
00691         if (ANIM_animdata_get_context(C, &ac) == 0)
00692                 return OPERATOR_CANCELLED;
00693         
00694         /* copy keyframes */
00695         if (copy_graph_keys(&ac)) {     
00696                 BKE_report(op->reports, RPT_ERROR, "No keyframes copied to keyframes copy/paste buffer");
00697                 return OPERATOR_CANCELLED;
00698         }
00699         
00700         /* just return - no operator needed here (no changes) */
00701         return OPERATOR_FINISHED;
00702 }
00703  
00704 void GRAPH_OT_copy (wmOperatorType *ot)
00705 {
00706         /* identifiers */
00707         ot->name= "Copy Keyframes";
00708         ot->idname= "GRAPH_OT_copy";
00709         ot->description= "Copy selected keyframes to the copy/paste buffer";
00710         
00711         /* api callbacks */
00712         ot->exec= graphkeys_copy_exec;
00713         ot->poll= graphop_editable_keyframes_poll;
00714         
00715         /* flags */
00716         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
00717 }
00718 
00719 
00720 
00721 static int graphkeys_paste_exec(bContext *C, wmOperator *op)
00722 {
00723         bAnimContext ac;
00724 
00725         const eKeyPasteOffset offset_mode= RNA_enum_get(op->ptr, "offset");
00726         const eKeyMergeMode merge_mode= RNA_enum_get(op->ptr, "merge");
00727         
00728         /* get editor data */
00729         if (ANIM_animdata_get_context(C, &ac) == 0)
00730                 return OPERATOR_CANCELLED;
00731         
00732         if(ac.reports==NULL) {
00733                 ac.reports= op->reports;
00734         }
00735 
00736         /* paste keyframes */
00737         if (paste_graph_keys(&ac, offset_mode, merge_mode)) {
00738                 return OPERATOR_CANCELLED;
00739         }
00740         
00741         /* validate keyframes after editing */
00742         ANIM_editkeyframes_refresh(&ac);
00743         
00744         /* set notifier that keyframes have changed */
00745         WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME|NA_EDITED, NULL);
00746         
00747         return OPERATOR_FINISHED;
00748 }
00749  
00750 void GRAPH_OT_paste (wmOperatorType *ot)
00751 {
00752         /* identifiers */
00753         ot->name= "Paste Keyframes";
00754         ot->idname= "GRAPH_OT_paste";
00755         ot->description= "Paste keyframes from copy/paste buffer for the selected channels, starting on the current frame";
00756         
00757         /* api callbacks */
00758 //      ot->invoke= WM_operator_props_popup; // better wait for graph redo panel
00759         ot->exec= graphkeys_paste_exec;
00760         ot->poll= graphop_editable_keyframes_poll;
00761         
00762         /* flags */
00763         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
00764 
00765         RNA_def_enum(ot->srna, "offset", keyframe_paste_offset_items, KEYFRAME_PASTE_OFFSET_CFRA_START, "Offset", "Paste time offset of keys");
00766         RNA_def_enum(ot->srna, "merge", keyframe_paste_merge_items, KEYFRAME_PASTE_MERGE_MIX, "Type", "Method of merking pasted keys and existing");
00767 }
00768 
00769 /* ******************** Duplicate Keyframes Operator ************************* */
00770 
00771 static void duplicate_graph_keys (bAnimContext *ac)
00772 {
00773         ListBase anim_data = {NULL, NULL};
00774         bAnimListElem *ale;
00775         int filter;
00776         
00777         /* filter data */
00778         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVESONLY | ANIMFILTER_NODUPLIS);
00779         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
00780         
00781         /* loop through filtered data and delete selected keys */
00782         for (ale= anim_data.first; ale; ale= ale->next) {
00783                 duplicate_fcurve_keys((FCurve *)ale->key_data);
00784         }
00785         
00786         /* free filtered list */
00787         BLI_freelistN(&anim_data);
00788 }
00789 
00790 /* ------------------- */
00791 
00792 static int graphkeys_duplicate_exec(bContext *C, wmOperator *UNUSED(op))
00793 {
00794         bAnimContext ac;
00795         
00796         /* get editor data */
00797         if (ANIM_animdata_get_context(C, &ac) == 0)
00798                 return OPERATOR_CANCELLED;
00799                 
00800         /* duplicate keyframes */
00801         duplicate_graph_keys(&ac);
00802         
00803         /* validate keyframes after editing */
00804         ANIM_editkeyframes_refresh(&ac);
00805         
00806         /* set notifier that keyframes have changed */
00807         WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME|NA_EDITED, NULL);
00808         
00809         return OPERATOR_FINISHED;
00810 }
00811 
00812 static int graphkeys_duplicate_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
00813 {
00814         graphkeys_duplicate_exec(C, op);
00815 
00816         return OPERATOR_FINISHED;
00817 }
00818  
00819 void GRAPH_OT_duplicate (wmOperatorType *ot)
00820 {
00821         /* identifiers */
00822         ot->name= "Duplicate Keyframes";
00823         ot->idname= "GRAPH_OT_duplicate";
00824         ot->description= "Make a copy of all selected keyframes";
00825         
00826         /* api callbacks */
00827         ot->invoke= graphkeys_duplicate_invoke;
00828         ot->exec= graphkeys_duplicate_exec;
00829         ot->poll= graphop_editable_keyframes_poll;
00830         
00831         /* flags */
00832         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
00833         
00834         /* to give to transform */
00835         RNA_def_enum(ot->srna, "mode", transform_mode_types, TFM_TRANSLATION, "Mode", "");
00836 }
00837 
00838 /* ******************** Delete Keyframes Operator ************************* */
00839 
00840 static void delete_graph_keys (bAnimContext *ac)
00841 {
00842         ListBase anim_data = {NULL, NULL};
00843         bAnimListElem *ale;
00844         int filter;
00845         
00846         /* filter data */
00847         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVESONLY | ANIMFILTER_NODUPLIS);
00848         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
00849         
00850         /* loop through filtered data and delete selected keys */
00851         for (ale= anim_data.first; ale; ale= ale->next) {
00852                 FCurve *fcu= (FCurve *)ale->key_data;
00853                 AnimData *adt= ale->adt;
00854                 
00855                 /* delete selected keyframes only */
00856                 delete_fcurve_keys(fcu); 
00857                 
00858                 /* Only delete curve too if it won't be doing anything anymore */
00859                 if ((fcu->totvert == 0) && (list_has_suitable_fmodifier(&fcu->modifiers, 0, FMI_TYPE_GENERATE_CURVE) == 0))
00860                         ANIM_fcurve_delete_from_animdata(ac, adt, fcu);
00861         }
00862         
00863         /* free filtered list */
00864         BLI_freelistN(&anim_data);
00865 }
00866 
00867 /* ------------------- */
00868 
00869 static int graphkeys_delete_exec(bContext *C, wmOperator *UNUSED(op))
00870 {
00871         bAnimContext ac;
00872         
00873         /* get editor data */
00874         if (ANIM_animdata_get_context(C, &ac) == 0)
00875                 return OPERATOR_CANCELLED;
00876                 
00877         /* delete keyframes */
00878         delete_graph_keys(&ac);
00879         
00880         /* validate keyframes after editing */
00881         ANIM_editkeyframes_refresh(&ac);
00882         
00883         /* set notifier that keyframes have changed */
00884         WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME|NA_EDITED, NULL);
00885         
00886         return OPERATOR_FINISHED;
00887 }
00888  
00889 void GRAPH_OT_delete (wmOperatorType *ot)
00890 {
00891         /* identifiers */
00892         ot->name= "Delete Keyframes";
00893         ot->idname= "GRAPH_OT_delete";
00894         ot->description= "Remove all selected keyframes";
00895         
00896         /* api callbacks */
00897         ot->invoke= WM_operator_confirm;
00898         ot->exec= graphkeys_delete_exec;
00899         ot->poll= graphop_editable_keyframes_poll;
00900         
00901         /* flags */
00902         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
00903 }
00904 
00905 /* ******************** Clean Keyframes Operator ************************* */
00906 
00907 static void clean_graph_keys (bAnimContext *ac, float thresh)
00908 {       
00909         ListBase anim_data = {NULL, NULL};
00910         bAnimListElem *ale;
00911         int filter;
00912         
00913         /* filter data */
00914         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_SEL | ANIMFILTER_CURVESONLY | ANIMFILTER_NODUPLIS);
00915         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
00916         
00917         /* loop through filtered data and clean curves */
00918         for (ale= anim_data.first; ale; ale= ale->next)
00919                 clean_fcurve((FCurve *)ale->key_data, thresh);
00920         
00921         /* free temp data */
00922         BLI_freelistN(&anim_data);
00923 }
00924 
00925 /* ------------------- */
00926 
00927 static int graphkeys_clean_exec(bContext *C, wmOperator *op)
00928 {
00929         bAnimContext ac;
00930         float thresh;
00931         
00932         /* get editor data */
00933         if (ANIM_animdata_get_context(C, &ac) == 0)
00934                 return OPERATOR_CANCELLED;
00935                 
00936         /* get cleaning threshold */
00937         thresh= RNA_float_get(op->ptr, "threshold");
00938         
00939         /* clean keyframes */
00940         clean_graph_keys(&ac, thresh);
00941         
00942         /* validate keyframes after editing */
00943         ANIM_editkeyframes_refresh(&ac);
00944         
00945         /* set notifier that keyframes have changed */
00946         WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME|NA_EDITED, NULL);
00947         
00948         return OPERATOR_FINISHED;
00949 }
00950  
00951 void GRAPH_OT_clean (wmOperatorType *ot)
00952 {
00953         /* identifiers */
00954         ot->name= "Clean Keyframes";
00955         ot->idname= "GRAPH_OT_clean";
00956         ot->description= "Simplify F-Curves by removing closely spaced keyframes";
00957         
00958         /* api callbacks */
00959         //ot->invoke=  // XXX we need that number popup for this! 
00960         ot->exec= graphkeys_clean_exec;
00961         ot->poll= graphop_editable_keyframes_poll;
00962         
00963         /* flags */
00964         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
00965         
00966         /* properties */
00967         ot->prop= RNA_def_float(ot->srna, "threshold", 0.001f, 0.0f, FLT_MAX, "Threshold", "", 0.0f, 1000.0f);
00968 }
00969 
00970 /* ******************** Bake F-Curve Operator *********************** */
00971 /* This operator bakes the data of the selected F-Curves to F-Points */
00972 
00973 /* Bake each F-Curve into a set of samples */
00974 static void bake_graph_curves (bAnimContext *ac, int start, int end)
00975 {       
00976         ListBase anim_data = {NULL, NULL};
00977         bAnimListElem *ale;
00978         int filter;
00979         
00980         /* filter data */
00981         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_SEL | ANIMFILTER_FOREDIT | ANIMFILTER_CURVESONLY | ANIMFILTER_NODUPLIS);
00982         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
00983         
00984         /* loop through filtered data and add keys between selected keyframes on every frame  */
00985         for (ale= anim_data.first; ale; ale= ale->next) {
00986                 FCurve *fcu= (FCurve *)ale->key_data;
00987                 ChannelDriver *driver= fcu->driver;
00988                 
00989                 /* disable driver so that it don't muck up the sampling process */
00990                 fcu->driver= NULL;
00991                 
00992                 /* create samples */
00993                 fcurve_store_samples(fcu, NULL, start, end, fcurve_samplingcb_evalcurve);
00994                 
00995                 /* restore driver */
00996                 fcu->driver= driver;
00997         }
00998         
00999         /* admin and redraws */
01000         BLI_freelistN(&anim_data);
01001 }
01002 
01003 /* ------------------- */
01004 
01005 static int graphkeys_bake_exec(bContext *C, wmOperator *UNUSED(op))
01006 {
01007         bAnimContext ac;
01008         Scene *scene= NULL;
01009         int start, end;
01010         
01011         /* get editor data */
01012         if (ANIM_animdata_get_context(C, &ac) == 0)
01013                 return OPERATOR_CANCELLED;
01014                 
01015         /* for now, init start/end from preview-range extents */
01016         // TODO: add properties for this 
01017         scene= ac.scene;
01018         start= PSFRA;
01019         end= PEFRA;
01020         
01021         /* bake keyframes */
01022         bake_graph_curves(&ac, start, end);
01023         
01024         /* validate keyframes after editing */
01025         ANIM_editkeyframes_refresh(&ac);
01026         
01027         /* set notifier that keyframes have changed */
01028         // NOTE: some distinction between order/number of keyframes and type should be made?
01029         WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME|NA_EDITED, NULL);
01030         
01031         return OPERATOR_FINISHED;
01032 }
01033  
01034 void GRAPH_OT_bake (wmOperatorType *ot)
01035 {
01036         /* identifiers */
01037         ot->name= "Bake Curve";
01038         ot->idname= "GRAPH_OT_bake";
01039         ot->description= "Bake selected F-Curves to a set of sampled points defining a similar curve";
01040         
01041         /* api callbacks */
01042         ot->invoke= WM_operator_confirm; // FIXME...
01043         ot->exec= graphkeys_bake_exec;
01044         ot->poll= graphop_selected_fcurve_poll; 
01045         
01046         /* flags */
01047         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
01048         
01049         // todo: add props for start/end frames
01050 }
01051 
01052 /* ******************** Sound Bake F-Curve Operator *********************** */
01053 /* This operator bakes the given sound to the selected F-Curves */
01054 
01055 /* ------------------- */
01056 
01057 /* Custom data storage passed to the F-Sample-ing function,
01058  * which provides the necessary info for baking the sound
01059  */
01060 typedef struct tSoundBakeInfo {
01061         float *samples;
01062         int length;
01063         int cfra;
01064 } tSoundBakeInfo;
01065 
01066 /* ------------------- */
01067 
01068 /* Sampling callback used to determine the value from the sound to
01069  * save in the F-Curve at the specified frame
01070  */
01071 static float fcurve_samplingcb_sound (FCurve *UNUSED(fcu), void *data, float evaltime)
01072 {
01073         tSoundBakeInfo *sbi= (tSoundBakeInfo *)data;
01074 
01075         int position = evaltime - sbi->cfra;
01076         if((position < 0) || (position >= sbi->length))
01077                 return 0.0f;
01078 
01079         return sbi->samples[position];
01080 }
01081 
01082 /* ------------------- */
01083 
01084 #ifdef WITH_AUDASPACE
01085 static int graphkeys_sound_bake_exec(bContext *C, wmOperator *op)
01086 {
01087         bAnimContext ac;
01088         ListBase anim_data = {NULL, NULL};
01089         bAnimListElem *ale;
01090         int filter;
01091 
01092         tSoundBakeInfo sbi;
01093         Scene *scene= NULL;
01094         int start, end;
01095 
01096         char path[FILE_MAX];
01097 
01098         /* get editor data */
01099         if (ANIM_animdata_get_context(C, &ac) == 0)
01100                 return OPERATOR_CANCELLED;
01101 
01102         RNA_string_get(op->ptr, "filepath", path);
01103 
01104         scene= ac.scene;        /* current scene */
01105 
01106         /* store necessary data for the baking steps */
01107         sbi.samples = AUD_readSoundBuffer(path,
01108                                                                           RNA_float_get(op->ptr, "low"),
01109                                                                           RNA_float_get(op->ptr, "high"),
01110                                                                           RNA_float_get(op->ptr, "attack"),
01111                                                                           RNA_float_get(op->ptr, "release"),
01112                                                                           RNA_float_get(op->ptr, "threshold"),
01113                                                                           RNA_boolean_get(op->ptr, "accumulate"),
01114                                                                           RNA_boolean_get(op->ptr, "use_additive"),
01115                                                                           RNA_boolean_get(op->ptr, "square"),
01116                                                                           RNA_float_get(op->ptr, "sthreshold"),
01117                                                                           FPS, &sbi.length);
01118 
01119         if (sbi.samples == NULL) {
01120                 BKE_report(op->reports, RPT_ERROR, "Unsupported audio format");
01121                 return OPERATOR_CANCELLED;
01122         }
01123 
01124         /* determine extents of the baking */
01125         sbi.cfra = start = CFRA;
01126         end = CFRA + sbi.length - 1;
01127 
01128         /* filter anim channels */
01129         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_SEL | ANIMFILTER_FOREDIT | ANIMFILTER_CURVESONLY | ANIMFILTER_NODUPLIS);
01130         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
01131 
01132         /* loop through all selected F-Curves, replacing its data with the sound samples */
01133         for (ale= anim_data.first; ale; ale= ale->next) {
01134                 FCurve *fcu= (FCurve *)ale->key_data;
01135                 
01136                 /* sample the sound */
01137                 fcurve_store_samples(fcu, &sbi, start, end, fcurve_samplingcb_sound);
01138         }
01139 
01140         /* free sample data */
01141         free(sbi.samples);
01142 
01143         /* admin and redraws */
01144         BLI_freelistN(&anim_data);
01145 
01146         /* validate keyframes after editing */
01147         ANIM_editkeyframes_refresh(&ac);
01148 
01149         /* set notifier that 'keyframes' have changed */
01150         WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME|NA_EDITED, NULL);
01151 
01152         return OPERATOR_FINISHED;
01153 }
01154 
01155 #else //WITH_AUDASPACE
01156 
01157 static int graphkeys_sound_bake_exec(bContext *UNUSED(C), wmOperator *op)
01158 {
01159         BKE_report(op->reports, RPT_ERROR, "Compiled without sound support");
01160 
01161         return OPERATOR_CANCELLED;
01162 }
01163 
01164 #endif //WITH_AUDASPACE
01165 
01166 static int graphkeys_sound_bake_invoke (bContext *C, wmOperator *op, wmEvent *event)
01167 {
01168         bAnimContext ac;
01169 
01170         /* verify editor data */
01171         if (ANIM_animdata_get_context(C, &ac) == 0)
01172                 return OPERATOR_CANCELLED;
01173 
01174         return WM_operator_filesel(C, op, event);
01175 }
01176 
01177 void GRAPH_OT_sound_bake (wmOperatorType *ot)
01178 {
01179         /* identifiers */
01180         ot->name= "Bake Sound to F-Curves";
01181         ot->idname= "GRAPH_OT_sound_bake";
01182         ot->description= "Bakes a sound wave to selected F-Curves";
01183 
01184         /* api callbacks */
01185         ot->invoke= graphkeys_sound_bake_invoke;
01186         ot->exec= graphkeys_sound_bake_exec;
01187         ot->poll= graphop_selected_fcurve_poll;
01188 
01189         /* flags */
01190         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
01191 
01192         /* properties */
01193         WM_operator_properties_filesel(ot, FOLDERFILE|SOUNDFILE|MOVIEFILE, FILE_SPECIAL, FILE_OPENFILE, WM_FILESEL_FILEPATH);
01194         RNA_def_float(ot->srna, "low", 0.0f, 0.0, 100000.0, "Lowest frequency", "", 0.1, 1000.00);
01195         RNA_def_float(ot->srna, "high", 100000.0, 0.0, 100000.0, "Highest frequency", "", 0.1, 1000.00);
01196         RNA_def_float(ot->srna, "attack", 0.005, 0.0, 2.0, "Attack time", "", 0.01, 0.1);
01197         RNA_def_float(ot->srna, "release", 0.2, 0.0, 5.0, "Release time", "", 0.01, 0.2);
01198         RNA_def_float(ot->srna, "threshold", 0.0, 0.0, 1.0, "Threshold", "", 0.01, 0.1);
01199         RNA_def_boolean(ot->srna, "accumulate", 0, "Accumulate", "");
01200         RNA_def_boolean(ot->srna, "use_additive", 0, "Additive", "");
01201         RNA_def_boolean(ot->srna, "square", 0, "Square", "");
01202         RNA_def_float(ot->srna, "sthreshold", 0.1, 0.0, 1.0, "Square Threshold", "", 0.01, 0.1);
01203 }
01204 
01205 /* ******************** Sample Keyframes Operator *********************** */
01206 /* This operator 'bakes' the values of the curve into new keyframes between pairs
01207  * of selected keyframes. It is useful for creating keyframes for tweaking overlap.
01208  */
01209 
01210 /* Evaluates the curves between each selected keyframe on each frame, and keys the value  */
01211 static void sample_graph_keys (bAnimContext *ac)
01212 {       
01213         ListBase anim_data = {NULL, NULL};
01214         bAnimListElem *ale;
01215         int filter;
01216         
01217         /* filter data */
01218         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE| ANIMFILTER_FOREDIT | ANIMFILTER_CURVESONLY | ANIMFILTER_NODUPLIS);
01219         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
01220         
01221         /* loop through filtered data and add keys between selected keyframes on every frame  */
01222         for (ale= anim_data.first; ale; ale= ale->next)
01223                 sample_fcurve((FCurve *)ale->key_data);
01224         
01225         /* admin and redraws */
01226         BLI_freelistN(&anim_data);
01227 }
01228 
01229 /* ------------------- */
01230 
01231 static int graphkeys_sample_exec(bContext *C, wmOperator *UNUSED(op))
01232 {
01233         bAnimContext ac;
01234         
01235         /* get editor data */
01236         if (ANIM_animdata_get_context(C, &ac) == 0)
01237                 return OPERATOR_CANCELLED;
01238         
01239         /* sample keyframes */
01240         sample_graph_keys(&ac);
01241         
01242         /* validate keyframes after editing */
01243         ANIM_editkeyframes_refresh(&ac);
01244         
01245         /* set notifier that keyframes have changed */
01246         WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME|NA_EDITED, NULL);
01247         
01248         return OPERATOR_FINISHED;
01249 }
01250  
01251 void GRAPH_OT_sample (wmOperatorType *ot)
01252 {
01253         /* identifiers */
01254         ot->name= "Sample Keyframes";
01255         ot->idname= "GRAPH_OT_sample";
01256         ot->description= "Add keyframes on every frame between the selected keyframes";
01257         
01258         /* api callbacks */
01259         ot->exec= graphkeys_sample_exec;
01260         ot->poll= graphop_editable_keyframes_poll;
01261         
01262         /* flags */
01263         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
01264 }
01265 
01266 
01267 /* ************************************************************************** */
01268 /* SETTINGS STUFF */
01269 
01270 /* ******************** Set Extrapolation-Type Operator *********************** */
01271 
01272 /* defines for set extrapolation-type for selected keyframes tool */
01273 static EnumPropertyItem prop_graphkeys_expo_types[] = {
01274         {FCURVE_EXTRAPOLATE_CONSTANT, "CONSTANT", 0, "Constant Extrapolation", ""},
01275         {FCURVE_EXTRAPOLATE_LINEAR, "LINEAR", 0, "Linear Extrapolation", ""},
01276         {0, NULL, 0, NULL, NULL}
01277 };
01278 
01279 /* this function is responsible for setting extrapolation mode for keyframes */
01280 static void setexpo_graph_keys(bAnimContext *ac, short mode) 
01281 {
01282         ListBase anim_data = {NULL, NULL};
01283         bAnimListElem *ale;
01284         int filter;
01285         
01286         /* filter data */
01287         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_SEL | ANIMFILTER_FOREDIT | ANIMFILTER_CURVESONLY | ANIMFILTER_NODUPLIS);
01288         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
01289         
01290         /* loop through setting mode per F-Curve */
01291         for (ale= anim_data.first; ale; ale= ale->next) {
01292                 FCurve *fcu= (FCurve *)ale->data;
01293                 fcu->extend= mode;
01294         }
01295         
01296         /* cleanup */
01297         BLI_freelistN(&anim_data);
01298 }
01299 
01300 /* ------------------- */
01301 
01302 static int graphkeys_expo_exec(bContext *C, wmOperator *op)
01303 {
01304         bAnimContext ac;
01305         short mode;
01306         
01307         /* get editor data */
01308         if (ANIM_animdata_get_context(C, &ac) == 0)
01309                 return OPERATOR_CANCELLED;
01310                 
01311         /* get handle setting mode */
01312         mode= RNA_enum_get(op->ptr, "type");
01313         
01314         /* set handle type */
01315         setexpo_graph_keys(&ac, mode);
01316         
01317         /* validate keyframes after editing */
01318         ANIM_editkeyframes_refresh(&ac);
01319         
01320         /* set notifier that keyframe properties have changed */
01321         WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_PROP, NULL);
01322         
01323         return OPERATOR_FINISHED;
01324 }
01325  
01326 void GRAPH_OT_extrapolation_type (wmOperatorType *ot)
01327 {
01328         /* identifiers */
01329         ot->name= "Set Keyframe Extrapolation";
01330         ot->idname= "GRAPH_OT_extrapolation_type";
01331         ot->description= "Set extrapolation mode for selected F-Curves";
01332         
01333         /* api callbacks */
01334         ot->invoke= WM_menu_invoke;
01335         ot->exec= graphkeys_expo_exec;
01336         ot->poll= graphop_editable_keyframes_poll;
01337         
01338         /* flags */
01339         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
01340         
01341         /* id-props */
01342         ot->prop= RNA_def_enum(ot->srna, "type", prop_graphkeys_expo_types, 0, "Type", "");
01343 }
01344 
01345 /* ******************** Set Interpolation-Type Operator *********************** */
01346 
01347 /* this function is responsible for setting interpolation mode for keyframes */
01348 static void setipo_graph_keys(bAnimContext *ac, short mode) 
01349 {
01350         ListBase anim_data = {NULL, NULL};
01351         bAnimListElem *ale;
01352         int filter;
01353         KeyframeEditFunc set_cb= ANIM_editkeyframes_ipo(mode);
01354         
01355         /* filter data */
01356         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVESONLY | ANIMFILTER_NODUPLIS);
01357         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
01358         
01359         /* loop through setting BezTriple interpolation
01360          * Note: we do not supply KeyframeEditData to the looper yet. Currently that's not necessary here...
01361          */
01362         for (ale= anim_data.first; ale; ale= ale->next)
01363                 ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, set_cb, calchandles_fcurve);
01364         
01365         /* cleanup */
01366         BLI_freelistN(&anim_data);
01367 }
01368 
01369 /* ------------------- */
01370 
01371 static int graphkeys_ipo_exec(bContext *C, wmOperator *op)
01372 {
01373         bAnimContext ac;
01374         short mode;
01375         
01376         /* get editor data */
01377         if (ANIM_animdata_get_context(C, &ac) == 0)
01378                 return OPERATOR_CANCELLED;
01379                 
01380         /* get handle setting mode */
01381         mode= RNA_enum_get(op->ptr, "type");
01382         
01383         /* set handle type */
01384         setipo_graph_keys(&ac, mode);
01385         
01386         /* validate keyframes after editing */
01387         ANIM_editkeyframes_refresh(&ac);
01388         
01389         /* set notifier that keyframe properties have changed */
01390         WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_PROP, NULL);
01391         
01392         return OPERATOR_FINISHED;
01393 }
01394  
01395 void GRAPH_OT_interpolation_type (wmOperatorType *ot)
01396 {
01397         /* identifiers */
01398         ot->name= "Set Keyframe Interpolation";
01399         ot->idname= "GRAPH_OT_interpolation_type";
01400         ot->description= "Set interpolation mode for the F-Curve segments starting from the selected keyframes";
01401         
01402         /* api callbacks */
01403         ot->invoke= WM_menu_invoke;
01404         ot->exec= graphkeys_ipo_exec;
01405         ot->poll= graphop_editable_keyframes_poll;
01406         
01407         /* flags */
01408         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
01409         
01410         /* id-props */
01411         ot->prop= RNA_def_enum(ot->srna, "type", beztriple_interpolation_mode_items, 0, "Type", "");
01412 }
01413 
01414 /* ******************** Set Handle-Type Operator *********************** */
01415 
01416 /* ------------------- */
01417 
01418 /* this function is responsible for setting handle-type of selected keyframes */
01419 static void sethandles_graph_keys(bAnimContext *ac, short mode) 
01420 {
01421         ListBase anim_data = {NULL, NULL};
01422         bAnimListElem *ale;
01423         int filter;
01424         
01425         KeyframeEditFunc edit_cb= ANIM_editkeyframes_handles(mode);
01426         KeyframeEditFunc sel_cb= ANIM_editkeyframes_ok(BEZT_OK_SELECTED);
01427         
01428         /* filter data */
01429         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVESONLY | ANIMFILTER_NODUPLIS);
01430         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
01431         
01432         /* loop through setting flags for handles 
01433          * Note: we do not supply KeyframeEditData to the looper yet. Currently that's not necessary here...
01434          */
01435         for (ale= anim_data.first; ale; ale= ale->next) {
01436                 FCurve *fcu= (FCurve *)ale->key_data;
01437                 
01438                 /* any selected keyframes for editing? */
01439                 if (ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, sel_cb, NULL)) {
01440                         /* for auto/auto-clamped, toggle the auto-handles flag on the F-Curve */
01441                         if (mode == HD_AUTO_ANIM)
01442                                 fcu->flag |= FCURVE_AUTO_HANDLES;
01443                         else if (mode == HD_AUTO)
01444                                 fcu->flag &= ~FCURVE_AUTO_HANDLES;
01445                         
01446                         /* change type of selected handles */
01447                         ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, edit_cb, calchandles_fcurve);
01448                 }
01449         }
01450         
01451         /* cleanup */
01452         BLI_freelistN(&anim_data);
01453 }
01454 /* ------------------- */
01455 
01456 static int graphkeys_handletype_exec(bContext *C, wmOperator *op)
01457 {
01458         bAnimContext ac;
01459         short mode;
01460         
01461         /* get editor data */
01462         if (ANIM_animdata_get_context(C, &ac) == 0)
01463                 return OPERATOR_CANCELLED;
01464                 
01465         /* get handle setting mode */
01466         mode= RNA_enum_get(op->ptr, "type");
01467         
01468         /* set handle type */
01469         sethandles_graph_keys(&ac, mode);
01470         
01471         /* validate keyframes after editing */
01472         ANIM_editkeyframes_refresh(&ac);
01473         
01474         /* set notifier that keyframe properties have changed */
01475         WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_PROP, NULL);
01476         
01477         return OPERATOR_FINISHED;
01478 }
01479  
01480  void GRAPH_OT_handle_type (wmOperatorType *ot)
01481 {
01482          /* sync with editcurve_handle_type_items */
01483          static EnumPropertyItem graphkeys_handle_type_items[] = {
01484                  {HD_AUTO, "AUTO", 0, "Automatic", "Handles that are automatically adjusted upon moving the keyframe. Whole curve"},
01485                  {HD_VECT, "VECTOR", 0, "Vector", ""},
01486                  {HD_ALIGN, "ALIGNED", 0, "Aligned", ""},
01487                  {HD_FREE, "FREE_ALIGN", 0, "Free", ""},
01488                  {HD_AUTO_ANIM, "ANIM_CLAMPED", 0, "Auto Clamped", "Auto handles clamped to not overshoot. Whole curve"},
01489                  {0, NULL, 0, NULL, NULL}};      
01490 
01491         /* identifiers */
01492         ot->name= "Set Keyframe Handle Type";
01493         ot->idname= "GRAPH_OT_handle_type";
01494         ot->description= "Set type of handle for selected keyframes";
01495         
01496         /* api callbacks */
01497         ot->invoke= WM_menu_invoke;
01498         ot->exec= graphkeys_handletype_exec;
01499         ot->poll= graphop_editable_keyframes_poll;
01500         
01501         /* flags */
01502         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
01503         
01504         /* id-props */
01505         ot->prop= RNA_def_enum(ot->srna, "type", graphkeys_handle_type_items, 0, "Type", "");
01506 }
01507 
01508 /* ************************************************************************** */
01509 /* TRANSFORM STUFF */
01510 
01511 /* ***************** 'Euler Filter' Operator **************************** */
01512 /* Euler filter tools (as seen in Maya), are necessary for working with 'baked'
01513  * rotation curves (with Euler rotations). The main purpose of such tools is to
01514  * resolve any discontinuities that may arise in the curves due to the clamping
01515  * of values to -180 degrees to 180 degrees.
01516  */
01517 
01518 /* set of three euler-rotation F-Curves */
01519 typedef struct tEulerFilter {
01520         struct tEulerFilter *next, *prev;
01521         
01522         ID *id;                                                 /* ID-block which owns the channels */
01523         FCurve *(fcurves[3]);                   /* 3 Pointers to F-Curves */    
01524         char *rna_path;                                 /* Pointer to one of the RNA Path's used by one of the F-Curves */
01525 } tEulerFilter;
01526  
01527 static int graphkeys_euler_filter_exec (bContext *C, wmOperator *op)
01528 {
01529         bAnimContext ac;
01530         
01531         ListBase anim_data= {NULL, NULL};
01532         bAnimListElem *ale;
01533         int filter;
01534         
01535         ListBase eulers = {NULL, NULL};
01536         tEulerFilter *euf= NULL;
01537         int groups=0, failed=0;
01538         
01539         /* get editor data */
01540         if (ANIM_animdata_get_context(C, &ac) == 0)
01541                 return OPERATOR_CANCELLED;
01542                 
01543         /* The process is done in two passes:
01544          *       1) Sets of three related rotation curves are identified from the selected channels,
01545          *              and are stored as a single 'operation unit' for the next step
01546          *       2) Each set of three F-Curves is processed for each keyframe, with the values being
01547          *              processed as necessary
01548          */
01549          
01550         /* step 1: extract only the rotation f-curves */
01551         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_CURVESONLY | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
01552         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
01553         
01554         for (ale= anim_data.first; ale; ale= ale->next) {
01555                 FCurve *fcu = (FCurve *)ale->data;
01556                 
01557                 /* check if this is an appropriate F-Curve 
01558                  *      - only rotation curves
01559                  *      - for pchan curves, make sure we're only using the euler curves
01560                  */
01561                 if (strstr(fcu->rna_path, "rotation_euler") == NULL)
01562                         continue;
01563                 else if (ELEM3(fcu->array_index, 0, 1, 2) == 0) {
01564                         BKE_reportf(op->reports, RPT_WARNING,
01565                                 "Euler Rotation F-Curve has invalid index (ID='%s', Path='%s', Index=%d)", 
01566                                 (ale->id)? ale->id->name:"<No ID>", fcu->rna_path, fcu->array_index);
01567                         continue;
01568                 }
01569                 
01570                 /* optimisation: assume that xyz curves will always be stored consecutively,
01571                  * so if the paths or the ID's don't match up, then a curve needs to be added 
01572                  * to a new group
01573                  */
01574                 if ((euf) && (euf->id == ale->id) && (strcmp(euf->rna_path, fcu->rna_path)==0)) {
01575                         /* this should be fine to add to the existing group then */
01576                         euf->fcurves[fcu->array_index]= fcu;
01577                 }
01578                 else {
01579                         /* just add to a new block */
01580                         euf= MEM_callocN(sizeof(tEulerFilter), "tEulerFilter");
01581                         BLI_addtail(&eulers, euf);
01582                         groups++;
01583                         
01584                         euf->id= ale->id;
01585                         euf->rna_path = fcu->rna_path; /* this should be safe, since we're only using it for a short time */
01586                         euf->fcurves[fcu->array_index]= fcu;
01587                 }
01588         }
01589         BLI_freelistN(&anim_data);
01590         
01591         if (groups == 0) {
01592                 BKE_report(op->reports, RPT_WARNING, "No Euler Rotation F-Curves to fix up");
01593                 return OPERATOR_CANCELLED;
01594         }
01595         
01596         /* step 2: go through each set of curves, processing the values at each keyframe 
01597          *      - it is assumed that there must be a full set of keyframes at each keyframe position
01598          */
01599         for (euf= eulers.first; euf; euf= euf->next) {
01600                 int f;
01601                 
01602                 /* sanity check: ensure that there are enough F-Curves to work on in this group */
01603                 // TODO: also enforce assumption that there be a full set of keyframes at each position by ensuring that totvert counts are same?
01604                 if (ELEM3(NULL, euf->fcurves[0], euf->fcurves[1], euf->fcurves[2])) {
01605                         /* report which components are missing */
01606                         BKE_reportf(op->reports, RPT_WARNING,
01607                                 "Missing %s%s%s component(s) of euler rotation for ID='%s' and RNA-Path='%s'",
01608                                 (euf->fcurves[0]==NULL)? "X":"",
01609                                 (euf->fcurves[1]==NULL)? "Y":"",
01610                                 (euf->fcurves[2]==NULL)? "Z":"",
01611                                 euf->id->name, euf->rna_path);
01612                                 
01613                         /* keep track of number of failed sets, and carry on to next group */
01614                         failed++;
01615                         continue;
01616                 }
01617                 
01618                 /* simple method: just treat any difference between keys of greater than 180 degrees as being a flip */
01619                 // FIXME: there are more complicated methods that will be needed to fix more cases than just some
01620                 for (f = 0; f < 3; f++) {
01621                         FCurve *fcu = euf->fcurves[f];
01622                         BezTriple *bezt, *prev=NULL;
01623                         unsigned int i;
01624                         
01625                         /* skip if not enough vets to do a decent analysis of... */
01626                         if (fcu->totvert <= 2)
01627                                 continue;
01628                         
01629                         /* prev follows bezt, bezt = "current" point to be fixed */
01630                         for (i=0, bezt=fcu->bezt; i < fcu->totvert; i++, prev=bezt, bezt++) {
01631                                 /* our method depends on determining a "difference" from the previous vert */
01632                                 if (prev == NULL)
01633                                         continue;
01634                                 
01635                                 /* > 180 degree flip? */
01636                                 if (fabs(prev->vec[1][1] - bezt->vec[1][1]) >= M_PI) {
01637                                         /* 360 degrees to add/subtract frame value until difference is acceptably small that there's no more flip */
01638                                         const float fac = 2.0f * (float)M_PI;
01639                                         
01640                                         if (prev->vec[1][1] > bezt->vec[1][1]) {
01641                                                 while (fabsf(bezt->vec[1][1] - prev->vec[1][1]) >= (float)M_PI) {
01642                                                         bezt->vec[0][1] += fac;
01643                                                         bezt->vec[1][1] += fac;
01644                                                         bezt->vec[2][1] += fac;
01645                                                 }
01646                                         }
01647                                         else /* if (prev->vec[1][1] < bezt->vec[1][1]) */ {
01648                                                 while (fabsf(bezt->vec[1][1] - prev->vec[1][1]) >= (float)M_PI) {
01649                                                         bezt->vec[0][1] -= fac;
01650                                                         bezt->vec[1][1] -= fac;
01651                                                         bezt->vec[2][1] -= fac;
01652                                                 }
01653                                         }
01654                                 }
01655                         }
01656                 }
01657         }
01658         BLI_freelistN(&eulers);
01659         
01660         /* updates + finishing warnings */
01661         if (failed == groups) {
01662                 BKE_report(op->reports, RPT_ERROR, 
01663                         "No Euler Rotations could be corrected. Ensure each rotation has keys for all components, and that F-Curves for these are in consecutive XYZ order and selected.");
01664                 return OPERATOR_CANCELLED;
01665         }
01666         else {
01667                 if (failed) {
01668                         BKE_report(op->reports, RPT_ERROR,
01669                                 "Some Euler Rotations couldn't be corrected due to missing/unselected/out-of-order F-Curves. Ensure each rotation has keys for all components, and that F-Curves for these are in consecutive XYZ order and selected.");
01670                 }
01671                 
01672                 /* validate keyframes after editing */
01673                 ANIM_editkeyframes_refresh(&ac);
01674                 
01675                 /* set notifier that keyframes have changed */
01676                 WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME|NA_EDITED, NULL);
01677                 
01678                 /* done at last */
01679                 return OPERATOR_FINISHED;
01680         }
01681 }
01682  
01683 void GRAPH_OT_euler_filter (wmOperatorType *ot)
01684 {
01685         /* identifiers */
01686         ot->name= "Euler Discontinuity Filter";
01687         ot->idname= "GRAPH_OT_euler_filter";
01688         ot->description= "Fixes the most common causes of gimbal lock in the selected Euler Rotation F-Curves";
01689         
01690         /* api callbacks */
01691         ot->exec= graphkeys_euler_filter_exec;
01692         ot->poll= graphop_editable_keyframes_poll;
01693         
01694         /* flags */
01695         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
01696 }
01697 
01698 /* ***************** Jump to Selected Frames Operator *********************** */
01699 
01700 /* snap current-frame indicator to 'average time' of selected keyframe */
01701 static int graphkeys_framejump_exec(bContext *C, wmOperator *UNUSED(op))
01702 {
01703         bAnimContext ac;
01704         ListBase anim_data= {NULL, NULL};
01705         bAnimListElem *ale;
01706         int filter;
01707         KeyframeEditData ked;
01708         
01709         /* get editor data */
01710         if (ANIM_animdata_get_context(C, &ac) == 0)
01711                 return OPERATOR_CANCELLED;
01712         
01713         /* init edit data */
01714         memset(&ked, 0, sizeof(KeyframeEditData));
01715         
01716         /* loop over action data, averaging values */
01717         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_CURVESONLY | ANIMFILTER_NODUPLIS);
01718         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
01719         
01720         for (ale= anim_data.first; ale; ale= ale->next) {
01721                 AnimData *adt= ANIM_nla_mapping_get(&ac, ale);
01722                 
01723                 /* apply unit corrections */
01724                 ANIM_unit_mapping_apply_fcurve(ac.scene, ale->id, ale->key_data, ANIM_UNITCONV_ONLYKEYS);
01725                 
01726                 if (adt) {
01727                         ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1); 
01728                         ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, bezt_calc_average, NULL);
01729                         ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1); 
01730                 }
01731                 else
01732                         ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, bezt_calc_average, NULL);
01733                 
01734                 /* unapply unit corrections */
01735                 ANIM_unit_mapping_apply_fcurve(ac.scene, ale->id, ale->key_data, ANIM_UNITCONV_RESTORE|ANIM_UNITCONV_ONLYKEYS);
01736         }
01737         
01738         BLI_freelistN(&anim_data);
01739         
01740         /* set the new current frame and cursor values, based on the average time and value */
01741         if (ked.i1) {
01742                 SpaceIpo *sipo= ac.sa->spacedata.first;
01743                 Scene *scene= ac.scene;
01744                 
01745                 /* take the average values, rounding to the nearest int for the current frame */
01746                 CFRA= (int)floor((ked.f1 / ked.i1) + 0.5f);
01747                 SUBFRA= 0.f;
01748                 sipo->cursorVal= ked.f2 / (float)ked.i1;
01749         }
01750         
01751         /* set notifier that things have changed */
01752         WM_event_add_notifier(C, NC_SCENE|ND_FRAME, ac.scene);
01753         
01754         return OPERATOR_FINISHED;
01755 }
01756 
01757 void GRAPH_OT_frame_jump (wmOperatorType *ot)
01758 {
01759         /* identifiers */
01760         ot->name= "Jump to Frame";
01761         ot->idname= "GRAPH_OT_frame_jump";
01762         ot->description= "Set the current frame to the average frame of the selected keyframes";
01763         
01764         /* api callbacks */
01765         ot->exec= graphkeys_framejump_exec;
01766         ot->poll= graphop_visible_keyframes_poll;
01767         
01768         /* flags */
01769         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
01770 }
01771 
01772 /* ******************** Snap Keyframes Operator *********************** */
01773 
01774 /* defines for snap keyframes tool */
01775 static EnumPropertyItem prop_graphkeys_snap_types[] = {
01776         {GRAPHKEYS_SNAP_CFRA, "CFRA", 0, "Current Frame", ""},
01777         {GRAPHKEYS_SNAP_VALUE, "VALUE", 0, "Cursor Value", ""},
01778         {GRAPHKEYS_SNAP_NEAREST_FRAME, "NEAREST_FRAME", 0, "Nearest Frame", ""}, // XXX as single entry?
01779         {GRAPHKEYS_SNAP_NEAREST_SECOND, "NEAREST_SECOND", 0, "Nearest Second", ""}, // XXX as single entry?
01780         {GRAPHKEYS_SNAP_NEAREST_MARKER, "NEAREST_MARKER", 0, "Nearest Marker", ""},
01781         {GRAPHKEYS_SNAP_HORIZONTAL, "HORIZONTAL", 0, "Flatten Handles", ""},
01782         {0, NULL, 0, NULL, NULL}
01783 };
01784 
01785 /* this function is responsible for snapping keyframes to frame-times */
01786 static void snap_graph_keys(bAnimContext *ac, short mode) 
01787 {
01788         ListBase anim_data = {NULL, NULL};
01789         bAnimListElem *ale;
01790         int filter;
01791         
01792         KeyframeEditData ked;
01793         KeyframeEditFunc edit_cb;
01794         
01795         /* filter data */
01796         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVESONLY | ANIMFILTER_NODUPLIS);
01797         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
01798         
01799         /* get beztriple editing callbacks */
01800         edit_cb= ANIM_editkeyframes_snap(mode);
01801         
01802         memset(&ked, 0, sizeof(KeyframeEditData)); 
01803         ked.scene= ac->scene;
01804         if (mode == GRAPHKEYS_SNAP_NEAREST_MARKER) {
01805                 ked.list.first= (ac->markers) ? ac->markers->first : NULL;
01806                 ked.list.last= (ac->markers) ? ac->markers->last : NULL;
01807         }
01808         else if (mode == GRAPHKEYS_SNAP_VALUE) {
01809                 SpaceIpo *sipo= (SpaceIpo *)ac->sa->spacedata.first;
01810                 ked.f1= (sipo) ? sipo->cursorVal : 0.0f;
01811         }
01812         
01813         /* snap keyframes */
01814         for (ale= anim_data.first; ale; ale= ale->next) {
01815                 AnimData *adt= ANIM_nla_mapping_get(ac, ale);
01816                 
01817                 /* apply unit corrections */
01818                 ANIM_unit_mapping_apply_fcurve(ac->scene, ale->id, ale->key_data, 0);
01819                 
01820                 if (adt) {
01821                         ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1); 
01822                         ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve);
01823                         ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
01824                 }
01825                 else 
01826                         ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve);
01827                         
01828                 /* apply unit corrections */
01829                 ANIM_unit_mapping_apply_fcurve(ac->scene, ale->id, ale->key_data, ANIM_UNITCONV_RESTORE);
01830         }
01831         
01832         BLI_freelistN(&anim_data);
01833 }
01834 
01835 /* ------------------- */
01836 
01837 static int graphkeys_snap_exec(bContext *C, wmOperator *op)
01838 {
01839         bAnimContext ac;
01840         short mode;
01841         
01842         /* get editor data */
01843         if (ANIM_animdata_get_context(C, &ac) == 0)
01844                 return OPERATOR_CANCELLED;
01845                 
01846         /* get snapping mode */
01847         mode= RNA_enum_get(op->ptr, "type");
01848         
01849         /* snap keyframes */
01850         snap_graph_keys(&ac, mode);
01851         
01852         /* validate keyframes after editing */
01853         ANIM_editkeyframes_refresh(&ac);
01854         
01855         /* set notifier that keyframes have changed */
01856         WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME|NA_EDITED, NULL);
01857         
01858         return OPERATOR_FINISHED;
01859 }
01860  
01861 void GRAPH_OT_snap (wmOperatorType *ot)
01862 {
01863         /* identifiers */
01864         ot->name= "Snap Keys";
01865         ot->idname= "GRAPH_OT_snap";
01866         ot->description= "Snap selected keyframes to the chosen times/values";
01867         
01868         /* api callbacks */
01869         ot->invoke= WM_menu_invoke;
01870         ot->exec= graphkeys_snap_exec;
01871         ot->poll= graphop_editable_keyframes_poll;
01872         
01873         /* flags */
01874         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
01875         
01876         /* id-props */
01877         ot->prop= RNA_def_enum(ot->srna, "type", prop_graphkeys_snap_types, 0, "Type", "");
01878 }
01879 
01880 /* ******************** Mirror Keyframes Operator *********************** */
01881 
01882 /* defines for mirror keyframes tool */
01883 static EnumPropertyItem prop_graphkeys_mirror_types[] = {
01884         {GRAPHKEYS_MIRROR_CFRA, "CFRA", 0, "By Times over Current Frame", ""},
01885         {GRAPHKEYS_MIRROR_VALUE, "VALUE", 0, "By Values over Cursor Value", ""},
01886         {GRAPHKEYS_MIRROR_YAXIS, "YAXIS", 0, "By Times over Time=0", ""},
01887         {GRAPHKEYS_MIRROR_XAXIS, "XAXIS", 0, "By Values over Value=0", ""},
01888         {GRAPHKEYS_MIRROR_MARKER, "MARKER", 0, "By Times over First Selected Marker", ""},
01889         {0, NULL, 0, NULL, NULL}
01890 };
01891 
01892 /* this function is responsible for mirroring keyframes */
01893 static void mirror_graph_keys(bAnimContext *ac, short mode) 
01894 {
01895         ListBase anim_data = {NULL, NULL};
01896         bAnimListElem *ale;
01897         int filter;
01898         
01899         KeyframeEditData ked;
01900         KeyframeEditFunc edit_cb;
01901         
01902         /* get beztriple editing callbacks */
01903         edit_cb= ANIM_editkeyframes_mirror(mode);
01904         
01905         memset(&ked, 0, sizeof(KeyframeEditData)); 
01906         ked.scene= ac->scene;
01907         
01908         /* for 'first selected marker' mode, need to find first selected marker first! */
01909         // XXX should this be made into a helper func in the API?
01910         if (mode == GRAPHKEYS_MIRROR_MARKER) {
01911                 TimeMarker *marker= NULL;
01912                 
01913                 /* find first selected marker */
01914                 marker= ED_markers_get_first_selected(ac->markers);
01915                 
01916                 /* store marker's time (if available) */
01917                 if (marker)
01918                         ked.f1= (float)marker->frame;
01919                 else
01920                         return;
01921         }
01922         else if (mode == GRAPHKEYS_MIRROR_VALUE) {
01923                 SpaceIpo *sipo= (SpaceIpo *)ac->sa->spacedata.first;
01924                 ked.f1= (sipo) ? sipo->cursorVal : 0.0f;
01925         }
01926         
01927         /* filter data */
01928         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE| ANIMFILTER_FOREDIT | ANIMFILTER_CURVESONLY | ANIMFILTER_NODUPLIS);
01929         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
01930         
01931         /* mirror keyframes */
01932         for (ale= anim_data.first; ale; ale= ale->next) {
01933                 AnimData *adt= ANIM_nla_mapping_get(ac, ale);
01934                 
01935                 /* apply unit corrections */
01936                 ANIM_unit_mapping_apply_fcurve(ac->scene, ale->id, ale->key_data, ANIM_UNITCONV_ONLYKEYS);
01937                 
01938                 if (adt) {
01939                         ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1); 
01940                         ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve);
01941                         ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
01942                 }
01943                 else 
01944                         ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve);
01945                         
01946                 /* unapply unit corrections */
01947                 ANIM_unit_mapping_apply_fcurve(ac->scene, ale->id, ale->key_data, ANIM_UNITCONV_ONLYKEYS|ANIM_UNITCONV_RESTORE);
01948         }
01949         
01950         BLI_freelistN(&anim_data);
01951 }
01952 
01953 /* ------------------- */
01954 
01955 static int graphkeys_mirror_exec(bContext *C, wmOperator *op)
01956 {
01957         bAnimContext ac;
01958         short mode;
01959         
01960         /* get editor data */
01961         if (ANIM_animdata_get_context(C, &ac) == 0)
01962                 return OPERATOR_CANCELLED;
01963                 
01964         /* get mirroring mode */
01965         mode= RNA_enum_get(op->ptr, "type");
01966         
01967         /* mirror keyframes */
01968         mirror_graph_keys(&ac, mode);
01969         
01970         /* validate keyframes after editing */
01971         ANIM_editkeyframes_refresh(&ac);
01972         
01973         /* set notifier that keyframes have changed */
01974         WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME|NA_EDITED, NULL);
01975         
01976         return OPERATOR_FINISHED;
01977 }
01978  
01979 void GRAPH_OT_mirror (wmOperatorType *ot)
01980 {
01981         /* identifiers */
01982         ot->name= "Mirror Keys";
01983         ot->idname= "GRAPH_OT_mirror";
01984         ot->description= "Flip selected keyframes over the selected mirror line";
01985         
01986         /* api callbacks */
01987         ot->invoke= WM_menu_invoke;
01988         ot->exec= graphkeys_mirror_exec;
01989         ot->poll= graphop_editable_keyframes_poll;
01990         
01991         /* flags */
01992         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
01993         
01994         /* id-props */
01995         ot->prop= RNA_def_enum(ot->srna, "type", prop_graphkeys_mirror_types, 0, "Type", "");
01996 }
01997 
01998 /* ******************** Smooth Keyframes Operator *********************** */
01999 
02000 static int graphkeys_smooth_exec(bContext *C, wmOperator *UNUSED(op))
02001 {
02002         bAnimContext ac;
02003         ListBase anim_data = {NULL, NULL};
02004         bAnimListElem *ale;
02005         int filter;
02006         
02007         /* get editor data */
02008         if (ANIM_animdata_get_context(C, &ac) == 0)
02009                 return OPERATOR_CANCELLED;
02010         
02011         /* filter data */
02012         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE| ANIMFILTER_FOREDIT | ANIMFILTER_CURVESONLY | ANIMFILTER_NODUPLIS);
02013         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
02014         
02015         /* smooth keyframes */
02016         for (ale= anim_data.first; ale; ale= ale->next) {
02017                 /* For now, we can only smooth by flattening handles AND smoothing curve values.
02018                  * Perhaps the mode argument could be removed, as that functionality is offerred through 
02019                  * Snap->Flatten Handles anyway.
02020                  */
02021                 smooth_fcurve(ale->key_data);
02022         }
02023         BLI_freelistN(&anim_data);
02024         
02025         /* validate keyframes after editing */
02026         ANIM_editkeyframes_refresh(&ac);
02027         
02028         /* set notifier that keyframes have changed */
02029         WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME|NA_EDITED, NULL);
02030         
02031         return OPERATOR_FINISHED;
02032 }
02033  
02034 void GRAPH_OT_smooth (wmOperatorType *ot)
02035 {
02036         /* identifiers */
02037         ot->name= "Smooth Keys";
02038         ot->idname= "GRAPH_OT_smooth";
02039         ot->description= "Apply weighted moving means to make selected F-Curves less bumpy";
02040         
02041         /* api callbacks */
02042         ot->exec= graphkeys_smooth_exec;
02043         ot->poll= graphop_editable_keyframes_poll;
02044         
02045         /* flags */
02046         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
02047 }
02048 
02049 /* ************************************************************************** */
02050 /* F-CURVE MODIFIERS */
02051 
02052 /* ******************** Add F-Modifier Operator *********************** */
02053 
02054 /* present a special customised popup menu for this, with some filtering */
02055 static int graph_fmodifier_add_invoke (bContext *C, wmOperator *op, wmEvent *UNUSED(event))
02056 {
02057         uiPopupMenu *pup;
02058         uiLayout *layout;
02059         int i;
02060         
02061         pup= uiPupMenuBegin(C, "Add F-Curve Modifier", ICON_NONE);
02062         layout= uiPupMenuLayout(pup);
02063         
02064         /* start from 1 to skip the 'Invalid' modifier type */
02065         for (i = 1; i < FMODIFIER_NUM_TYPES; i++) {
02066                 FModifierTypeInfo *fmi= get_fmodifier_typeinfo(i);
02067                 PointerRNA props_ptr;
02068                 
02069                 /* check if modifier is valid for this context */
02070                 if (fmi == NULL)
02071                         continue;
02072                 
02073                 /* create operator menu item with relevant properties filled in */
02074                 props_ptr= uiItemFullO(layout, "GRAPH_OT_fmodifier_add", fmi->name, ICON_NONE, NULL, WM_OP_EXEC_REGION_WIN, UI_ITEM_O_RETURN_PROPS);
02075                         /* the only thing that gets set from the menu is the type of F-Modifier to add */
02076                 RNA_enum_set(&props_ptr, "type", i);
02077                         /* the following properties are just repeats of existing ones... */
02078                 RNA_boolean_set(&props_ptr, "only_active", RNA_boolean_get(op->ptr, "only_active"));
02079         }
02080         uiItemS(layout);
02081         
02082         uiPupMenuEnd(C, pup);
02083         
02084         return OPERATOR_CANCELLED;
02085 }
02086 
02087 static int graph_fmodifier_add_exec(bContext *C, wmOperator *op)
02088 {
02089         bAnimContext ac;
02090         ListBase anim_data = {NULL, NULL};
02091         bAnimListElem *ale;
02092         int filter;
02093         short type;
02094         
02095         /* get editor data */
02096         if (ANIM_animdata_get_context(C, &ac) == 0)
02097                 return OPERATOR_CANCELLED;
02098         
02099         /* get type of modifier to add */
02100         type= RNA_enum_get(op->ptr, "type");
02101         
02102         /* filter data */
02103         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVESONLY | ANIMFILTER_NODUPLIS);
02104         if (RNA_boolean_get(op->ptr, "only_active"))
02105                 filter |= ANIMFILTER_ACTIVE; // FIXME: enforce in this case only a single channel to get handled?
02106         else
02107                 filter |= (ANIMFILTER_SEL|ANIMFILTER_CURVEVISIBLE);
02108         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
02109         
02110         /* add f-modifier to each curve */
02111         for (ale= anim_data.first; ale; ale= ale->next) {
02112                 FCurve *fcu= (FCurve *)ale->data;
02113                 FModifier *fcm;
02114                 
02115                 /* add F-Modifier of specified type to active F-Curve, and make it the active one */
02116                 fcm= add_fmodifier(&fcu->modifiers, type);
02117                 if (fcm)
02118                         set_active_fmodifier(&fcu->modifiers, fcm);
02119                 else {
02120                         BKE_report(op->reports, RPT_ERROR, "Modifier couldn't be added. See console for details.");
02121                         break;
02122                 }
02123         }
02124         BLI_freelistN(&anim_data);
02125         
02126         /* validate keyframes after editing */
02127         ANIM_editkeyframes_refresh(&ac);
02128         
02129         /* set notifier that things have changed */
02130         // FIXME: this really isn't the best description for it...
02131         WM_event_add_notifier(C, NC_ANIMATION, NULL);
02132         
02133         return OPERATOR_FINISHED;
02134 }
02135  
02136 void GRAPH_OT_fmodifier_add (wmOperatorType *ot)
02137 {
02138         /* identifiers */
02139         ot->name= "Add F-Curve Modifier";
02140         ot->idname= "GRAPH_OT_fmodifier_add";
02141         ot->description= "Add F-Modifiers to the selected F-Curves";
02142         
02143         /* api callbacks */
02144         ot->invoke= graph_fmodifier_add_invoke;
02145         ot->exec= graph_fmodifier_add_exec;
02146         ot->poll= graphop_selected_fcurve_poll; 
02147         
02148         /* flags */
02149         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
02150         
02151         /* id-props */
02152         ot->prop= RNA_def_enum(ot->srna, "type", fmodifier_type_items, 0, "Type", "");
02153         RNA_def_boolean(ot->srna, "only_active", 1, "Only Active", "Only add F-Modifier to active F-Curve.");
02154 }
02155 
02156 /* ******************** Copy F-Modifiers Operator *********************** */
02157 
02158 static int graph_fmodifier_copy_exec(bContext *C, wmOperator *op)
02159 {
02160         bAnimContext ac;
02161         bAnimListElem *ale;
02162         short ok = 0;
02163         
02164         /* get editor data */
02165         if (ANIM_animdata_get_context(C, &ac) == 0)
02166                 return OPERATOR_CANCELLED;
02167         
02168         /* clear buffer first */
02169         free_fmodifiers_copybuf();
02170         
02171         /* get the active F-Curve */
02172         ale= get_active_fcurve_channel(&ac);
02173         
02174         /* if this exists, call the copy F-Modifiers API function */
02175         if (ale && ale->data) {
02176                 FCurve *fcu= (FCurve *)ale->data;
02177                 
02178                 // TODO: when 'active' vs 'all' boolean is added, change last param!
02179                 ok= ANIM_fmodifiers_copy_to_buf(&fcu->modifiers, 0);
02180                 
02181                 /* free temp data now */
02182                 MEM_freeN(ale);
02183         }
02184         
02185         /* successful or not? */
02186         if (ok == 0) {
02187                 BKE_report(op->reports, RPT_ERROR, "No F-Modifiers available to be copied");
02188                 return OPERATOR_CANCELLED;
02189         }
02190         else
02191                 return OPERATOR_FINISHED;
02192 }
02193  
02194 void GRAPH_OT_fmodifier_copy (wmOperatorType *ot)
02195 {
02196         /* identifiers */
02197         ot->name= "Copy F-Modifiers";
02198         ot->idname= "GRAPH_OT_fmodifier_copy";
02199         ot->description= "Copy the F-Modifier(s) of the active F-Curve.";
02200         
02201         /* api callbacks */
02202         ot->exec= graph_fmodifier_copy_exec;
02203         ot->poll= graphop_active_fcurve_poll; 
02204         
02205         /* flags */
02206         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
02207         
02208         /* id-props */
02209         //ot->prop = RNA_def_boolean(ot->srna, "all", 1, "All F-Modifiers", "Copy all the F-Modifiers, instead of just the active one");
02210 }
02211 
02212 /* ******************** Paste F-Modifiers Operator *********************** */
02213 
02214 static int graph_fmodifier_paste_exec(bContext *C, wmOperator *op)
02215 {
02216         bAnimContext ac;
02217         ListBase anim_data = {NULL, NULL};
02218         bAnimListElem *ale;
02219         int filter, ok=0;
02220         
02221         /* get editor data */
02222         if (ANIM_animdata_get_context(C, &ac) == 0)
02223                 return OPERATOR_CANCELLED;
02224         
02225         /* filter data */
02226         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_SEL | ANIMFILTER_FOREDIT | ANIMFILTER_CURVESONLY);
02227         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
02228         
02229         /* paste modifiers */
02230         for (ale = anim_data.first; ale; ale = ale->next) {
02231                 FCurve *fcu= (FCurve *)ale->data;
02232                 
02233                 // TODO: do we want to replace existing modifiers? add user pref for that!
02234                 ok += ANIM_fmodifiers_paste_from_buf(&fcu->modifiers, 0);
02235         }
02236         
02237         /* clean up */
02238         BLI_freelistN(&anim_data);
02239         
02240         /* successful or not? */
02241         if (ok) {
02242                 /* validate keyframes after editing */
02243                 ANIM_editkeyframes_refresh(&ac);
02244                 
02245                 /* set notifier that keyframes have changed */
02246                 WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME|NA_EDITED, NULL);
02247                 
02248                 return OPERATOR_FINISHED;
02249         }
02250         else {
02251                 BKE_report(op->reports, RPT_ERROR, "No F-Modifiers to paste");
02252                 return OPERATOR_CANCELLED;
02253         }
02254 }
02255  
02256 void GRAPH_OT_fmodifier_paste (wmOperatorType *ot)
02257 {
02258         /* identifiers */
02259         ot->name= "Paste F-Modifiers";
02260         ot->idname= "GRAPH_OT_fmodifier_paste";
02261         ot->description= "Add copied F-Modifiers to the selected F-Curves";
02262         
02263         /* api callbacks */
02264         ot->exec= graph_fmodifier_paste_exec;
02265         ot->poll= graphop_editable_keyframes_poll;
02266         
02267         /* flags */
02268         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
02269 }
02270 
02271 /* ************************************************************************** */