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