Merge with trunk r39928
[blender-staging.git] / source / blender / editors / armature / poseSlide.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2009, Blender Foundation, Joshua Leung
19  * This is a new part of Blender
20  *
21  * Contributor(s): Joshua Leung
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/editors/armature/poseSlide.c
27  *  \ingroup edarmature
28  */
29
30  
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <stddef.h>
34 #include <string.h>
35 #include <math.h>
36 #include <float.h>
37
38 #include "MEM_guardedalloc.h"
39
40 #include "BLI_math.h"
41 #include "BLI_blenlib.h"
42 #include "BLI_dynstr.h"
43 #include "BLI_dlrbTree.h"
44 #include "BLI_utildefines.h"
45
46 #include "DNA_anim_types.h"
47 #include "DNA_armature_types.h"
48 #include "DNA_object_types.h"
49 #include "DNA_scene_types.h"
50
51 #include "BKE_fcurve.h"
52
53 #include "BKE_context.h"
54 #include "BKE_report.h"
55
56 #include "RNA_access.h"
57 #include "RNA_define.h"
58
59 #include "WM_api.h"
60 #include "WM_types.h"
61
62 #include "ED_armature.h"
63 #include "ED_keyframes_draw.h"
64 #include "ED_markers.h"
65 #include "ED_screen.h"
66
67 #include "armature_intern.h"
68
69 /* **************************************************** */
70 /* == POSE 'SLIDING' TOOLS == 
71  *
72  * A) Push & Relax, Breakdowner
73  * These tools provide the animator with various capabilities
74  * for interactively controlling the spacing of poses, but also
75  * for 'pushing' and/or 'relaxing' extremes as they see fit.
76  *
77  * B) Propagate
78  * This tool copies elements of the selected pose to successive
79  * keyframes, allowing the animator to go back and modify the poses
80  * for some "static" pose controls, without having to repeatedly
81  * doing a "next paste" dance.
82  *
83  * C) Pose Sculpting
84  * This is yet to be implemented, but the idea here is to use
85  * sculpting techniques to make it easier to pose rigs by allowing
86  * rigs to be manipulated using a familiar paint-based interface. 
87  */
88 /* **************************************************** */
89 /* A) Push & Relax, Breakdowner */
90
91 /* Temporary data shared between these operators */
92 typedef struct tPoseSlideOp {
93         Scene *scene;           /* current scene */
94         ScrArea *sa;            /* area that we're operating in (needed for modal()) */
95         ARegion *ar;            /* region that we're operating in (needed for modal()) */
96         Object *ob;                     /* active object that Pose Info comes from */
97         bArmature *arm;         /* armature for pose */
98         
99         ListBase pfLinks;       /* links between posechannels and f-curves  */
100         DLRBT_Tree keys;        /* binary tree for quicker searching for keyframes (when applicable) */
101         
102         int cframe;                     /* current frame number */
103         int prevFrame;          /* frame before current frame (blend-from) */
104         int nextFrame;          /* frame after current frame (blend-to) */
105         
106         int mode;                       /* sliding mode (ePoseSlide_Modes) */
107         int flag;                       // unused for now, but can later get used for storing runtime settings....
108         
109         float percentage;       /* 0-1 value for determining the influence of whatever is relevant */
110 } tPoseSlideOp;
111
112 /* Pose Sliding Modes */
113 typedef enum ePoseSlide_Modes {
114         POSESLIDE_PUSH  = 0,            /* exaggerate the pose... */
115         POSESLIDE_RELAX,                        /* soften the pose... */
116         POSESLIDE_BREAKDOWN,            /* slide between the endpoint poses, finding a 'soft' spot */
117 } ePoseSlide_Modes;
118
119 /* ------------------------------------ */
120
121 /* operator init */
122 static int pose_slide_init (bContext *C, wmOperator *op, short mode)
123 {
124         tPoseSlideOp *pso;
125         bAction *act= NULL;
126         
127         /* init slide-op data */
128         pso= op->customdata= MEM_callocN(sizeof(tPoseSlideOp), "tPoseSlideOp");
129         
130         /* get info from context */
131         pso->scene= CTX_data_scene(C);
132         pso->ob= ED_object_pose_armature(CTX_data_active_object(C));
133         pso->arm= (pso->ob)? pso->ob->data : NULL;
134         pso->sa= CTX_wm_area(C); /* only really needed when doing modal() */
135         pso->ar= CTX_wm_region(C); /* only really needed when doing modal() */
136         
137         pso->cframe= pso->scene->r.cfra;
138         pso->mode= mode;
139         
140         /* set range info from property values - these may get overridden for the invoke() */
141         pso->percentage= RNA_float_get(op->ptr, "percentage");
142         pso->prevFrame= RNA_int_get(op->ptr, "prev_frame");
143         pso->nextFrame= RNA_int_get(op->ptr, "next_frame");
144         
145         /* check the settings from the context */
146         if (ELEM4(NULL, pso->ob, pso->arm, pso->ob->adt, pso->ob->adt->action))
147                 return 0;
148         else
149                 act= pso->ob->adt->action;
150         
151         /* for each Pose-Channel which gets affected, get the F-Curves for that channel 
152          * and set the relevant transform flags...
153          */
154         poseAnim_mapping_get(C, &pso->pfLinks, pso->ob, act);
155         
156         /* set depsgraph flags */
157                 /* make sure the lock is set OK, unlock can be accidentally saved? */
158         pso->ob->pose->flag |= POSE_LOCKED;
159         pso->ob->pose->flag &= ~POSE_DO_UNLOCK;
160         
161         /* do basic initialise of RB-BST used for finding keyframes, but leave the filling of it up 
162          * to the caller of this (usually only invoke() will do it, to make things more efficient).
163          */
164         BLI_dlrbTree_init(&pso->keys);
165         
166         /* return status is whether we've got all the data we were requested to get */
167         return 1;
168 }
169
170 /* exiting the operator - free data */
171 static void pose_slide_exit(wmOperator *op)
172 {
173         tPoseSlideOp *pso= op->customdata;
174         
175         /* if data exists, clear its data and exit */
176         if (pso) {
177                 /* free the temp pchan links and their data */
178                 poseAnim_mapping_free(&pso->pfLinks);
179                 
180                 /* free RB-BST for keyframes (if it contained data) */
181                 BLI_dlrbTree_free(&pso->keys);
182                 
183                 /* free data itself */
184                 MEM_freeN(pso);
185         }
186         
187         /* cleanup */
188         op->customdata= NULL;
189 }
190
191 /* ------------------------------------ */
192
193 /* helper for apply() / reset() - refresh the data */
194 static void pose_slide_refresh (bContext *C, tPoseSlideOp *pso)
195 {
196         /* wrapper around the generic version, allowing us to add some custom stuff later still */
197         poseAnim_mapping_refresh(C, pso->scene, pso->ob);
198 }
199
200 /* helper for apply() - perform sliding for some value */
201 static void pose_slide_apply_val (tPoseSlideOp *pso, FCurve *fcu, float *val)
202 {
203         float cframe = (float)pso->cframe;
204         float sVal, eVal;
205         float w1, w2;
206         
207         /* get keyframe values for endpoint poses to blend with */
208                 /* previous/start */
209         sVal= evaluate_fcurve(fcu, (float)pso->prevFrame);
210                 /* next/end */
211         eVal= evaluate_fcurve(fcu, (float)pso->nextFrame);
212         
213         /* calculate the relative weights of the endpoints */
214         if (pso->mode == POSESLIDE_BREAKDOWN) {
215                 /* get weights from the percentage control */
216                 w1= pso->percentage;    /* this must come second */
217                 w2= 1.0f - w1;                  /* this must come first */
218         }
219         else {
220                 /*      - these weights are derived from the relative distance of these 
221                  *        poses from the current frame
222                  *      - they then get normalised so that they only sum up to 1
223                  */
224                 float wtot; 
225                 
226                 w1 = cframe - (float)pso->prevFrame;
227                 w2 = (float)pso->nextFrame - cframe;
228                 
229                 wtot = w1 + w2;
230                 w1 = (w1/wtot);
231                 w2 = (w2/wtot);
232         }
233         
234         /* depending on the mode, calculate the new value
235          *      - in all of these, the start+end values are multiplied by w2 and w1 (respectively),
236          *        since multiplication in another order would decrease the value the current frame is closer to
237          */
238         switch (pso->mode) {
239                 case POSESLIDE_PUSH: /* make the current pose more pronounced */
240                 {
241                         /* perform a weighted average here, favouring the middle pose 
242                          *      - numerator should be larger than denominator to 'expand' the result
243                          *      - perform this weighting a number of times given by the percentage...
244                          */
245                         int iters= (int)ceil(10.0f*pso->percentage); // TODO: maybe a sensitivity ctrl on top of this is needed
246                         
247                         while (iters-- > 0) {
248                                 (*val)= ( -((sVal * w2) + (eVal * w1)) + ((*val) * 6.0f) ) / 5.0f; 
249                         }
250                 }
251                         break;
252                         
253                 case POSESLIDE_RELAX: /* make the current pose more like its surrounding ones */
254                 {
255                         /* perform a weighted average here, favouring the middle pose 
256                          *      - numerator should be smaller than denominator to 'relax' the result
257                          *      - perform this weighting a number of times given by the percentage...
258                          */
259                         int iters= (int)ceil(10.0f*pso->percentage); // TODO: maybe a sensitivity ctrl on top of this is needed
260                         
261                         while (iters-- > 0) {
262                                 (*val)= ( ((sVal * w2) + (eVal * w1)) + ((*val) * 5.0f) ) / 6.0f;
263                         }
264                 }
265                         break;
266                         
267                 case POSESLIDE_BREAKDOWN: /* make the current pose slide around between the endpoints */
268                 {
269                         /* perform simple linear interpolation - coefficient for start must come from pso->percentage... */
270                         // TODO: make this use some kind of spline interpolation instead?
271                         (*val)= ((sVal * w2) + (eVal * w1));
272                 }
273                         break;
274         }
275 }
276
277 /* helper for apply() - perform sliding for some 3-element vector */
278 static void pose_slide_apply_vec3 (tPoseSlideOp *pso, tPChanFCurveLink *pfl, float vec[3], const char propName[])
279 {
280         LinkData *ld=NULL;
281         char *path=NULL;
282         
283         /* get the path to use... */
284         path= BLI_sprintfN("%s.%s", pfl->pchan_path, propName);
285         
286         /* using this path, find each matching F-Curve for the variables we're interested in */
287         while ( (ld= poseAnim_mapping_getNextFCurve(&pfl->fcurves, ld, path)) ) {
288                 FCurve *fcu= (FCurve *)ld->data;
289                 
290                 /* just work on these channels one by one... there's no interaction between values */
291                 pose_slide_apply_val(pso, fcu, &vec[fcu->array_index]);
292         }
293         
294         /* free the temp path we got */
295         MEM_freeN(path);
296 }
297
298 /* helper for apply() - perform sliding for custom properties */
299 static void pose_slide_apply_props (tPoseSlideOp *pso, tPChanFCurveLink *pfl)
300 {
301         PointerRNA ptr = {{NULL}};
302         LinkData *ld;
303         int len = strlen(pfl->pchan_path);
304         
305         /* setup pointer RNA for resolving paths */
306         RNA_pointer_create(NULL, &RNA_PoseBone, pfl->pchan, &ptr);
307         
308         /* custom properties are just denoted using ["..."][etc.] after the end of the base path, 
309          * so just check for opening pair after the end of the path
310          */
311         for (ld = pfl->fcurves.first; ld; ld = ld->next) {
312                 FCurve *fcu = (FCurve *)ld->data;
313                 char *bPtr, *pPtr;
314                 
315                 if (fcu->rna_path == NULL)
316                         continue;
317                 
318                 /* do we have a match? 
319                  *      - bPtr is the RNA Path with the standard part chopped off
320                  *      - pPtr is the chunk of the path which is left over
321                  */
322                 bPtr = strstr(fcu->rna_path, pfl->pchan_path) + len;
323                 pPtr = strstr(bPtr, "[\"");   /* dummy " for texteditor bugs */
324                 
325                 if (pPtr) {
326                         /* use RNA to try and get a handle on this property, then, assuming that it is just
327                          * numerical, try and grab the value as a float for temp editing before setting back
328                          */
329                         PropertyRNA *prop = RNA_struct_find_property(&ptr, pPtr);
330                         
331                         if (prop) {
332                                 switch (RNA_property_type(prop)) {
333                                         case PROP_FLOAT:
334                                         {
335                                                 float tval = RNA_property_float_get(&ptr, prop);
336                                                 pose_slide_apply_val(pso, fcu, &tval);
337                                                 RNA_property_float_set(&ptr, prop, tval);
338                                         }
339                                                 break;
340                                         case PROP_BOOLEAN:
341                                         case PROP_ENUM:
342                                         case PROP_INT:
343                                         {
344                                                 float tval = (float)RNA_property_int_get(&ptr, prop);
345                                                 pose_slide_apply_val(pso, fcu, &tval);
346                                                 RNA_property_int_set(&ptr, prop, (int)tval);
347                                         }
348                                                 break;
349                                         default:
350                                                 /* cannot handle */
351                                                 //printf("Cannot Pose Slide non-numerical property\n");
352                                                 break;
353                                 }
354                         }
355                 }
356         }
357 }
358
359 /* helper for apply() - perform sliding for quaternion rotations (using quat blending) */
360 static void pose_slide_apply_quat (tPoseSlideOp *pso, tPChanFCurveLink *pfl)
361 {
362         FCurve *fcu_w=NULL, *fcu_x=NULL, *fcu_y=NULL, *fcu_z=NULL;
363         bPoseChannel *pchan= pfl->pchan;
364         LinkData *ld=NULL;
365         char *path=NULL;
366         float cframe;
367         
368         /* get the path to use - this should be quaternion rotations only (needs care) */
369         path= BLI_sprintfN("%s.%s", pfl->pchan_path, "rotation_quaternion");
370         
371         /* get the current frame number */
372         cframe= (float)pso->cframe;
373         
374         /* using this path, find each matching F-Curve for the variables we're interested in */
375         while ( (ld= poseAnim_mapping_getNextFCurve(&pfl->fcurves, ld, path)) ) {
376                 FCurve *fcu= (FCurve *)ld->data;
377                 
378                 /* assign this F-Curve to one of the relevant pointers... */
379                 switch (fcu->array_index) {
380                         case 3: /* z */
381                                 fcu_z= fcu;
382                                 break;
383                         case 2: /* y */
384                                 fcu_y= fcu;
385                                 break;
386                         case 1: /* x */
387                                 fcu_x= fcu;
388                                 break;
389                         case 0: /* w */
390                                 fcu_w= fcu;
391                                 break;
392                 }
393         }
394         
395         /* only if all channels exist, proceed */
396         if (fcu_w && fcu_x && fcu_y && fcu_z) {
397                 float quat_prev[4], quat_next[4];
398                 
399                 /* get 2 quats */
400                 quat_prev[0] = evaluate_fcurve(fcu_w, pso->prevFrame);
401                 quat_prev[1] = evaluate_fcurve(fcu_x, pso->prevFrame);
402                 quat_prev[2] = evaluate_fcurve(fcu_y, pso->prevFrame);
403                 quat_prev[3] = evaluate_fcurve(fcu_z, pso->prevFrame);
404                 
405                 quat_next[0] = evaluate_fcurve(fcu_w, pso->nextFrame);
406                 quat_next[1] = evaluate_fcurve(fcu_x, pso->nextFrame);
407                 quat_next[2] = evaluate_fcurve(fcu_y, pso->nextFrame);
408                 quat_next[3] = evaluate_fcurve(fcu_z, pso->nextFrame);
409                 
410                 /* perform blending */
411                 if (pso->mode == POSESLIDE_BREAKDOWN) {
412                         /* just perform the interpol between quat_prev and quat_next using pso->percentage as a guide */
413                         interp_qt_qtqt(pchan->quat, quat_prev, quat_next, pso->percentage);
414                 }
415                 else if (pso->mode == POSESLIDE_PUSH) {
416                         float quat_diff[4], quat_orig[4];
417                         
418                         /* calculate the delta transform from the previous to the current */
419                         // TODO: investigate ways to favour one transform more?
420                         sub_qt_qtqt(quat_diff, pchan->quat, quat_prev);
421                         
422                         /* make a copy of the original rotation */
423                         QUATCOPY(quat_orig, pchan->quat);
424                         
425                         /* increase the original by the delta transform, by an amount determined by percentage */
426                         add_qt_qtqt(pchan->quat, quat_orig, quat_diff, pso->percentage);
427                 }
428                 else {
429                         float quat_interp[4], quat_orig[4];
430                         int iters= (int)ceil(10.0f*pso->percentage); // TODO: maybe a sensitivity ctrl on top of this is needed
431                         
432                         /* perform this blending several times until a satisfactory result is reached */
433                         while (iters-- > 0) {
434                                 /* calculate the interpolation between the endpoints */
435                                 interp_qt_qtqt(quat_interp, quat_prev, quat_next, (cframe-pso->prevFrame) / (pso->nextFrame-pso->prevFrame) );
436                                 
437                                 /* make a copy of the original rotation */
438                                 QUATCOPY(quat_orig, pchan->quat);
439                                 
440                                 /* tricky interpolations - blending between original and new */
441                                 interp_qt_qtqt(pchan->quat, quat_orig, quat_interp, 1.0f/6.0f);
442                         }
443                 }
444         }
445         
446         /* free the path now */
447         MEM_freeN(path);
448 }
449
450 /* apply() - perform the pose sliding based on weighting various poses */
451 static void pose_slide_apply(bContext *C, tPoseSlideOp *pso)
452 {
453         tPChanFCurveLink *pfl;
454         
455         /* sanitise the frame ranges */
456         if (pso->prevFrame == pso->nextFrame) {
457                 /* move out one step either side */
458                 pso->prevFrame--;
459                 pso->nextFrame++;
460         }
461         
462         /* for each link, handle each set of transforms */
463         for (pfl= pso->pfLinks.first; pfl; pfl= pfl->next) {
464                 /* valid transforms for each PoseChannel should have been noted already 
465                  *      - sliding the pose should be a straightforward exercise for location+rotation, 
466                  *        but rotations get more complicated since we may want to use quaternion blending 
467                  *        for quaternions instead...
468                  */
469                 bPoseChannel *pchan= pfl->pchan;
470                  
471                 if (pchan->flag & POSE_LOC) {
472                         /* calculate these for the 'location' vector, and use location curves */
473                         pose_slide_apply_vec3(pso, pfl, pchan->loc, "location");
474                 }
475                 
476                 if (pchan->flag & POSE_SIZE) {
477                         /* calculate these for the 'scale' vector, and use scale curves */
478                         pose_slide_apply_vec3(pso, pfl, pchan->size, "scale");
479                 }
480                 
481                 if (pchan->flag & POSE_ROT) {
482                         /* everything depends on the rotation mode */
483                         if (pchan->rotmode > 0) {
484                                 /* eulers - so calculate these for the 'eul' vector, and use euler_rotation curves */
485                                 pose_slide_apply_vec3(pso, pfl, pchan->eul, "rotation_euler");
486                         }
487                         else if (pchan->rotmode == ROT_MODE_AXISANGLE) {
488                                 // TODO: need to figure out how to do this!
489                         }
490                         else {
491                                 /* quaternions - use quaternion blending */
492                                 pose_slide_apply_quat(pso, pfl);
493                         }
494                 }
495                 
496                 if (pfl->oldprops) {
497                         /* not strictly a transform, but contributes to the pose produced in many rigs */
498                         pose_slide_apply_props(pso, pfl);
499                 }
500         }
501         
502         /* depsgraph updates + redraws */
503         pose_slide_refresh(C, pso);
504 }
505
506 /* perform autokeyframing after changes were made + confirmed */
507 static void pose_slide_autoKeyframe (bContext *C, tPoseSlideOp *pso)
508 {
509         /* wrapper around the generic call */
510         poseAnim_mapping_autoKeyframe(C, pso->scene, pso->ob, &pso->pfLinks, (float)pso->cframe);
511 }
512
513 /* reset changes made to current pose */
514 static void pose_slide_reset (tPoseSlideOp *pso)
515 {
516         /* wrapper around the generic call, so that custom stuff can be added later */
517         poseAnim_mapping_reset(&pso->pfLinks);
518 }
519
520 /* ------------------------------------ */
521
522 /* draw percentage indicator in header */
523 static void pose_slide_draw_status (tPoseSlideOp *pso)
524 {
525         char statusStr[32];
526         char mode[32];
527         
528         switch (pso->mode) {
529                 case POSESLIDE_PUSH:
530                         strcpy(mode, "Push Pose");
531                         break;
532                 case POSESLIDE_RELAX:
533                         strcpy(mode, "Relax Pose");
534                         break;
535                 case POSESLIDE_BREAKDOWN:
536                         strcpy(mode, "Breakdown");
537                         break;
538                 
539                 default:
540                         // unknown
541                         strcpy(mode, "Sliding-Tool");
542                         break;
543         }
544         
545         sprintf(statusStr, "%s: %d %%", mode, (int)(pso->percentage*100.0f));
546         ED_area_headerprint(pso->sa, statusStr);
547 }
548
549 /* common code for invoke() methods */
550 static int pose_slide_invoke_common (bContext *C, wmOperator *op, tPoseSlideOp *pso)
551 {
552         tPChanFCurveLink *pfl;
553         AnimData *adt= pso->ob->adt;
554         wmWindow *win= CTX_wm_window(C);
555         
556         /* for each link, add all its keyframes to the search tree */
557         for (pfl= pso->pfLinks.first; pfl; pfl= pfl->next) {
558                 LinkData *ld;
559                 
560                 /* do this for each F-Curve */
561                 for (ld= pfl->fcurves.first; ld; ld= ld->next) {
562                         FCurve *fcu= (FCurve *)ld->data;
563                         fcurve_to_keylist(adt, fcu, &pso->keys, NULL);
564                 }
565         }
566         
567         /* consolidate these keyframes, and figure out the nearest ones */
568         BLI_dlrbTree_linkedlist_sync(&pso->keys);
569         
570                 /* cancel if no keyframes found... */
571         if (pso->keys.root) {
572                 ActKeyColumn *ak;
573                 float cframe= (float)pso->cframe;
574                 
575                 /* firstly, check if the current frame is a keyframe... */
576                 ak= (ActKeyColumn *)BLI_dlrbTree_search_exact(&pso->keys, compare_ak_cfraPtr, &cframe);
577                 
578                 if (ak == NULL) {
579                         /* current frame is not a keyframe, so search */
580                         ActKeyColumn *pk= (ActKeyColumn *)BLI_dlrbTree_search_prev(&pso->keys, compare_ak_cfraPtr, &cframe);
581                         ActKeyColumn *nk= (ActKeyColumn *)BLI_dlrbTree_search_next(&pso->keys, compare_ak_cfraPtr, &cframe);
582                         
583                         /* new set the frames */
584                                 /* prev frame */
585                         pso->prevFrame= (pk)? (pk->cfra) : (pso->cframe - 1);
586                         RNA_int_set(op->ptr, "prev_frame", pso->prevFrame);
587                                 /* next frame */
588                         pso->nextFrame= (nk)? (nk->cfra) : (pso->cframe + 1);
589                         RNA_int_set(op->ptr, "next_frame", pso->nextFrame);
590                 }
591                 else {
592                         /* current frame itself is a keyframe, so just take keyframes on either side */
593                                 /* prev frame */
594                         pso->prevFrame= (ak->prev)? (ak->prev->cfra) : (pso->cframe - 1);
595                         RNA_int_set(op->ptr, "prev_frame", pso->prevFrame);
596                                 /* next frame */
597                         pso->nextFrame= (ak->next)? (ak->next->cfra) : (pso->cframe + 1);
598                         RNA_int_set(op->ptr, "next_frame", pso->nextFrame);
599                 }
600         }
601         else {
602                 BKE_report(op->reports, RPT_ERROR, "No keyframes to slide between.");
603                 pose_slide_exit(op);
604                 return OPERATOR_CANCELLED;
605         }
606         
607         /* initial apply for operator... */
608         // TODO: need to calculate percentage for initial round too...
609         pose_slide_apply(C, pso);
610         
611         /* depsgraph updates + redraws */
612         pose_slide_refresh(C, pso);
613         
614         /* set cursor to indicate modal */
615         WM_cursor_modal(win, BC_EW_SCROLLCURSOR);
616         
617         /* header print */
618         pose_slide_draw_status(pso);
619         
620         /* add a modal handler for this operator */
621         WM_event_add_modal_handler(C, op);
622         return OPERATOR_RUNNING_MODAL;
623 }
624
625 /* common code for modal() */
626 static int pose_slide_modal (bContext *C, wmOperator *op, wmEvent *evt)
627 {
628         tPoseSlideOp *pso= op->customdata;
629         wmWindow *win= CTX_wm_window(C);
630         
631         switch (evt->type) {
632                 case LEFTMOUSE: /* confirm */
633                 {
634                         /* return to normal cursor and header status */
635                         ED_area_headerprint(pso->sa, NULL);
636                         WM_cursor_restore(win);
637                         
638                         /* insert keyframes as required... */
639                         pose_slide_autoKeyframe(C, pso);
640                         pose_slide_exit(op);
641                         
642                         /* done! */
643                         return OPERATOR_FINISHED;
644                 }
645                 
646                 case ESCKEY:    /* cancel */
647                 case RIGHTMOUSE: 
648                 {
649                         /* return to normal cursor and header status */
650                         ED_area_headerprint(pso->sa, NULL);
651                         WM_cursor_restore(win);
652                         
653                         /* reset transforms back to original state */
654                         pose_slide_reset(pso);
655                         
656                         /* depsgraph updates + redraws */
657                         pose_slide_refresh(C, pso);
658                         
659                         /* clean up temp data */
660                         pose_slide_exit(op);
661                         
662                         /* cancelled! */
663                         return OPERATOR_CANCELLED;
664                 }
665                         
666                 case MOUSEMOVE: /* calculate new position */
667                 {
668                         /* calculate percentage based on position of mouse (we only use x-axis for now.
669                          * since this is more conveninent for users to do), and store new percentage value 
670                          */
671                         pso->percentage= (evt->x - pso->ar->winrct.xmin) / ((float)pso->ar->winx);
672                         RNA_float_set(op->ptr, "percentage", pso->percentage);
673                         
674                         /* update percentage indicator in header */
675                         pose_slide_draw_status(pso);
676                         
677                         /* reset transforms (to avoid accumulation errors) */
678                         pose_slide_reset(pso);
679                         
680                         /* apply... */
681                         pose_slide_apply(C, pso);
682                 }
683                         break;
684                         
685                 default: /* unhandled event (maybe it was some view manip? */
686                         /* allow to pass through */
687                         return OPERATOR_RUNNING_MODAL|OPERATOR_PASS_THROUGH;
688         }
689         
690         /* still running... */
691         return OPERATOR_RUNNING_MODAL;
692 }
693
694 /* common code for cancel() */
695 static int pose_slide_cancel (bContext *UNUSED(C), wmOperator *op)
696 {
697         /* cleanup and done */
698         pose_slide_exit(op);
699         return OPERATOR_CANCELLED;
700 }
701
702 /* common code for exec() methods */
703 static int pose_slide_exec_common (bContext *C, wmOperator *op, tPoseSlideOp *pso)
704 {
705         /* settings should have been set up ok for applying, so just apply! */
706         pose_slide_apply(C, pso);
707         
708         /* insert keyframes if needed */
709         pose_slide_autoKeyframe(C, pso);
710         
711         /* cleanup and done */
712         pose_slide_exit(op);
713         
714         return OPERATOR_FINISHED;
715 }
716
717 /* common code for defining RNA properties */
718 static void pose_slide_opdef_properties (wmOperatorType *ot)
719 {
720         RNA_def_int(ot->srna, "prev_frame", 0, MINAFRAME, MAXFRAME, "Previous Keyframe", "Frame number of keyframe immediately before the current frame.", 0, 50);
721         RNA_def_int(ot->srna, "next_frame", 0, MINAFRAME, MAXFRAME, "Next Keyframe", "Frame number of keyframe immediately after the current frame.", 0, 50);
722         RNA_def_float_percentage(ot->srna, "percentage", 0.5f, 0.0f, 1.0f, "Percentage", "Weighting factor for the sliding operation", 0.3, 0.7);
723 }
724
725 /* ------------------------------------ */
726
727 /* invoke() - for 'push' mode */
728 static int pose_slide_push_invoke (bContext *C, wmOperator *op, wmEvent *UNUSED(evt))
729 {
730         tPoseSlideOp *pso;
731         
732         /* initialise data  */
733         if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) {
734                 pose_slide_exit(op);
735                 return OPERATOR_CANCELLED;
736         }
737         else
738                 pso= op->customdata;
739         
740         /* do common setup work */
741         return pose_slide_invoke_common(C, op, pso);
742 }
743
744 /* exec() - for push */
745 static int pose_slide_push_exec (bContext *C, wmOperator *op)
746 {
747         tPoseSlideOp *pso;
748         
749         /* initialise data (from RNA-props) */
750         if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) {
751                 pose_slide_exit(op);
752                 return OPERATOR_CANCELLED;
753         }
754         else
755                 pso= op->customdata;
756                 
757         /* do common exec work */
758         return pose_slide_exec_common(C, op, pso);
759 }
760
761 void POSE_OT_push (wmOperatorType *ot)
762 {
763         /* identifiers */
764         ot->name= "Push Pose";
765         ot->idname= "POSE_OT_push";
766         ot->description= "Exaggerate the current pose";
767         
768         /* callbacks */
769         ot->exec= pose_slide_push_exec;
770         ot->invoke= pose_slide_push_invoke;
771         ot->modal= pose_slide_modal;
772         ot->cancel= pose_slide_cancel;
773         ot->poll= ED_operator_posemode;
774         
775         /* flags */
776         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
777         
778         /* Properties */
779         pose_slide_opdef_properties(ot);
780 }
781
782 /* ........................ */
783
784 /* invoke() - for 'relax' mode */
785 static int pose_slide_relax_invoke (bContext *C, wmOperator *op, wmEvent *UNUSED(evt))
786 {
787         tPoseSlideOp *pso;
788         
789         /* initialise data  */
790         if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) {
791                 pose_slide_exit(op);
792                 return OPERATOR_CANCELLED;
793         }
794         else
795                 pso= op->customdata;
796         
797         /* do common setup work */
798         return pose_slide_invoke_common(C, op, pso);
799 }
800
801 /* exec() - for relax */
802 static int pose_slide_relax_exec (bContext *C, wmOperator *op)
803 {
804         tPoseSlideOp *pso;
805         
806         /* initialise data (from RNA-props) */
807         if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) {
808                 pose_slide_exit(op);
809                 return OPERATOR_CANCELLED;
810         }
811         else
812                 pso= op->customdata;
813                 
814         /* do common exec work */
815         return pose_slide_exec_common(C, op, pso);
816 }
817
818 void POSE_OT_relax (wmOperatorType *ot)
819 {
820         /* identifiers */
821         ot->name= "Relax Pose";
822         ot->idname= "POSE_OT_relax";
823         ot->description= "Make the current pose more similar to its surrounding ones";
824         
825         /* callbacks */
826         ot->exec= pose_slide_relax_exec;
827         ot->invoke= pose_slide_relax_invoke;
828         ot->modal= pose_slide_modal;
829         ot->cancel= pose_slide_cancel;
830         ot->poll= ED_operator_posemode;
831         
832         /* flags */
833         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
834         
835         /* Properties */
836         pose_slide_opdef_properties(ot);
837 }
838
839 /* ........................ */
840
841 /* invoke() - for 'breakdown' mode */
842 static int pose_slide_breakdown_invoke (bContext *C, wmOperator *op, wmEvent *UNUSED(evt))
843 {
844         tPoseSlideOp *pso;
845         
846         /* initialise data  */
847         if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) {
848                 pose_slide_exit(op);
849                 return OPERATOR_CANCELLED;
850         }
851         else
852                 pso= op->customdata;
853         
854         /* do common setup work */
855         return pose_slide_invoke_common(C, op, pso);
856 }
857
858 /* exec() - for breakdown */
859 static int pose_slide_breakdown_exec (bContext *C, wmOperator *op)
860 {
861         tPoseSlideOp *pso;
862         
863         /* initialise data (from RNA-props) */
864         if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) {
865                 pose_slide_exit(op);
866                 return OPERATOR_CANCELLED;
867         }
868         else
869                 pso= op->customdata;
870                 
871         /* do common exec work */
872         return pose_slide_exec_common(C, op, pso);
873 }
874
875 void POSE_OT_breakdown (wmOperatorType *ot)
876 {
877         /* identifiers */
878         ot->name= "Pose Breakdowner";
879         ot->idname= "POSE_OT_breakdown";
880         ot->description= "Create a suitable breakdown pose on the current frame";
881         
882         /* callbacks */
883         ot->exec= pose_slide_breakdown_exec;
884         ot->invoke= pose_slide_breakdown_invoke;
885         ot->modal= pose_slide_modal;
886         ot->cancel= pose_slide_cancel;
887         ot->poll= ED_operator_posemode;
888         
889         /* flags */
890         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
891         
892         /* Properties */
893         pose_slide_opdef_properties(ot);
894 }
895
896 /* **************************************************** */
897 /* B) Pose Propagate */
898
899 /* "termination conditions" - i.e. when we stop */
900 typedef enum ePosePropagate_Termination {
901                 /* stop after the current hold ends */
902         POSE_PROPAGATE_SMART_HOLDS = 0,
903                 /* only do on the last keyframe */
904         POSE_PROPAGATE_LAST_KEY,
905                 /* stop after the next keyframe */
906         POSE_PROPAGATE_NEXT_KEY,
907                 /* stop after the specified frame */
908         POSE_PROPAGATE_BEFORE_FRAME,
909                 /* stop when we run out of keyframes */
910         POSE_PROPAGATE_BEFORE_END,
911         
912                 /* only do on the frames where markers are selected */
913         POSE_PROPAGATE_SELECTED_MARKERS
914 } ePosePropagate_Termination;
915
916 /* termination data needed for some modes - assumes only one of these entries will be needed at a time */
917 typedef union tPosePropagate_ModeData {
918         /* smart holds + before frame: frame number to stop on */
919         float end_frame;
920         
921         /* selected markers: listbase for CfraElem's marking these frames */
922         ListBase sel_markers;
923 } tPosePropagate_ModeData;
924
925 /* --------------------------------- */
926
927 /* get frame on which the "hold" for the bone ends 
928  * XXX: this may not really work that well if a bone moves on some channels and not others
929  *              if this happens to be a major issue, scrap this, and just make this happen 
930  *              independently per F-Curve
931  */
932 static float pose_propagate_get_boneHoldEndFrame (Object *ob, tPChanFCurveLink *pfl, float startFrame)
933 {
934         DLRBT_Tree keys, blocks;
935         ActKeyBlock *ab;
936         
937         AnimData *adt= ob->adt;
938         LinkData *ld;
939         float endFrame = startFrame;
940         
941         /* set up optimised data-structures for searching for relevant keyframes + holds */
942         BLI_dlrbTree_init(&keys);
943         BLI_dlrbTree_init(&blocks);
944         
945         for (ld = pfl->fcurves.first; ld; ld = ld->next) {
946                 FCurve *fcu = (FCurve *)ld->data;
947                 fcurve_to_keylist(adt, fcu, &keys, &blocks);
948         }
949         
950         BLI_dlrbTree_linkedlist_sync(&keys);
951         BLI_dlrbTree_linkedlist_sync(&blocks);
952         
953         /* find the long keyframe (i.e. hold), and hence obtain the endFrame value 
954          *      - the best case would be one that starts on the frame itself
955          */
956         ab = (ActKeyBlock *)BLI_dlrbTree_search_exact(&blocks, compare_ab_cfraPtr, &startFrame);
957         
958         if (actkeyblock_is_valid(ab, &keys) == 0) {
959                 /* There are only two cases for no-exact match:
960                  *      1) the current frame is just before another key but not on a key itself
961                  *      2) the current frame is on a key, but that key doesn't link to the next
962                  *
963                  * If we've got the first case, then we can search for another block, 
964                  * otherwise forget it, as we'd be overwriting some valid data.
965                  */
966                 if (BLI_dlrbTree_search_exact(&keys, compare_ak_cfraPtr, &startFrame) == NULL) {
967                         /* we've got case 1, so try the one after */
968                         ab = (ActKeyBlock *)BLI_dlrbTree_search_next(&blocks, compare_ab_cfraPtr, &startFrame);
969                         
970                         if (actkeyblock_is_valid(ab, &keys) == 0) {
971                                 /* try the block before this frame then as last resort */
972                                 ab = (ActKeyBlock *)BLI_dlrbTree_search_prev(&blocks, compare_ab_cfraPtr, &startFrame);
973                                 
974                                 /* whatever happens, stop searching now... */
975                                 if (actkeyblock_is_valid(ab, &keys) == 0) {
976                                         /* restrict range to just the frame itself 
977                                          * i.e. everything is in motion, so no holds to safely overwrite
978                                          */
979                                         ab = NULL;
980                                 }
981                         }
982                 }
983                 else {
984                         /* we've got case 2 - set ab to NULL just in case, since we shouldn't do anything in this case */
985                         ab = NULL;
986                 }
987         }
988         
989         /* check if we can go any further than we've already gone */
990         if (ab) {
991                 /* go to next if it is also valid and meets "extension" criteria */
992                 while (ab->next) {
993                         ActKeyBlock *abn = (ActKeyBlock *)ab->next;
994                         
995                         /* must be valid */
996                         if (actkeyblock_is_valid(abn, &keys) == 0)
997                                 break;
998                         /* should start on the same frame that the last ended on */
999                         if (ab->end != abn->start)
1000                                 break;
1001                         /* should have the same number of curves */
1002                         if (ab->totcurve != abn->totcurve)
1003                                 break;
1004                         /* should have the same value 
1005                          * XXX: this may be a bit fuzzy on larger data sets, so be careful
1006                          */
1007                         if (ab->val != abn->val)
1008                                 break;
1009                                 
1010                         /* we can extend the bounds to the end of this "next" block now */
1011                         ab = abn;
1012                 }
1013                 
1014                 /* end frame can now take the value of the end of the block */
1015                 endFrame = ab->end;
1016         }
1017         
1018         /* free temp memory */
1019         BLI_dlrbTree_free(&keys);
1020         BLI_dlrbTree_free(&blocks);
1021         
1022         /* return the end frame we've found */
1023         return endFrame;
1024 }
1025
1026 /* get reference value from F-Curve using RNA */
1027 static short pose_propagate_get_refVal (Object *ob, FCurve *fcu, float *value)
1028 {
1029         PointerRNA id_ptr, ptr;
1030         PropertyRNA *prop;
1031         short found= FALSE;
1032         
1033         /* base pointer is always the object -> id_ptr */
1034         RNA_id_pointer_create(&ob->id, &id_ptr);
1035         
1036         /* resolve the property... */
1037         if (RNA_path_resolve(&id_ptr, fcu->rna_path, &ptr, &prop)) {
1038                 if (RNA_property_array_check(prop)) {
1039                         /* array */
1040                         if (fcu->array_index < RNA_property_array_length(&ptr, prop)) {
1041                                 found= TRUE;
1042                                 switch (RNA_property_type(prop)) {
1043                                         case PROP_BOOLEAN:
1044                                                 *value= (float)RNA_property_boolean_get_index(&ptr, prop, fcu->array_index);
1045                                                 break;
1046                                         case PROP_INT:
1047                                                 *value= (float)RNA_property_int_get_index(&ptr, prop, fcu->array_index);
1048                                                 break;
1049                                         case PROP_FLOAT:
1050                                                 *value= RNA_property_float_get_index(&ptr, prop, fcu->array_index);
1051                                                 break;
1052                                         default:
1053                                                 found= FALSE;
1054                                                 break;
1055                                 }
1056                         }
1057                 }
1058                 else {
1059                         /* not an array */
1060                         found= TRUE;
1061                         switch (RNA_property_type(prop)) {
1062                                 case PROP_BOOLEAN:
1063                                         *value= (float)RNA_property_boolean_get(&ptr, prop);
1064                                         break;
1065                                 case PROP_INT:
1066                                         *value= (float)RNA_property_int_get(&ptr, prop);
1067                                         break;
1068                                 case PROP_ENUM:
1069                                         *value= (float)RNA_property_enum_get(&ptr, prop);
1070                                         break;
1071                                 case PROP_FLOAT:
1072                                         *value= RNA_property_float_get(&ptr, prop);
1073                                         break;
1074                                 default:
1075                                         found= FALSE;
1076                                         break;
1077                         }
1078                 }
1079         }
1080         
1081         return found;
1082 }
1083
1084 /* propagate just works along each F-Curve in turn */
1085 static void pose_propagate_fcurve (wmOperator *op, Object *ob, FCurve *fcu, 
1086                                 float startFrame, tPosePropagate_ModeData modeData)
1087 {
1088         const int mode = RNA_enum_get(op->ptr, "mode");
1089         
1090         BezTriple *bezt;
1091         float refVal = 0.0f;
1092         short keyExists;
1093         int i, match;
1094         short first=1;
1095         
1096         /* skip if no keyframes to edit */
1097         if ((fcu->bezt == NULL) || (fcu->totvert < 2))
1098                 return;
1099                 
1100         /* find the reference value from bones directly, which means that the user
1101          * doesn't need to firstly keyframe the pose (though this doesn't mean that 
1102          * they can't either)
1103          */
1104         if( !pose_propagate_get_refVal(ob, fcu, &refVal))
1105                 return;
1106         
1107         /* find the first keyframe to start propagating from 
1108          *      - if there's a keyframe on the current frame, we probably want to save this value there too
1109          *        since it may be as of yet unkeyed
1110          *      - if starting before the starting frame, don't touch the key, as it may have had some valid 
1111          *        values
1112          */
1113         match = binarysearch_bezt_index(fcu->bezt, startFrame, fcu->totvert, &keyExists);
1114         
1115         if (fcu->bezt[match].vec[1][0] < startFrame)
1116                 i = match + 1;
1117         else
1118                 i = match;
1119         
1120         for (bezt = &fcu->bezt[i]; i < fcu->totvert; i++, bezt++) {
1121                 /* additional termination conditions based on the operator 'mode' property go here... */
1122                 if (ELEM(mode, POSE_PROPAGATE_BEFORE_FRAME, POSE_PROPAGATE_SMART_HOLDS)) {
1123                         /* stop if keyframe is outside the accepted range */
1124                         if (bezt->vec[1][0] > modeData.end_frame)
1125                                 break; 
1126                 }
1127                 else if (mode == POSE_PROPAGATE_NEXT_KEY) {
1128                         /* stop after the first keyframe has been processed */
1129                         if (first == 0)
1130                                 break;
1131                 }
1132                 else if (mode == POSE_PROPAGATE_LAST_KEY) {
1133                         /* only affect this frame if it will be the last one */
1134                         if (i != (fcu->totvert-1))
1135                                 continue;
1136                 }
1137                 else if (mode == POSE_PROPAGATE_SELECTED_MARKERS) {
1138                         /* only allow if there's a marker on this frame */
1139                         CfraElem *ce = NULL;
1140                         
1141                         /* stop on matching marker if there is one */
1142                         for (ce = modeData.sel_markers.first; ce; ce = ce->next) {
1143                                 if (ce->cfra == (int)(floor(bezt->vec[1][0] + 0.5f)))
1144                                         break;
1145                         }
1146                         
1147                         /* skip this keyframe if no marker */
1148                         if (ce == NULL)
1149                                 continue;
1150                 }
1151                 
1152                 /* just flatten handles, since values will now be the same either side... */
1153                 // TODO: perhaps a fade-out modulation of the value is required here (optional once again)?
1154                 bezt->vec[0][1] = bezt->vec[1][1] = bezt->vec[2][1] = refVal;
1155                 
1156                 /* select keyframe to indicate that it's been changed */
1157                 bezt->f2 |= SELECT;
1158                 first = 0;
1159         }
1160 }
1161
1162 /* --------------------------------- */
1163
1164 static int pose_propagate_exec (bContext *C, wmOperator *op)
1165 {
1166         Scene *scene = CTX_data_scene(C);
1167         Object *ob= ED_object_pose_armature(CTX_data_active_object(C));
1168         bAction *act= (ob && ob->adt)? ob->adt->action : NULL;
1169         
1170         ListBase pflinks = {NULL, NULL};
1171         tPChanFCurveLink *pfl;
1172         
1173         tPosePropagate_ModeData modeData;
1174         const int mode = RNA_enum_get(op->ptr, "mode");
1175         
1176         /* sanity checks */
1177         if (ob == NULL) {
1178                 BKE_report(op->reports, RPT_ERROR, "No object to propagate poses for");
1179                 return OPERATOR_CANCELLED;
1180         }
1181         if (act == NULL) {
1182                 BKE_report(op->reports, RPT_ERROR, "No keyframed poses to propagate to");
1183                 return OPERATOR_CANCELLED;
1184         }
1185         
1186         /* isolate F-Curves related to the selected bones */
1187         poseAnim_mapping_get(C, &pflinks, ob, act);
1188         
1189         /* mode-specific data preprocessing (requiring no access to curves) */
1190         if (mode == POSE_PROPAGATE_SELECTED_MARKERS) {
1191                 /* get a list of selected markers */
1192                 ED_markers_make_cfra_list(&scene->markers, &modeData.sel_markers, SELECT);
1193         }
1194         else {
1195                 /* assume everything else wants endFrame */
1196                 modeData.end_frame = RNA_float_get(op->ptr, "end_frame");
1197         }
1198         
1199         /* for each bone, perform the copying required */
1200         for (pfl = pflinks.first; pfl; pfl = pfl->next) {
1201                 LinkData *ld;
1202                 
1203                 /* mode-specific data preprocessing (requiring access to all curves) */
1204                 if (mode == POSE_PROPAGATE_SMART_HOLDS) {
1205                         /* we store in endFrame the end frame of the "long keyframe" (i.e. a held value) starting
1206                          * from the keyframe that occurs after the current frame
1207                          */
1208                         modeData.end_frame = pose_propagate_get_boneHoldEndFrame(ob, pfl, (float)CFRA);
1209                 }
1210                 
1211                 /* go through propagating pose to keyframes, curve by curve */
1212                 for (ld = pfl->fcurves.first; ld; ld= ld->next)
1213                         pose_propagate_fcurve(op, ob, (FCurve *)ld->data, (float)CFRA, modeData);
1214         }
1215         
1216         /* free temp data */
1217         poseAnim_mapping_free(&pflinks);
1218         
1219         if (mode == POSE_PROPAGATE_SELECTED_MARKERS)
1220                 BLI_freelistN(&modeData.sel_markers);
1221         
1222         /* updates + notifiers */
1223         poseAnim_mapping_refresh(C, scene, ob);
1224         
1225         return OPERATOR_FINISHED;
1226 }
1227
1228 /* --------------------------------- */
1229
1230 void POSE_OT_propagate (wmOperatorType *ot)
1231 {
1232         static EnumPropertyItem terminate_items[]= {
1233                 {POSE_PROPAGATE_SMART_HOLDS, "WHILE_HELD", 0, "While Held", "Propagate pose to all keyframes after current frame that don't change (Default behaviour)"},
1234                 {POSE_PROPAGATE_NEXT_KEY, "NEXT_KEY", 0, "To Next Keyframe", "Propagate pose to first keyframe following the current frame only"},
1235                 {POSE_PROPAGATE_LAST_KEY, "LAST_KEY", 0, "To Last Keyframe", "Propagate pose to the last keyframe only (i.e. making action cyclic)"},
1236                 {POSE_PROPAGATE_BEFORE_FRAME, "BEFORE_FRAME", 0, "Before Frame", "Propagate pose to all keyframes between current frame and 'Frame' property"},
1237                 {POSE_PROPAGATE_BEFORE_END, "BEFORE_END", 0, "Before Last Keyframe", "Propagate pose to all keyframes from current frame until no more are found"},
1238                 {POSE_PROPAGATE_SELECTED_MARKERS, "SELECTED_MARKERS", 0, "On Selected Markers", "Propagate pose to all keyframes occurring on frames with Scene Markers after the current frame"},
1239                 {0, NULL, 0, NULL, NULL}};
1240                 
1241         /* identifiers */
1242         ot->name= "Propagate Pose";
1243         ot->idname= "POSE_OT_propagate";
1244         ot->description= "Copy selected aspects of the current pose to subsequent poses already keyframed";
1245         
1246         /* callbacks */
1247         ot->exec= pose_propagate_exec;
1248         ot->poll= ED_operator_posemode; // XXX: needs selected bones!
1249         
1250         /* flag */
1251         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1252         
1253         /* properties */
1254         // TODO: add "fade out" control for tapering off amount of propagation as time goes by?
1255         ot->prop= RNA_def_enum(ot->srna, "mode", terminate_items, POSE_PROPAGATE_SMART_HOLDS, "Terminate Mode", "Method used to determine when to stop propagating pose to keyframes");
1256         RNA_def_float(ot->srna, "end_frame", 250.0, FLT_MIN, FLT_MAX, "End Frame", "Frame to stop propagating frames to (for 'Before Frame' mode)", 1.0, 250.0);
1257 }
1258
1259 /* **************************************************** */