1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2007, Blender Foundation
17  * This is a new part of Blender
18  */
19 
20 /** \file
21  * \ingroup edarmature
22  */
23 
24 #include <math.h>
25 #include <string.h>
26 
27 #include "MEM_guardedalloc.h"
28 
29 #include "BLI_blenlib.h"
30 #include "BLI_dlrbTree.h"
31 #include "BLI_string_utils.h"
32 
33 #include "BLT_translation.h"
34 
35 #include "DNA_anim_types.h"
36 #include "DNA_armature_types.h"
37 #include "DNA_object_types.h"
38 #include "DNA_scene_types.h"
39 
40 #include "BKE_action.h"
41 #include "BKE_animsys.h"
42 #include "BKE_armature.h"
43 #include "BKE_idprop.h"
44 #include "BKE_lib_id.h"
45 #include "BKE_main.h"
46 #include "BKE_object.h"
47 
48 #include "BKE_context.h"
49 #include "BKE_report.h"
50 
51 #include "DEG_depsgraph.h"
52 
53 #include "RNA_access.h"
54 #include "RNA_define.h"
55 #include "RNA_enum_types.h"
56 
57 #include "WM_api.h"
58 #include "WM_types.h"
59 
60 #include "UI_interface.h"
61 #include "UI_resources.h"
62 
63 #include "ED_anim_api.h"
64 #include "ED_armature.h"
65 #include "ED_keyframes_draw.h"
66 #include "ED_keyframes_edit.h"
67 #include "ED_keyframing.h"
68 #include "ED_object.h"
69 #include "ED_screen.h"
70 
71 #include "armature_intern.h"
72 
73 /* ******* XXX ********** */
74 
action_set_activemarker(void * UNUSED (a),void * UNUSED (b),void * UNUSED (c))75 static void action_set_activemarker(void *UNUSED(a), void *UNUSED(b), void *UNUSED(c))
76 {
77 }
78 
79 /* ************************************************************* */
80 /**
81  * Pose-Library Tool for Blender
82  * =============================
83  *
84  * Overview:
85  *  This tool allows animators to store a set of frequently used poses to dump into
86  *  the active action to help in "budget" productions to quickly block out new actions.
87  *  It acts as a kind of "glorified clipboard for poses", allowing for naming of poses.
88  *
89  * Features:
90  * - Pose-libs are simply normal Actions.
91  * - Each "pose" is simply a set of key-frames that occur on a particular frame.
92  *   - A set of #TimeMarker that belong to each Action, help 'label' where a 'pose' can be
93  *     found in the Action.
94  * - The Scroll-wheel or PageUp/Down buttons when used in a special mode or after pressing/holding
95  *   [a modifier] key, cycles through the poses available for the active pose's pose-lib,
96  *   allowing the animator to preview what action best suits that pose.
97  */
98 /* ************************************************************* */
99 
100 /* gets the first available frame in poselib to store a pose on
101  * - frames start from 1, and a pose should occur on every frame... 0 is error!
102  */
poselib_get_free_index(bAction * act)103 static int poselib_get_free_index(bAction *act)
104 {
105   TimeMarker *marker;
106   int low = 0, high = 0;
107   bool changed = false;
108 
109   /* sanity checks */
110   if (ELEM(NULL, act, act->markers.first)) {
111     return 1;
112   }
113 
114   /* As poses are not stored in chronological order, we must iterate over this list
115    * a few times until we don't make any new discoveries (mostly about the lower bound).
116    * Prevents problems with deleting then trying to add new poses T27412.
117    */
118   do {
119     changed = false;
120 
121     for (marker = act->markers.first; marker; marker = marker->next) {
122       /* only increase low if value is 1 greater than low, to find "gaps" where
123        * poses were removed from the poselib
124        */
125       if (marker->frame == (low + 1)) {
126         low++;
127         changed = true;
128       }
129 
130       /* value replaces high if it is the highest value encountered yet */
131       if (marker->frame > high) {
132         high = marker->frame;
133         changed = true;
134       }
135     }
136   } while (changed != 0);
137 
138   /* - if low is not equal to high, then low+1 is a gap
139    * - if low is equal to high, then high+1 is the next index (add at end)
140    */
141   if (low < high) {
142     return (low + 1);
143   }
144   return (high + 1);
145 }
146 
147 /* returns the active pose for a poselib */
poselib_get_active_pose(bAction * act)148 static TimeMarker *poselib_get_active_pose(bAction *act)
149 {
150   if ((act) && (act->active_marker)) {
151     return BLI_findlink(&act->markers, act->active_marker - 1);
152   }
153   return NULL;
154 }
155 
156 /* Get object that Pose Lib should be found on */
157 /* XXX C can be zero */
get_poselib_object(bContext * C)158 static Object *get_poselib_object(bContext *C)
159 {
160   ScrArea *area;
161 
162   /* sanity check */
163   if (C == NULL) {
164     return NULL;
165   }
166 
167   area = CTX_wm_area(C);
168 
169   if (area && (area->spacetype == SPACE_PROPERTIES)) {
170     return ED_object_context(C);
171   }
172   return BKE_object_pose_armature_get(CTX_data_active_object(C));
173 }
174 
175 /* Poll callback for operators that require existing PoseLib data (with poses) to work */
has_poselib_pose_data_poll(bContext * C)176 static bool has_poselib_pose_data_poll(bContext *C)
177 {
178   Object *ob = get_poselib_object(C);
179   return (ob && ob->poselib);
180 }
181 
182 /* Poll callback for operators that require existing PoseLib data (with poses)
183  * as they need to do some editing work on those poses (i.e. not on lib-linked actions)
184  */
has_poselib_pose_data_for_editing_poll(bContext * C)185 static bool has_poselib_pose_data_for_editing_poll(bContext *C)
186 {
187   Object *ob = get_poselib_object(C);
188   return (ob && ob->poselib && !ID_IS_LINKED(ob->poselib));
189 }
190 
191 /* ----------------------------------- */
192 
193 /* Initialize a new poselib (whether it is needed or not) */
poselib_init_new(Main * bmain,Object * ob)194 static bAction *poselib_init_new(Main *bmain, Object *ob)
195 {
196   /* sanity checks - only for armatures */
197   if (ELEM(NULL, ob, ob->pose)) {
198     return NULL;
199   }
200 
201   /* init object's poselib action (unlink old one if there) */
202   if (ob->poselib) {
203     id_us_min(&ob->poselib->id);
204   }
205 
206   ob->poselib = BKE_action_add(bmain, "PoseLib");
207   ob->poselib->idroot = ID_OB;
208 
209   return ob->poselib;
210 }
211 
212 /* Initialize a new poselib (checks if that needs to happen) */
poselib_validate(Main * bmain,Object * ob)213 static bAction *poselib_validate(Main *bmain, Object *ob)
214 {
215   if (ELEM(NULL, ob, ob->pose)) {
216     return NULL;
217   }
218   if (ob->poselib == NULL) {
219     return poselib_init_new(bmain, ob);
220   }
221   return ob->poselib;
222 }
223 
224 /* ************************************************************* */
225 /* Pose Lib UI Operators */
226 
poselib_new_exec(bContext * C,wmOperator * UNUSED (op))227 static int poselib_new_exec(bContext *C, wmOperator *UNUSED(op))
228 {
229   Main *bmain = CTX_data_main(C);
230   Object *ob = get_poselib_object(C);
231 
232   /* sanity checks */
233   if (ob == NULL) {
234     return OPERATOR_CANCELLED;
235   }
236 
237   /* new method here deals with the rest... */
238   poselib_init_new(bmain, ob);
239 
240   /* notifier here might evolve? */
241   WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
242 
243   return OPERATOR_FINISHED;
244 }
245 
POSELIB_OT_new(wmOperatorType * ot)246 void POSELIB_OT_new(wmOperatorType *ot)
247 {
248   /* identifiers */
249   ot->name = "New Pose Library";
250   ot->idname = "POSELIB_OT_new";
251   ot->description = "Add New Pose Library to active Object";
252 
253   /* callbacks */
254   ot->exec = poselib_new_exec;
255   ot->poll = ED_operator_posemode;
256 
257   /* flags */
258   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
259 }
260 
261 /* ------------------------------------------------ */
262 
poselib_unlink_exec(bContext * C,wmOperator * UNUSED (op))263 static int poselib_unlink_exec(bContext *C, wmOperator *UNUSED(op))
264 {
265   Object *ob = get_poselib_object(C);
266 
267   /* sanity checks */
268   if (ELEM(NULL, ob, ob->poselib)) {
269     return OPERATOR_CANCELLED;
270   }
271 
272   /* there should be a poselib (we just checked above!), so just lower its user count and remove */
273   id_us_min(&ob->poselib->id);
274   ob->poselib = NULL;
275 
276   /* notifier here might evolve? */
277   WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
278 
279   return OPERATOR_FINISHED;
280 }
281 
POSELIB_OT_unlink(wmOperatorType * ot)282 void POSELIB_OT_unlink(wmOperatorType *ot)
283 {
284   /* identifiers */
285   ot->name = "Unlink Pose Library";
286   ot->idname = "POSELIB_OT_unlink";
287   ot->description = "Remove Pose Library from active Object";
288 
289   /* callbacks */
290   ot->exec = poselib_unlink_exec;
291   ot->poll = has_poselib_pose_data_poll;
292 
293   /* flags */
294   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
295 }
296 
297 /* ************************************************************* */
298 /* Pose Editing Operators */
299 
300 /* This tool automagically generates/validates poselib data so that it corresponds to the data
301  * in the action. This is for use in making existing actions usable as poselibs.
302  */
poselib_sanitize_exec(bContext * C,wmOperator * op)303 static int poselib_sanitize_exec(bContext *C, wmOperator *op)
304 {
305   Object *ob = get_poselib_object(C);
306   bAction *act = (ob) ? ob->poselib : NULL;
307   DLRBT_Tree keys;
308   ActKeyColumn *ak;
309   TimeMarker *marker, *markern;
310 
311   /* validate action */
312   if (act == NULL) {
313     BKE_report(op->reports, RPT_WARNING, "No action to validate");
314     return OPERATOR_CANCELLED;
315   }
316 
317   /* determine which frames have keys */
318   BLI_dlrbTree_init(&keys);
319   action_to_keylist(NULL, act, &keys, 0);
320 
321   /* for each key, make sure there is a corresponding pose */
322   for (ak = keys.first; ak; ak = ak->next) {
323     /* check if any pose matches this */
324     /* TODO: don't go looking through the list like this every time... */
325     for (marker = act->markers.first; marker; marker = marker->next) {
326       if (IS_EQ((double)marker->frame, (double)ak->cfra)) {
327         marker->flag = -1;
328         break;
329       }
330     }
331 
332     /* add new if none found */
333     if (marker == NULL) {
334       /* add pose to poselib */
335       marker = MEM_callocN(sizeof(TimeMarker), "ActionMarker");
336 
337       BLI_snprintf(marker->name, sizeof(marker->name), "F%d Pose", (int)ak->cfra);
338 
339       marker->frame = (int)ak->cfra;
340       marker->flag = -1;
341 
342       BLI_addtail(&act->markers, marker);
343     }
344   }
345 
346   /* remove all untagged poses (unused), and remove all tags */
347   for (marker = act->markers.first; marker; marker = markern) {
348     markern = marker->next;
349 
350     if (marker->flag != -1) {
351       BLI_freelinkN(&act->markers, marker);
352     }
353     else {
354       marker->flag = 0;
355     }
356   }
357 
358   /* free temp memory */
359   BLI_dlrbTree_free(&keys);
360 
361   /* send notifiers for this - using keyframe editing notifiers, since action
362    * may be being shown in anim editors as active action
363    */
364   WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
365 
366   return OPERATOR_FINISHED;
367 }
368 
POSELIB_OT_action_sanitize(wmOperatorType * ot)369 void POSELIB_OT_action_sanitize(wmOperatorType *ot)
370 {
371   /* identifiers */
372   ot->name = "Sanitize Pose Library Action";
373   ot->idname = "POSELIB_OT_action_sanitize";
374   ot->description = "Make action suitable for use as a Pose Library";
375 
376   /* callbacks */
377   ot->exec = poselib_sanitize_exec;
378   ot->poll = has_poselib_pose_data_for_editing_poll;
379 
380   /* flags */
381   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
382 }
383 
384 /* ------------------------------------------ */
385 
386 /* Poll callback for adding poses to a PoseLib */
poselib_add_poll(bContext * C)387 static bool poselib_add_poll(bContext *C)
388 {
389   /* There are 2 cases we need to be careful with:
390    *  1) When this operator is invoked from a hotkey, there may be no PoseLib yet
391    *  2) If a PoseLib already exists, we can't edit the action if it is a lib-linked
392    *     actions, as data will be lost when saving the file
393    */
394   if (ED_operator_posemode(C)) {
395     Object *ob = get_poselib_object(C);
396     if (ob) {
397       if ((ob->poselib == NULL) || !ID_IS_LINKED(ob->poselib)) {
398         return true;
399       }
400     }
401   }
402   return false;
403 }
404 
poselib_add_menu_invoke__replacemenu(bContext * C,uiLayout * layout,void * UNUSED (arg))405 static void poselib_add_menu_invoke__replacemenu(bContext *C, uiLayout *layout, void *UNUSED(arg))
406 {
407   Object *ob = get_poselib_object(C);
408   bAction *act = ob->poselib; /* never NULL */
409   TimeMarker *marker;
410 
411   wmOperatorType *ot = WM_operatortype_find("POSELIB_OT_pose_add", 1);
412 
413   BLI_assert(ot != NULL);
414 
415   /* set the operator execution context correctly */
416   uiLayoutSetOperatorContext(layout, WM_OP_EXEC_DEFAULT);
417 
418   /* add each marker to this menu */
419   for (marker = act->markers.first; marker; marker = marker->next) {
420     PointerRNA props_ptr;
421     uiItemFullO_ptr(
422         layout, ot, marker->name, ICON_ARMATURE_DATA, NULL, WM_OP_EXEC_DEFAULT, 0, &props_ptr);
423     RNA_int_set(&props_ptr, "frame", marker->frame);
424     RNA_string_set(&props_ptr, "name", marker->name);
425   }
426 }
427 
poselib_add_menu_invoke(bContext * C,wmOperator * op,const wmEvent * UNUSED (event))428 static int poselib_add_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
429 {
430   Scene *scene = CTX_data_scene(C);
431   Object *ob = get_poselib_object(C);
432   bPose *pose = (ob) ? ob->pose : NULL;
433   uiPopupMenu *pup;
434   uiLayout *layout;
435 
436   /* sanity check */
437   if (ELEM(NULL, ob, pose)) {
438     return OPERATOR_CANCELLED;
439   }
440 
441   /* start building */
442   pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE);
443   layout = UI_popup_menu_layout(pup);
444   uiLayoutSetOperatorContext(layout, WM_OP_EXEC_DEFAULT);
445 
446   /* add new (adds to the first unoccupied frame) */
447   uiItemIntO(layout,
448              IFACE_("Add New"),
449              ICON_NONE,
450              "POSELIB_OT_pose_add",
451              "frame",
452              poselib_get_free_index(ob->poselib));
453 
454   /* check if we have any choices to add a new pose in any other way */
455   if ((ob->poselib) && (ob->poselib->markers.first)) {
456     /* add new (on current frame) */
457     uiItemIntO(layout,
458                IFACE_("Add New (Current Frame)"),
459                ICON_NONE,
460                "POSELIB_OT_pose_add",
461                "frame",
462                CFRA);
463 
464     /* replace existing - submenu */
465     uiItemMenuF(
466         layout, IFACE_("Replace Existing..."), 0, poselib_add_menu_invoke__replacemenu, NULL);
467   }
468 
469   UI_popup_menu_end(C, pup);
470 
471   /* this operator is only for a menu, not used further */
472   return OPERATOR_INTERFACE;
473 }
474 
poselib_add_exec(bContext * C,wmOperator * op)475 static int poselib_add_exec(bContext *C, wmOperator *op)
476 {
477   Main *bmain = CTX_data_main(C);
478   Object *ob = get_poselib_object(C);
479   bAction *act = poselib_validate(bmain, ob);
480   bPose *pose = (ob) ? ob->pose : NULL;
481   TimeMarker *marker;
482   KeyingSet *ks;
483   int frame = RNA_int_get(op->ptr, "frame");
484   char name[64];
485 
486   /* sanity check (invoke should have checked this anyway) */
487   if (ELEM(NULL, ob, pose)) {
488     return OPERATOR_CANCELLED;
489   }
490 
491   /* get name to give to pose */
492   RNA_string_get(op->ptr, "name", name);
493 
494   /* add pose to poselib - replaces any existing pose there
495    * - for the 'replace' option, this should end up finding the appropriate marker,
496    *   so no new one will be added
497    */
498   for (marker = act->markers.first; marker; marker = marker->next) {
499     if (marker->frame == frame) {
500       BLI_strncpy(marker->name, name, sizeof(marker->name));
501       break;
502     }
503   }
504   if (marker == NULL) {
505     marker = MEM_callocN(sizeof(TimeMarker), "ActionMarker");
506 
507     BLI_strncpy(marker->name, name, sizeof(marker->name));
508     marker->frame = frame;
509 
510     BLI_addtail(&act->markers, marker);
511   }
512 
513   /* validate name */
514   BLI_uniquename(
515       &act->markers, marker, DATA_("Pose"), '.', offsetof(TimeMarker, name), sizeof(marker->name));
516 
517   /* use Keying Set to determine what to store for the pose */
518 
519   /* this includes custom props :)*/
520   ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_WHOLE_CHARACTER_SELECTED_ID);
521 
522   ANIM_apply_keyingset(C, NULL, act, ks, MODIFYKEY_MODE_INSERT, (float)frame);
523 
524   /* store new 'active' pose number */
525   act->active_marker = BLI_listbase_count(&act->markers);
526   DEG_id_tag_update(&act->id, ID_RECALC_COPY_ON_WRITE);
527 
528   /* done */
529   return OPERATOR_FINISHED;
530 }
531 
POSELIB_OT_pose_add(wmOperatorType * ot)532 void POSELIB_OT_pose_add(wmOperatorType *ot)
533 {
534   /* identifiers */
535   ot->name = "PoseLib Add Pose";
536   ot->idname = "POSELIB_OT_pose_add";
537   ot->description = "Add the current Pose to the active Pose Library";
538 
539   /* api callbacks */
540   ot->invoke = poselib_add_menu_invoke;
541   ot->exec = poselib_add_exec;
542   ot->poll = poselib_add_poll;
543 
544   /* flags */
545   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
546 
547   /* properties */
548   RNA_def_int(ot->srna, "frame", 1, 0, INT_MAX, "Frame", "Frame to store pose on", 0, INT_MAX);
549   RNA_def_string(ot->srna, "name", "Pose", 64, "Pose Name", "Name of newly added Pose");
550 }
551 
552 /* ----- */
553 
554 /* can be called with C == NULL */
poselib_stored_pose_itemf(bContext * C,PointerRNA * UNUSED (ptr),PropertyRNA * UNUSED (prop),bool * r_free)555 static const EnumPropertyItem *poselib_stored_pose_itemf(bContext *C,
556                                                          PointerRNA *UNUSED(ptr),
557                                                          PropertyRNA *UNUSED(prop),
558                                                          bool *r_free)
559 {
560   Object *ob = get_poselib_object(C);
561   bAction *act = (ob) ? ob->poselib : NULL;
562   TimeMarker *marker;
563   EnumPropertyItem *item = NULL, item_tmp = {0};
564   int totitem = 0;
565   int i = 0;
566 
567   if (C == NULL) {
568     return DummyRNA_NULL_items;
569   }
570 
571   /* check that the action exists */
572   if (act) {
573     /* add each marker to the list */
574     for (marker = act->markers.first, i = 0; marker; marker = marker->next, i++) {
575       item_tmp.identifier = item_tmp.name = marker->name;
576       item_tmp.icon = ICON_ARMATURE_DATA;
577       item_tmp.value = i;
578       RNA_enum_item_add(&item, &totitem, &item_tmp);
579     }
580   }
581 
582   RNA_enum_item_end(&item, &totitem);
583   *r_free = true;
584 
585   return item;
586 }
587 
poselib_remove_exec(bContext * C,wmOperator * op)588 static int poselib_remove_exec(bContext *C, wmOperator *op)
589 {
590   Object *ob = get_poselib_object(C);
591   bAction *act = (ob) ? ob->poselib : NULL;
592   TimeMarker *marker;
593   int marker_index;
594   FCurve *fcu;
595   PropertyRNA *prop;
596 
597   /* check if valid poselib */
598   if (act == NULL) {
599     BKE_report(op->reports, RPT_ERROR, "Object does not have pose lib data");
600     return OPERATOR_CANCELLED;
601   }
602 
603   prop = RNA_struct_find_property(op->ptr, "pose");
604   if (RNA_property_is_set(op->ptr, prop)) {
605     marker_index = RNA_property_enum_get(op->ptr, prop);
606   }
607   else {
608     marker_index = act->active_marker - 1;
609   }
610 
611   /* get index (and pointer) of pose to remove */
612   marker = BLI_findlink(&act->markers, marker_index);
613   if (marker == NULL) {
614     BKE_reportf(op->reports, RPT_ERROR, "Invalid pose specified %d", marker_index);
615     return OPERATOR_CANCELLED;
616   }
617 
618   /* remove relevant keyframes */
619   for (fcu = act->curves.first; fcu; fcu = fcu->next) {
620     BezTriple *bezt;
621     uint i;
622 
623     if (fcu->bezt) {
624       for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
625         /* check if remove */
626         if (IS_EQF(bezt->vec[1][0], (float)marker->frame)) {
627           delete_fcurve_key(fcu, i, 1);
628           break;
629         }
630       }
631     }
632   }
633 
634   /* remove poselib from list */
635   BLI_freelinkN(&act->markers, marker);
636 
637   /* fix active pose number */
638   act->active_marker = 0;
639 
640   /* send notifiers for this - using keyframe editing notifiers, since action
641    * may be being shown in anim editors as active action
642    */
643   WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
644   DEG_id_tag_update(&act->id, ID_RECALC_COPY_ON_WRITE);
645 
646   /* done */
647   return OPERATOR_FINISHED;
648 }
649 
POSELIB_OT_pose_remove(wmOperatorType * ot)650 void POSELIB_OT_pose_remove(wmOperatorType *ot)
651 {
652   PropertyRNA *prop;
653 
654   /* identifiers */
655   ot->name = "PoseLib Remove Pose";
656   ot->idname = "POSELIB_OT_pose_remove";
657   ot->description = "Remove nth pose from the active Pose Library";
658 
659   /* api callbacks */
660   ot->invoke = WM_menu_invoke;
661   ot->exec = poselib_remove_exec;
662   ot->poll = has_poselib_pose_data_for_editing_poll;
663 
664   /* flags */
665   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
666 
667   /* properties */
668   prop = RNA_def_enum(ot->srna, "pose", DummyRNA_NULL_items, 0, "Pose", "The pose to remove");
669   RNA_def_enum_funcs(prop, poselib_stored_pose_itemf);
670   RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
671   ot->prop = prop;
672 }
673 
poselib_rename_invoke(bContext * C,wmOperator * op,const wmEvent * event)674 static int poselib_rename_invoke(bContext *C, wmOperator *op, const wmEvent *event)
675 {
676   Object *ob = get_poselib_object(C);
677   bAction *act = (ob) ? ob->poselib : NULL;
678   TimeMarker *marker;
679 
680   /* check if valid poselib */
681   if (act == NULL) {
682     BKE_report(op->reports, RPT_ERROR, "Object does not have pose lib data");
683     return OPERATOR_CANCELLED;
684   }
685 
686   /* get index (and pointer) of pose to remove */
687   marker = BLI_findlink(&act->markers, act->active_marker - 1);
688   if (marker == NULL) {
689     BKE_report(op->reports, RPT_ERROR, "Invalid index for pose");
690     return OPERATOR_CANCELLED;
691   }
692 
693   /* Use the existing name of the marker as the name,
694    * and use the active marker as the one to rename. */
695   RNA_enum_set(op->ptr, "pose", act->active_marker - 1);
696   RNA_string_set(op->ptr, "name", marker->name);
697 
698   /* part to sync with other similar operators... */
699   return WM_operator_props_popup_confirm(C, op, event);
700 }
701 
poselib_rename_exec(bContext * C,wmOperator * op)702 static int poselib_rename_exec(bContext *C, wmOperator *op)
703 {
704   Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
705   bAction *act = (ob) ? ob->poselib : NULL;
706   TimeMarker *marker;
707   char newname[64];
708 
709   /* check if valid poselib */
710   if (act == NULL) {
711     BKE_report(op->reports, RPT_ERROR, "Object does not have pose lib data");
712     return OPERATOR_CANCELLED;
713   }
714 
715   /* get index (and pointer) of pose to remove */
716   marker = BLI_findlink(&act->markers, RNA_enum_get(op->ptr, "pose"));
717   if (marker == NULL) {
718     BKE_report(op->reports, RPT_ERROR, "Invalid index for pose");
719     return OPERATOR_CANCELLED;
720   }
721 
722   /* get new name */
723   RNA_string_get(op->ptr, "name", newname);
724 
725   /* copy name and validate it */
726   BLI_strncpy(marker->name, newname, sizeof(marker->name));
727   BLI_uniquename(
728       &act->markers, marker, DATA_("Pose"), '.', offsetof(TimeMarker, name), sizeof(marker->name));
729 
730   /* send notifiers for this - using keyframe editing notifiers, since action
731    * may be being shown in anim editors as active action
732    */
733   WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
734 
735   /* done */
736   return OPERATOR_FINISHED;
737 }
738 
POSELIB_OT_pose_rename(wmOperatorType * ot)739 void POSELIB_OT_pose_rename(wmOperatorType *ot)
740 {
741   PropertyRNA *prop;
742 
743   /* identifiers */
744   ot->name = "PoseLib Rename Pose";
745   ot->idname = "POSELIB_OT_pose_rename";
746   ot->description = "Rename specified pose from the active Pose Library";
747 
748   /* api callbacks */
749   ot->invoke = poselib_rename_invoke;
750   ot->exec = poselib_rename_exec;
751   ot->poll = has_poselib_pose_data_for_editing_poll;
752 
753   /* flags */
754   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
755 
756   /* properties */
757   /* NOTE: name not pose is the operator's "main" property,
758    * so that it will get activated in the popup for easy renaming */
759   ot->prop = RNA_def_string(
760       ot->srna, "name", "RenamedPose", 64, "New Pose Name", "New name for pose");
761   prop = RNA_def_enum(ot->srna, "pose", DummyRNA_NULL_items, 0, "Pose", "The pose to rename");
762   RNA_def_enum_funcs(prop, poselib_stored_pose_itemf);
763   RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
764 }
765 
poselib_move_exec(bContext * C,wmOperator * op)766 static int poselib_move_exec(bContext *C, wmOperator *op)
767 {
768   Object *ob = get_poselib_object(C);
769   bAction *act = (ob) ? ob->poselib : NULL;
770   TimeMarker *marker;
771   int marker_index;
772   int dir;
773   PropertyRNA *prop;
774 
775   /* check if valid poselib */
776   if (act == NULL) {
777     BKE_report(op->reports, RPT_ERROR, "Object does not have pose lib data");
778     return OPERATOR_CANCELLED;
779   }
780 
781   prop = RNA_struct_find_property(op->ptr, "pose");
782   if (RNA_property_is_set(op->ptr, prop)) {
783     marker_index = RNA_property_enum_get(op->ptr, prop);
784   }
785   else {
786     marker_index = act->active_marker - 1;
787   }
788 
789   /* get index (and pointer) of pose to remove */
790   marker = BLI_findlink(&act->markers, marker_index);
791   if (marker == NULL) {
792     BKE_reportf(op->reports, RPT_ERROR, "Invalid pose specified %d", marker_index);
793     return OPERATOR_CANCELLED;
794   }
795 
796   dir = RNA_enum_get(op->ptr, "direction");
797 
798   /* move pose */
799   if (BLI_listbase_link_move(&act->markers, marker, dir)) {
800     act->active_marker = marker_index + dir + 1;
801 
802     /* send notifiers for this - using keyframe editing notifiers, since action
803      * may be being shown in anim editors as active action
804      */
805     WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
806   }
807   else {
808     return OPERATOR_CANCELLED;
809   }
810 
811   /* done */
812   return OPERATOR_FINISHED;
813 }
814 
POSELIB_OT_pose_move(wmOperatorType * ot)815 void POSELIB_OT_pose_move(wmOperatorType *ot)
816 {
817   PropertyRNA *prop;
818   static const EnumPropertyItem pose_lib_pose_move[] = {
819       {-1, "UP", 0, "Up", ""},
820       {1, "DOWN", 0, "Down", ""},
821       {0, NULL, 0, NULL, NULL},
822   };
823 
824   /* identifiers */
825   ot->name = "PoseLib Move Pose";
826   ot->idname = "POSELIB_OT_pose_move";
827   ot->description = "Move the pose up or down in the active Pose Library";
828 
829   /* api callbacks */
830   ot->invoke = WM_menu_invoke;
831   ot->exec = poselib_move_exec;
832   ot->poll = has_poselib_pose_data_for_editing_poll;
833 
834   /* flags */
835   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
836 
837   /* properties */
838   prop = RNA_def_enum(ot->srna, "pose", DummyRNA_NULL_items, 0, "Pose", "The pose to move");
839   RNA_def_enum_funcs(prop, poselib_stored_pose_itemf);
840   RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
841   ot->prop = prop;
842 
843   RNA_def_enum(ot->srna,
844                "direction",
845                pose_lib_pose_move,
846                0,
847                "Direction",
848                "Direction to move the chosen pose towards");
849 }
850 
851 /* ************************************************************* */
852 /* Pose-Lib Browsing/Previewing Operator */
853 
854 /* Simple struct for storing settings/data for use during PoseLib preview */
855 typedef struct tPoseLib_PreviewData {
856   /** tPoseLib_Backup structs for restoring poses. */
857   ListBase backups;
858   /** LinkData structs storing list of poses which match the current search-string. */
859   ListBase searchp;
860 
861   /** active scene. */
862   Scene *scene;
863   /** active area. */
864   ScrArea *area;
865 
866   /** RNA-Pointer to Object 'ob' .*/
867   PointerRNA rna_ptr;
868   /** object to work on. */
869   Object *ob;
870   /** object's armature data. */
871   bArmature *arm;
872   /** object's pose. */
873   bPose *pose;
874   /** poselib to use. */
875   bAction *act;
876   /** 'active' pose. */
877   TimeMarker *marker;
878 
879   /** total number of elements to work on. */
880   int totcount;
881 
882   /** state of main loop. */
883   short state;
884   /** redraw/update settings during main loop. */
885   short redraw;
886   /** flags for various settings. */
887   short flag;
888 
889   /** position of cursor in searchstr (cursor occurs before the item at the nominated index) */
890   short search_cursor;
891   /** (Part of) Name to search for to filter poses that get shown. */
892   char searchstr[64];
893   /** Previously set searchstr (from last loop run),
894    * so that we can detected when to rebuild searchp. */
895   char searchold[64];
896 
897   /** Info-text to print in header. */
898   char headerstr[UI_MAX_DRAW_STR];
899 } tPoseLib_PreviewData;
900 
901 /* defines for tPoseLib_PreviewData->state values */
902 enum {
903   PL_PREVIEW_ERROR = -1,
904   PL_PREVIEW_RUNNING,
905   PL_PREVIEW_CONFIRM,
906   PL_PREVIEW_CANCEL,
907   PL_PREVIEW_RUNONCE,
908 };
909 
910 /* defines for tPoseLib_PreviewData->redraw values */
911 enum {
912   PL_PREVIEW_NOREDRAW = 0,
913   PL_PREVIEW_REDRAWALL,
914   PL_PREVIEW_REDRAWHEADER,
915 };
916 
917 /* defines for tPoseLib_PreviewData->flag values */
918 enum {
919   PL_PREVIEW_FIRSTTIME = (1 << 0),
920   PL_PREVIEW_SHOWORIGINAL = (1 << 1),
921   PL_PREVIEW_ANY_BONE_SELECTED = (1 << 2),
922 };
923 
924 /* ---------------------------- */
925 
926 /* simple struct for storing backup info */
927 typedef struct tPoseLib_Backup {
928   struct tPoseLib_Backup *next, *prev;
929 
930   bPoseChannel *pchan; /* pose channel backups are for */
931 
932   bPoseChannel olddata; /* copy of pose channel's old data (at start) */
933   IDProperty *oldprops; /* copy (needs freeing) of pose channel's properties (at start) */
934 } tPoseLib_Backup;
935 
936 /* Makes a copy of the current pose for restoration purposes - doesn't do constraints currently */
poselib_backup_posecopy(tPoseLib_PreviewData * pld)937 static void poselib_backup_posecopy(tPoseLib_PreviewData *pld)
938 {
939   bActionGroup *agrp;
940   bPoseChannel *pchan;
941   bool selected = false;
942 
943   /* determine whether any bone is selected. */
944   LISTBASE_FOREACH (bPoseChannel *, bchan, &pld->pose->chanbase) {
945     selected = bchan->bone != NULL && bchan->bone->flag & BONE_SELECTED;
946     if (selected) {
947       pld->flag |= PL_PREVIEW_ANY_BONE_SELECTED;
948       break;
949     }
950   }
951   if (!selected) {
952     pld->flag &= ~PL_PREVIEW_ANY_BONE_SELECTED;
953   }
954 
955   /* for each posechannel that has an actionchannel in */
956   for (agrp = pld->act->groups.first; agrp; agrp = agrp->next) {
957     /* try to find posechannel */
958     pchan = BKE_pose_channel_find_name(pld->pose, agrp->name);
959 
960     /* backup data if available */
961     if (pchan) {
962       tPoseLib_Backup *plb;
963 
964       /* store backup */
965       plb = MEM_callocN(sizeof(tPoseLib_Backup), "tPoseLib_Backup");
966 
967       plb->pchan = pchan;
968       memcpy(&plb->olddata, plb->pchan, sizeof(bPoseChannel));
969 
970       if (pchan->prop) {
971         plb->oldprops = IDP_CopyProperty(pchan->prop);
972       }
973 
974       BLI_addtail(&pld->backups, plb);
975 
976       /* mark as being affected */
977       pld->totcount++;
978     }
979   }
980 }
981 
982 /* Restores original pose */
poselib_backup_restore(tPoseLib_PreviewData * pld)983 static void poselib_backup_restore(tPoseLib_PreviewData *pld)
984 {
985   tPoseLib_Backup *plb;
986 
987   for (plb = pld->backups.first; plb; plb = plb->next) {
988     /* copy most of data straight back */
989     memcpy(plb->pchan, &plb->olddata, sizeof(bPoseChannel));
990 
991     /* just overwrite values of properties from the stored copies (there should be some) */
992     if (plb->oldprops) {
993       IDP_SyncGroupValues(plb->pchan->prop, plb->oldprops);
994     }
995 
996     /* TODO: constraints settings aren't restored yet,
997      * even though these could change (though not that likely) */
998   }
999 }
1000 
1001 /* Free list of backups, including any side data it may use */
poselib_backup_free_data(tPoseLib_PreviewData * pld)1002 static void poselib_backup_free_data(tPoseLib_PreviewData *pld)
1003 {
1004   tPoseLib_Backup *plb, *plbn;
1005 
1006   for (plb = pld->backups.first; plb; plb = plbn) {
1007     plbn = plb->next;
1008 
1009     /* free custom data */
1010     if (plb->oldprops) {
1011       IDP_FreeProperty(plb->oldprops);
1012     }
1013 
1014     /* free backup element now */
1015     BLI_freelinkN(&pld->backups, plb);
1016   }
1017 }
1018 
1019 /* ---------------------------- */
1020 
1021 /* Applies the appropriate stored pose from the pose-library to the current pose
1022  * - assumes that a valid object, with a poselib has been supplied
1023  * - gets the string to print in the header
1024  * - this code is based on the code for extract_pose_from_action in blenkernel/action.c
1025  */
poselib_apply_pose(tPoseLib_PreviewData * pld,const AnimationEvalContext * anim_eval_context)1026 static void poselib_apply_pose(tPoseLib_PreviewData *pld,
1027                                const AnimationEvalContext *anim_eval_context)
1028 {
1029   PointerRNA *ptr = &pld->rna_ptr;
1030   bArmature *arm = pld->arm;
1031   bPose *pose = pld->pose;
1032   bPoseChannel *pchan;
1033   bAction *act = pld->act;
1034   bActionGroup *agrp;
1035 
1036   KeyframeEditData ked = {{NULL}};
1037   KeyframeEditFunc group_ok_cb;
1038   int frame = 1;
1039   const bool any_bone_selected = pld->flag & PL_PREVIEW_ANY_BONE_SELECTED;
1040 
1041   /* get the frame */
1042   if (pld->marker) {
1043     frame = pld->marker->frame;
1044   }
1045   else {
1046     return;
1047   }
1048 
1049   /* init settings for testing groups for keyframes */
1050   group_ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAMERANGE);
1051   ked.f1 = ((float)frame) - 0.5f;
1052   ked.f2 = ((float)frame) + 0.5f;
1053   AnimationEvalContext anim_context_at_frame = BKE_animsys_eval_context_construct_at(
1054       anim_eval_context, frame);
1055 
1056   /* start applying - only those channels which have a key at this point in time! */
1057   for (agrp = act->groups.first; agrp; agrp = agrp->next) {
1058     /* check if group has any keyframes */
1059     if (ANIM_animchanneldata_keyframes_loop(
1060             &ked, NULL, agrp, ALE_GROUP, NULL, group_ok_cb, NULL)) {
1061       /* has keyframe on this frame, so try to get a PoseChannel with this name */
1062       pchan = BKE_pose_channel_find_name(pose, agrp->name);
1063 
1064       if (pchan) {
1065         bool ok = 0;
1066 
1067         /* check if this bone should get any animation applied */
1068         if (!any_bone_selected) {
1069           /* if no bones are selected, then any bone is ok */
1070           ok = 1;
1071         }
1072         else if (pchan->bone) {
1073           /* only ok if bone is visible and selected */
1074           if ((pchan->bone->flag & BONE_SELECTED) && (pchan->bone->flag & BONE_HIDDEN_P) == 0 &&
1075               (pchan->bone->layer & arm->layer)) {
1076             ok = 1;
1077           }
1078         }
1079 
1080         if (ok) {
1081           animsys_evaluate_action_group(ptr, act, agrp, &anim_context_at_frame);
1082         }
1083       }
1084     }
1085   }
1086 }
1087 
1088 /* Auto-keys/tags bones affected by the pose used from the poselib */
poselib_keytag_pose(bContext * C,Scene * scene,tPoseLib_PreviewData * pld)1089 static void poselib_keytag_pose(bContext *C, Scene *scene, tPoseLib_PreviewData *pld)
1090 {
1091   bPose *pose = pld->pose;
1092   bPoseChannel *pchan;
1093   bAction *act = pld->act;
1094   bActionGroup *agrp;
1095 
1096   KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_WHOLE_CHARACTER_ID);
1097   ListBase dsources = {NULL, NULL};
1098   bool autokey = autokeyframe_cfra_can_key(scene, &pld->ob->id);
1099   const bool any_bone_selected = pld->flag & PL_PREVIEW_ANY_BONE_SELECTED;
1100 
1101   /* start tagging/keying */
1102   for (agrp = act->groups.first; agrp; agrp = agrp->next) {
1103     /* only for selected bones unless there aren't any selected, in which case all are included  */
1104     pchan = BKE_pose_channel_find_name(pose, agrp->name);
1105 
1106     if (pchan) {
1107       if (!any_bone_selected || ((pchan->bone) && (pchan->bone->flag & BONE_SELECTED))) {
1108         if (autokey) {
1109           /* add datasource override for the PoseChannel, to be used later */
1110           ANIM_relative_keyingset_add_source(&dsources, &pld->ob->id, &RNA_PoseBone, pchan);
1111 
1112           /* clear any unkeyed tags */
1113           if (pchan->bone) {
1114             pchan->bone->flag &= ~BONE_UNKEYED;
1115           }
1116         }
1117         else {
1118           /* add unkeyed tags */
1119           if (pchan->bone) {
1120             pchan->bone->flag |= BONE_UNKEYED;
1121           }
1122         }
1123       }
1124     }
1125   }
1126 
1127   /* perform actual auto-keying now */
1128   if (autokey) {
1129     /* insert keyframes for all relevant bones in one go */
1130     ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
1131     BLI_freelistN(&dsources);
1132   }
1133 
1134   /* send notifiers for this */
1135   WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
1136 }
1137 
1138 /* Apply the relevant changes to the pose */
poselib_preview_apply(bContext * C,wmOperator * op)1139 static void poselib_preview_apply(bContext *C, wmOperator *op)
1140 {
1141   tPoseLib_PreviewData *pld = (tPoseLib_PreviewData *)op->customdata;
1142 
1143   /* only recalc pose (and its dependencies) if pose has changed */
1144   if (pld->redraw == PL_PREVIEW_REDRAWALL) {
1145     /* don't clear pose if firsttime */
1146     if ((pld->flag & PL_PREVIEW_FIRSTTIME) == 0) {
1147       poselib_backup_restore(pld);
1148     }
1149     else {
1150       pld->flag &= ~PL_PREVIEW_FIRSTTIME;
1151     }
1152 
1153     /* pose should be the right one to draw (unless we're temporarily not showing it) */
1154     if ((pld->flag & PL_PREVIEW_SHOWORIGINAL) == 0) {
1155       RNA_int_set(op->ptr, "pose_index", BLI_findindex(&pld->act->markers, pld->marker));
1156       struct Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
1157 
1158       const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(
1159           depsgraph, 0.0f /* poselib_apply_pose() determines its own evaluation time. */);
1160       poselib_apply_pose(pld, &anim_eval_context);
1161     }
1162     else {
1163       RNA_int_set(op->ptr, "pose_index", -2); /* -2 means don't apply any pose */
1164     }
1165 
1166     DEG_id_tag_update(&pld->ob->id, ID_RECALC_GEOMETRY);
1167   }
1168 
1169   /* do header print - if interactively previewing */
1170   if (pld->state == PL_PREVIEW_RUNNING) {
1171     if (pld->flag & PL_PREVIEW_SHOWORIGINAL) {
1172       ED_area_status_text(pld->area, TIP_("PoseLib Previewing Pose: [Showing Original Pose]"));
1173       ED_workspace_status_text(C, TIP_("Use Tab to start previewing poses again"));
1174     }
1175     else if (pld->searchstr[0]) {
1176       char tempstr[65];
1177       char markern[64];
1178       short index;
1179 
1180       /* get search-string */
1181       index = pld->search_cursor;
1182 
1183       if (index >= 0 && index < sizeof(tempstr) - 1) {
1184         memcpy(&tempstr[0], &pld->searchstr[0], index);
1185         tempstr[index] = '|';
1186         memcpy(&tempstr[index + 1], &pld->searchstr[index], (sizeof(tempstr) - 1) - index);
1187       }
1188       else {
1189         BLI_strncpy(tempstr, pld->searchstr, sizeof(tempstr));
1190       }
1191 
1192       /* get marker name */
1193       BLI_strncpy(markern, pld->marker ? pld->marker->name : "No Matches", sizeof(markern));
1194 
1195       BLI_snprintf(pld->headerstr,
1196                    sizeof(pld->headerstr),
1197                    TIP_("PoseLib Previewing Pose: Filter - [%s] | "
1198                         "Current Pose - \"%s\""),
1199                    tempstr,
1200                    markern);
1201       ED_area_status_text(pld->area, pld->headerstr);
1202       ED_workspace_status_text(C, TIP_("Use ScrollWheel or PageUp/Down to change pose"));
1203     }
1204     else {
1205       BLI_snprintf(pld->headerstr,
1206                    sizeof(pld->headerstr),
1207                    TIP_("PoseLib Previewing Pose: \"%s\""),
1208                    pld->marker->name);
1209       ED_area_status_text(pld->area, pld->headerstr);
1210       ED_workspace_status_text(C, NULL);
1211     }
1212   }
1213 
1214   /* request drawing of view + clear redraw flag */
1215   WM_event_add_notifier(C, NC_OBJECT | ND_POSE, pld->ob);
1216   pld->redraw = PL_PREVIEW_NOREDRAW;
1217 }
1218 
1219 /* ---------------------------- */
1220 
1221 /* This helper function is called during poselib_preview_poses to find the
1222  * pose to preview next (after a change event)
1223  */
poselib_preview_get_next(tPoseLib_PreviewData * pld,int step)1224 static void poselib_preview_get_next(tPoseLib_PreviewData *pld, int step)
1225 {
1226   /* stop if not going anywhere, as we assume that there is a direction to move in */
1227   if (step == 0) {
1228     return;
1229   }
1230 
1231   /* search-string dictates a special approach */
1232   if (pld->searchstr[0]) {
1233     TimeMarker *marker;
1234     LinkData *ld, *ldn, *ldc;
1235 
1236     /* free and rebuild if needed (i.e. if search-str changed) */
1237     if (!STREQ(pld->searchstr, pld->searchold)) {
1238       /* free list of temporary search matches */
1239       BLI_freelistN(&pld->searchp);
1240 
1241       /* generate a new list of search matches */
1242       for (marker = pld->act->markers.first; marker; marker = marker->next) {
1243         /* does the name partially match?
1244          * - don't worry about case, to make it easier for users to quickly input a name (or
1245          *   part of one), which is the whole point of this feature
1246          */
1247         if (BLI_strcasestr(marker->name, pld->searchstr)) {
1248           /* make link-data to store reference to it */
1249           ld = MEM_callocN(sizeof(LinkData), "PoseMatch");
1250           ld->data = marker;
1251           BLI_addtail(&pld->searchp, ld);
1252         }
1253       }
1254 
1255       /* set current marker to NULL (so that we start from first) */
1256       pld->marker = NULL;
1257     }
1258 
1259     /* check if any matches */
1260     if (BLI_listbase_is_empty(&pld->searchp)) {
1261       pld->marker = NULL;
1262       return;
1263     }
1264 
1265     /* find first match */
1266     for (ldc = pld->searchp.first; ldc; ldc = ldc->next) {
1267       if (ldc->data == pld->marker) {
1268         break;
1269       }
1270     }
1271     if (ldc == NULL) {
1272       ldc = pld->searchp.first;
1273     }
1274 
1275     /* Loop through the matches in a cyclic fashion, incrementing/decrementing step as appropriate
1276      * until step == 0. At this point, marker should be the correct marker.
1277      */
1278     if (step > 0) {
1279       for (ld = ldc; ld && step; ld = ldn, step--) {
1280         ldn = (ld->next) ? ld->next : pld->searchp.first;
1281       }
1282     }
1283     else {
1284       for (ld = ldc; ld && step; ld = ldn, step++) {
1285         ldn = (ld->prev) ? ld->prev : pld->searchp.last;
1286       }
1287     }
1288 
1289     /* set marker */
1290     if (ld) {
1291       pld->marker = ld->data;
1292     }
1293   }
1294   else {
1295     TimeMarker *marker, *next;
1296 
1297     /* if no marker, because we just ended searching, then set that to the start of the list */
1298     if (pld->marker == NULL) {
1299       pld->marker = pld->act->markers.first;
1300     }
1301 
1302     /* Loop through the markers in a cyclic fashion, incrementing/decrementing step as appropriate
1303      * until step == 0. At this point, marker should be the correct marker.
1304      */
1305     if (step > 0) {
1306       for (marker = pld->marker; marker && step; marker = next, step--) {
1307         next = (marker->next) ? marker->next : pld->act->markers.first;
1308       }
1309     }
1310     else {
1311       for (marker = pld->marker; marker && step; marker = next, step++) {
1312         next = (marker->prev) ? marker->prev : pld->act->markers.last;
1313       }
1314     }
1315 
1316     /* it should be fairly impossible for marker to be NULL */
1317     if (marker) {
1318       pld->marker = marker;
1319     }
1320   }
1321 }
1322 
1323 /* specially handle events for searching */
poselib_preview_handle_search(tPoseLib_PreviewData * pld,ushort event,char ascii)1324 static void poselib_preview_handle_search(tPoseLib_PreviewData *pld, ushort event, char ascii)
1325 {
1326   /* try doing some form of string manipulation first */
1327   switch (event) {
1328     case EVT_BACKSPACEKEY:
1329       if (pld->searchstr[0] && pld->search_cursor) {
1330         short len = strlen(pld->searchstr);
1331         short index = pld->search_cursor;
1332         short i;
1333 
1334         for (i = index; i <= len; i++) {
1335           pld->searchstr[i - 1] = pld->searchstr[i];
1336         }
1337 
1338         pld->search_cursor--;
1339 
1340         poselib_preview_get_next(pld, 1);
1341         pld->redraw = PL_PREVIEW_REDRAWALL;
1342         return;
1343       }
1344       break;
1345 
1346     case EVT_DELKEY:
1347       if (pld->searchstr[0] && pld->searchstr[1]) {
1348         short len = strlen(pld->searchstr);
1349         short index = pld->search_cursor;
1350         int i;
1351 
1352         if (index < len) {
1353           for (i = index; i < len; i++) {
1354             pld->searchstr[i] = pld->searchstr[i + 1];
1355           }
1356 
1357           poselib_preview_get_next(pld, 1);
1358           pld->redraw = PL_PREVIEW_REDRAWALL;
1359           return;
1360         }
1361       }
1362       break;
1363   }
1364 
1365   if (ascii) {
1366     /* character to add to the string */
1367     short index = pld->search_cursor;
1368     short len = (pld->searchstr[0]) ? strlen(pld->searchstr) : 0;
1369     short i;
1370 
1371     if (len) {
1372       for (i = len; i > index; i--) {
1373         pld->searchstr[i] = pld->searchstr[i - 1];
1374       }
1375     }
1376     else {
1377       pld->searchstr[1] = 0;
1378     }
1379 
1380     pld->searchstr[index] = ascii;
1381     pld->search_cursor++;
1382 
1383     poselib_preview_get_next(pld, 1);
1384     pld->redraw = PL_PREVIEW_REDRAWALL;
1385   }
1386 }
1387 
1388 /* handle events for poselib_preview_poses */
poselib_preview_handle_event(bContext * UNUSED (C),wmOperator * op,const wmEvent * event)1389 static int poselib_preview_handle_event(bContext *UNUSED(C), wmOperator *op, const wmEvent *event)
1390 {
1391   tPoseLib_PreviewData *pld = op->customdata;
1392   int ret = OPERATOR_RUNNING_MODAL;
1393 
1394   /* only accept 'press' event, and ignore 'release', so that we don't get double actions */
1395   if (ELEM(event->val, KM_PRESS, KM_NOTHING) == 0) {
1396 #if 0
1397     printf("PoseLib: skipping event with type '%s' and val %d\n",
1398            WM_key_event_string(event->type, false),
1399            event->val);
1400 #endif
1401     return ret;
1402   }
1403 
1404   /* backup stuff that needs to occur before every operation
1405    * - make a copy of searchstr, so that we know if cache needs to be rebuilt
1406    */
1407   BLI_strncpy(pld->searchold, pld->searchstr, sizeof(pld->searchold));
1408 
1409   /* if we're currently showing the original pose, only certain events are handled */
1410   if (pld->flag & PL_PREVIEW_SHOWORIGINAL) {
1411     switch (event->type) {
1412       /* exit - cancel */
1413       case EVT_ESCKEY:
1414       case RIGHTMOUSE:
1415         pld->state = PL_PREVIEW_CANCEL;
1416         break;
1417 
1418       /* exit - confirm */
1419       case LEFTMOUSE:
1420       case EVT_RETKEY:
1421       case EVT_PADENTER:
1422       case EVT_SPACEKEY:
1423         pld->state = PL_PREVIEW_CONFIRM;
1424         break;
1425 
1426       /* view manipulation */
1427       /* we add pass through here, so that the operators responsible for these can still run,
1428        * even though we still maintain control (as RUNNING_MODAL flag is still set too)
1429        */
1430       case EVT_PAD0:
1431       case EVT_PAD1:
1432       case EVT_PAD2:
1433       case EVT_PAD3:
1434       case EVT_PAD4:
1435       case EVT_PAD5:
1436       case EVT_PAD6:
1437       case EVT_PAD7:
1438       case EVT_PAD8:
1439       case EVT_PAD9:
1440       case EVT_PADPLUSKEY:
1441       case EVT_PADMINUS:
1442       case MIDDLEMOUSE:
1443       case MOUSEMOVE:
1444         // pld->redraw = PL_PREVIEW_REDRAWHEADER;
1445         ret = OPERATOR_PASS_THROUGH;
1446         break;
1447 
1448       /* quicky compare to original */
1449       case EVT_TABKEY:
1450         pld->flag &= ~PL_PREVIEW_SHOWORIGINAL;
1451         pld->redraw = PL_PREVIEW_REDRAWALL;
1452         break;
1453     }
1454 
1455     /* EXITS HERE... */
1456     return ret;
1457   }
1458 
1459   /* NORMAL EVENT HANDLING... */
1460   /* searching takes priority over normal activity */
1461   switch (event->type) {
1462     /* exit - cancel */
1463     case EVT_ESCKEY:
1464     case RIGHTMOUSE:
1465       pld->state = PL_PREVIEW_CANCEL;
1466       break;
1467 
1468     /* exit - confirm */
1469     case LEFTMOUSE:
1470     case EVT_RETKEY:
1471     case EVT_PADENTER:
1472     case EVT_SPACEKEY:
1473       pld->state = PL_PREVIEW_CONFIRM;
1474       break;
1475 
1476     /* toggle between original pose and poselib pose*/
1477     case EVT_TABKEY:
1478       pld->flag |= PL_PREVIEW_SHOWORIGINAL;
1479       pld->redraw = PL_PREVIEW_REDRAWALL;
1480       break;
1481 
1482     /* change to previous pose (cyclic) */
1483     case EVT_PAGEUPKEY:
1484     case WHEELUPMOUSE:
1485       poselib_preview_get_next(pld, -1);
1486       pld->redraw = PL_PREVIEW_REDRAWALL;
1487       break;
1488 
1489     /* change to next pose (cyclic) */
1490     case EVT_PAGEDOWNKEY:
1491     case WHEELDOWNMOUSE:
1492       poselib_preview_get_next(pld, 1);
1493       pld->redraw = PL_PREVIEW_REDRAWALL;
1494       break;
1495 
1496     /* jump 5 poses (cyclic, back) */
1497     case EVT_DOWNARROWKEY:
1498       poselib_preview_get_next(pld, -5);
1499       pld->redraw = PL_PREVIEW_REDRAWALL;
1500       break;
1501 
1502     /* jump 5 poses (cyclic, forward) */
1503     case EVT_UPARROWKEY:
1504       poselib_preview_get_next(pld, 5);
1505       pld->redraw = PL_PREVIEW_REDRAWALL;
1506       break;
1507 
1508     /* change to next pose or searching cursor control */
1509     case EVT_RIGHTARROWKEY:
1510       if (pld->searchstr[0]) {
1511         /* move text-cursor to the right */
1512         if (pld->search_cursor < strlen(pld->searchstr)) {
1513           pld->search_cursor++;
1514         }
1515         pld->redraw = PL_PREVIEW_REDRAWHEADER;
1516       }
1517       else {
1518         /* change to next pose (cyclic) */
1519         poselib_preview_get_next(pld, 1);
1520         pld->redraw = PL_PREVIEW_REDRAWALL;
1521       }
1522       break;
1523 
1524     /* change to next pose or searching cursor control */
1525     case EVT_LEFTARROWKEY:
1526       if (pld->searchstr[0]) {
1527         /* move text-cursor to the left */
1528         if (pld->search_cursor) {
1529           pld->search_cursor--;
1530         }
1531         pld->redraw = PL_PREVIEW_REDRAWHEADER;
1532       }
1533       else {
1534         /* change to previous pose (cyclic) */
1535         poselib_preview_get_next(pld, -1);
1536         pld->redraw = PL_PREVIEW_REDRAWALL;
1537       }
1538       break;
1539 
1540     /* change to first pose or start of searching string */
1541     case EVT_HOMEKEY:
1542       if (pld->searchstr[0]) {
1543         pld->search_cursor = 0;
1544         pld->redraw = PL_PREVIEW_REDRAWHEADER;
1545       }
1546       else {
1547         /* change to first pose */
1548         pld->marker = pld->act->markers.first;
1549         pld->act->active_marker = 1;
1550 
1551         pld->redraw = PL_PREVIEW_REDRAWALL;
1552       }
1553       break;
1554 
1555     /* change to last pose or start of searching string */
1556     case EVT_ENDKEY:
1557       if (pld->searchstr[0]) {
1558         pld->search_cursor = strlen(pld->searchstr);
1559         pld->redraw = PL_PREVIEW_REDRAWHEADER;
1560       }
1561       else {
1562         /* change to last pose */
1563         pld->marker = pld->act->markers.last;
1564         pld->act->active_marker = BLI_listbase_count(&pld->act->markers);
1565 
1566         pld->redraw = PL_PREVIEW_REDRAWALL;
1567       }
1568       break;
1569 
1570     /* view manipulation */
1571     /* we add pass through here, so that the operators responsible for these can still run,
1572      * even though we still maintain control (as RUNNING_MODAL flag is still set too)
1573      */
1574     case MIDDLEMOUSE:
1575     case MOUSEMOVE:
1576       // pld->redraw = PL_PREVIEW_REDRAWHEADER;
1577       ret = OPERATOR_PASS_THROUGH;
1578       break;
1579 
1580     /* view manipulation, or searching */
1581     case EVT_PAD0:
1582     case EVT_PAD1:
1583     case EVT_PAD2:
1584     case EVT_PAD3:
1585     case EVT_PAD4:
1586     case EVT_PAD5:
1587     case EVT_PAD6:
1588     case EVT_PAD7:
1589     case EVT_PAD8:
1590     case EVT_PAD9:
1591     case EVT_PADPLUSKEY:
1592     case EVT_PADMINUS:
1593       if (pld->searchstr[0]) {
1594         /* searching... */
1595         poselib_preview_handle_search(pld, event->type, event->ascii);
1596       }
1597       else {
1598         /* view manipulation (see above) */
1599         // pld->redraw = PL_PREVIEW_REDRAWHEADER;
1600         ret = OPERATOR_PASS_THROUGH;
1601       }
1602       break;
1603 
1604     /* otherwise, assume that searching might be able to handle it */
1605     default:
1606       poselib_preview_handle_search(pld, event->type, event->ascii);
1607       break;
1608   }
1609 
1610   return ret;
1611 }
1612 
1613 /* ---------------------------- */
1614 
1615 /* Init PoseLib Previewing data */
poselib_preview_init_data(bContext * C,wmOperator * op)1616 static void poselib_preview_init_data(bContext *C, wmOperator *op)
1617 {
1618   tPoseLib_PreviewData *pld;
1619   Object *ob = get_poselib_object(C);
1620   int pose_index = RNA_int_get(op->ptr, "pose_index");
1621 
1622   /* set up preview state info */
1623   op->customdata = pld = MEM_callocN(sizeof(tPoseLib_PreviewData), "PoseLib Preview Data");
1624 
1625   /* get basic data */
1626   pld->ob = ob;
1627   pld->arm = (ob) ? (ob->data) : NULL;
1628   pld->pose = (ob) ? (ob->pose) : NULL;
1629   pld->act = (ob) ? (ob->poselib) : NULL;
1630 
1631   pld->scene = CTX_data_scene(C);
1632   pld->area = CTX_wm_area(C);
1633 
1634   /* get starting pose based on RNA-props for this operator */
1635   if (pose_index == -1) {
1636     pld->marker = poselib_get_active_pose(pld->act);
1637   }
1638   else if (pose_index == -2) {
1639     pld->flag |= PL_PREVIEW_SHOWORIGINAL;
1640   }
1641   else {
1642     pld->marker = (pld->act) ? BLI_findlink(&pld->act->markers, pose_index) : NULL;
1643   }
1644 
1645   /* check if valid poselib */
1646   if (ELEM(NULL, pld->ob, pld->pose, pld->arm)) {
1647     BKE_report(op->reports, RPT_ERROR, "Pose lib is only for armatures in pose mode");
1648     pld->state = PL_PREVIEW_ERROR;
1649     return;
1650   }
1651   if (pld->act == NULL) {
1652     BKE_report(op->reports, RPT_ERROR, "Object does not have a valid pose lib");
1653     pld->state = PL_PREVIEW_ERROR;
1654     return;
1655   }
1656   if (pld->marker == NULL) {
1657     if (pld->act->markers.first) {
1658       /* just use first one then... */
1659       pld->marker = pld->act->markers.first;
1660       if (pose_index > -2) {
1661         BKE_report(op->reports, RPT_WARNING, "Pose lib had no active pose");
1662       }
1663     }
1664     else {
1665       BKE_report(op->reports, RPT_ERROR, "Pose lib has no poses to preview/apply");
1666       pld->state = PL_PREVIEW_ERROR;
1667       return;
1668     }
1669   }
1670 
1671   /* get ID pointer for applying poses */
1672   RNA_id_pointer_create(&ob->id, &pld->rna_ptr);
1673 
1674   /* make backups for restoring pose */
1675   poselib_backup_posecopy(pld);
1676 
1677   /* set flags for running */
1678   pld->state = PL_PREVIEW_RUNNING;
1679   pld->redraw = PL_PREVIEW_REDRAWALL;
1680   pld->flag |= PL_PREVIEW_FIRSTTIME;
1681 
1682   /* set depsgraph flags */
1683   /* make sure the lock is set OK, unlock can be accidentally saved? */
1684   pld->pose->flag |= POSE_LOCKED;
1685   pld->pose->flag &= ~POSE_DO_UNLOCK;
1686 
1687   /* clear strings + search */
1688   pld->headerstr[0] = pld->searchstr[0] = pld->searchold[0] = '\0';
1689   pld->search_cursor = 0;
1690 }
1691 
1692 /* After previewing poses */
poselib_preview_cleanup(bContext * C,wmOperator * op)1693 static void poselib_preview_cleanup(bContext *C, wmOperator *op)
1694 {
1695   tPoseLib_PreviewData *pld = (tPoseLib_PreviewData *)op->customdata;
1696   Scene *scene = pld->scene;
1697   Object *ob = pld->ob;
1698   bPose *pose = pld->pose;
1699   bAction *act = pld->act;
1700   TimeMarker *marker = pld->marker;
1701 
1702   /* redraw the header so that it doesn't show any of our stuff anymore */
1703   ED_area_status_text(pld->area, NULL);
1704   ED_workspace_status_text(C, NULL);
1705 
1706   /* this signal does one recalc on pose, then unlocks, so ESC or edit will work */
1707   pose->flag |= POSE_DO_UNLOCK;
1708 
1709   /* clear pose if canceled */
1710   if (pld->state == PL_PREVIEW_CANCEL) {
1711     poselib_backup_restore(pld);
1712 
1713     DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
1714   }
1715   else if (pld->state == PL_PREVIEW_CONFIRM) {
1716     /* tag poses as appropriate */
1717     poselib_keytag_pose(C, scene, pld);
1718 
1719     /* change active pose setting */
1720     act->active_marker = BLI_findindex(&act->markers, marker) + 1;
1721     action_set_activemarker(act, marker, NULL);
1722 
1723     /* Update event for pose and deformation children */
1724     DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
1725 
1726     /* updates */
1727     if (IS_AUTOKEY_MODE(scene, NORMAL)) {
1728       // remake_action_ipos(ob->action);
1729     }
1730   }
1731 
1732   /* Request final redraw of the view. */
1733   WM_event_add_notifier(C, NC_OBJECT | ND_POSE, pld->ob);
1734 
1735   /* free memory used for backups and searching */
1736   poselib_backup_free_data(pld);
1737   BLI_freelistN(&pld->searchp);
1738 
1739   /* free temp data for operator */
1740   MEM_freeN(pld);
1741   op->customdata = NULL;
1742 }
1743 
1744 /* End previewing operation */
poselib_preview_exit(bContext * C,wmOperator * op)1745 static int poselib_preview_exit(bContext *C, wmOperator *op)
1746 {
1747   tPoseLib_PreviewData *pld = op->customdata;
1748   int exit_state = pld->state;
1749 
1750   /* finish up */
1751   poselib_preview_cleanup(C, op);
1752 
1753   if (ELEM(exit_state, PL_PREVIEW_CANCEL, PL_PREVIEW_ERROR)) {
1754     return OPERATOR_CANCELLED;
1755   }
1756   return OPERATOR_FINISHED;
1757 }
1758 
1759 /* Cancel previewing operation (called when exiting Blender) */
poselib_preview_cancel(bContext * C,wmOperator * op)1760 static void poselib_preview_cancel(bContext *C, wmOperator *op)
1761 {
1762   poselib_preview_exit(C, op);
1763 }
1764 
1765 /* main modal status check */
poselib_preview_modal(bContext * C,wmOperator * op,const wmEvent * event)1766 static int poselib_preview_modal(bContext *C, wmOperator *op, const wmEvent *event)
1767 {
1768   tPoseLib_PreviewData *pld = op->customdata;
1769   int ret;
1770 
1771   /* 1) check state to see if we're still running */
1772   if (pld->state != PL_PREVIEW_RUNNING) {
1773     return poselib_preview_exit(C, op);
1774   }
1775 
1776   /* 2) handle events */
1777   ret = poselib_preview_handle_event(C, op, event);
1778 
1779   /* 3) apply changes and redraw, otherwise, confirming goes wrong */
1780   if (pld->redraw) {
1781     poselib_preview_apply(C, op);
1782   }
1783 
1784   return ret;
1785 }
1786 
1787 /* Modal Operator init */
poselib_preview_invoke(bContext * C,wmOperator * op,const wmEvent * UNUSED (event))1788 static int poselib_preview_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
1789 {
1790   tPoseLib_PreviewData *pld;
1791 
1792   /* check if everything is ok, and init settings for modal operator */
1793   poselib_preview_init_data(C, op);
1794   pld = (tPoseLib_PreviewData *)op->customdata;
1795 
1796   if (pld->state == PL_PREVIEW_ERROR) {
1797     /* an error occurred, so free temp mem used */
1798     poselib_preview_cleanup(C, op);
1799     return OPERATOR_CANCELLED;
1800   }
1801 
1802   /* do initial apply to have something to look at */
1803   poselib_preview_apply(C, op);
1804 
1805   /* add temp handler if we're running as a modal operator */
1806   WM_event_add_modal_handler(C, op);
1807 
1808   return OPERATOR_RUNNING_MODAL;
1809 }
1810 
1811 /* Repeat operator */
poselib_preview_exec(bContext * C,wmOperator * op)1812 static int poselib_preview_exec(bContext *C, wmOperator *op)
1813 {
1814   tPoseLib_PreviewData *pld;
1815 
1816   /* check if everything is ok, and init settings for modal operator */
1817   poselib_preview_init_data(C, op);
1818   pld = (tPoseLib_PreviewData *)op->customdata;
1819 
1820   if (pld->state == PL_PREVIEW_ERROR) {
1821     /* an error occurred, so free temp mem used */
1822     poselib_preview_cleanup(C, op);
1823     return OPERATOR_CANCELLED;
1824   }
1825 
1826   /* the exec() callback is effectively a 'run-once' scenario, so set the state to that
1827    * so that everything draws correctly
1828    */
1829   pld->state = PL_PREVIEW_RUNONCE;
1830 
1831   /* apply the active pose */
1832   poselib_preview_apply(C, op);
1833 
1834   /* now, set the status to exit */
1835   pld->state = PL_PREVIEW_CONFIRM;
1836 
1837   /* cleanup */
1838   return poselib_preview_exit(C, op);
1839 }
1840 
POSELIB_OT_browse_interactive(wmOperatorType * ot)1841 void POSELIB_OT_browse_interactive(wmOperatorType *ot)
1842 {
1843   /* identifiers */
1844   ot->name = "PoseLib Browse Poses";
1845   ot->idname = "POSELIB_OT_browse_interactive";
1846   ot->description = "Interactively browse poses in 3D-View";
1847 
1848   /* callbacks */
1849   ot->invoke = poselib_preview_invoke;
1850   ot->modal = poselib_preview_modal;
1851   ot->cancel = poselib_preview_cancel;
1852   ot->exec = poselib_preview_exec;
1853   ot->poll = has_poselib_pose_data_poll;
1854 
1855   /* flags */
1856   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
1857 
1858   /* properties */
1859   /* TODO: make the pose_index into a proper enum instead of a cryptic int. */
1860   ot->prop = RNA_def_int(
1861       ot->srna,
1862       "pose_index",
1863       -1,
1864       -2,
1865       INT_MAX,
1866       "Pose",
1867       "Index of the pose to apply (-2 for no change to pose, -1 for poselib active pose)",
1868       0,
1869       INT_MAX);
1870 
1871   /* XXX: percentage vs factor? */
1872   /* not used yet */
1873 #if 0
1874   RNA_def_float_factor(ot->srna,
1875                        "blend_factor",
1876                        1.0f,
1877                        0.0f,
1878                        1.0f,
1879                        "Blend Factor",
1880                        "Amount that the pose is applied on top of the existing poses",
1881                        0.0f,
1882                        1.0f);
1883 #endif
1884 }
1885 
POSELIB_OT_apply_pose(wmOperatorType * ot)1886 void POSELIB_OT_apply_pose(wmOperatorType *ot)
1887 {
1888   /* identifiers */
1889   ot->name = "Apply Pose Library Pose";
1890   ot->idname = "POSELIB_OT_apply_pose";
1891   ot->description = "Apply specified Pose Library pose to the rig";
1892 
1893   /* callbacks */
1894   ot->exec = poselib_preview_exec;
1895   ot->poll = has_poselib_pose_data_poll;
1896 
1897   /* flags */
1898   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1899 
1900   /* properties */
1901   /* TODO: make the pose_index into a proper enum instead of a cryptic int... */
1902   ot->prop = RNA_def_int(
1903       ot->srna,
1904       "pose_index",
1905       -1,
1906       -2,
1907       INT_MAX,
1908       "Pose",
1909       "Index of the pose to apply (-2 for no change to pose, -1 for poselib active pose)",
1910       0,
1911       INT_MAX);
1912 }
1913