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) 2001-2002 by NaN Holding BV.
17  * All rights reserved.
18  * Pose Mode API's and Operators for Pose Mode armatures
19  */
20 
21 /** \file
22  * \ingroup edarmature
23  */
24 
25 #include "MEM_guardedalloc.h"
26 
27 #include "BLI_blenlib.h"
28 #include "BLI_math.h"
29 
30 #include "DNA_anim_types.h"
31 #include "DNA_armature_types.h"
32 #include "DNA_object_types.h"
33 #include "DNA_scene_types.h"
34 
35 #include "BKE_action.h"
36 #include "BKE_anim_visualization.h"
37 #include "BKE_armature.h"
38 #include "BKE_context.h"
39 #include "BKE_deform.h"
40 #include "BKE_global.h"
41 #include "BKE_layer.h"
42 #include "BKE_main.h"
43 #include "BKE_object.h"
44 #include "BKE_report.h"
45 #include "BKE_scene.h"
46 
47 #include "DEG_depsgraph.h"
48 #include "DEG_depsgraph_query.h"
49 
50 #include "RNA_access.h"
51 #include "RNA_define.h"
52 #include "RNA_enum_types.h"
53 
54 #include "WM_api.h"
55 #include "WM_types.h"
56 
57 #include "ED_anim_api.h"
58 #include "ED_armature.h"
59 #include "ED_keyframing.h"
60 #include "ED_object.h"
61 #include "ED_screen.h"
62 #include "ED_view3d.h"
63 
64 #include "UI_interface.h"
65 
66 #include "armature_intern.h"
67 
68 #undef DEBUG_TIME
69 
70 #include "PIL_time.h"
71 #ifdef DEBUG_TIME
72 #  include "PIL_time_utildefines.h"
73 #endif
74 
75 /* matches logic with ED_operator_posemode_context() */
ED_pose_object_from_context(bContext * C)76 Object *ED_pose_object_from_context(bContext *C)
77 {
78   ScrArea *area = CTX_wm_area(C);
79   Object *ob;
80 
81   /* Since this call may also be used from the buttons window,
82    * we need to check for where to get the object. */
83   if (area && area->spacetype == SPACE_PROPERTIES) {
84     ob = ED_object_context(C);
85   }
86   else {
87     ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
88   }
89 
90   return ob;
91 }
92 
93 /* This function is used to process the necessary updates for */
ED_object_posemode_enter_ex(struct Main * bmain,Object * ob)94 bool ED_object_posemode_enter_ex(struct Main *bmain, Object *ob)
95 {
96   BLI_assert(!ID_IS_LINKED(ob));
97   bool ok = false;
98 
99   switch (ob->type) {
100     case OB_ARMATURE:
101       ob->restore_mode = ob->mode;
102       ob->mode |= OB_MODE_POSE;
103       /* Inform all CoW versions that we changed the mode. */
104       DEG_id_tag_update_ex(bmain, &ob->id, ID_RECALC_COPY_ON_WRITE);
105       ok = true;
106 
107       break;
108     default:
109       break;
110   }
111 
112   return ok;
113 }
ED_object_posemode_enter(bContext * C,Object * ob)114 bool ED_object_posemode_enter(bContext *C, Object *ob)
115 {
116   ReportList *reports = CTX_wm_reports(C);
117   if (ID_IS_LINKED(ob)) {
118     BKE_report(reports, RPT_WARNING, "Cannot pose libdata");
119     return false;
120   }
121   struct Main *bmain = CTX_data_main(C);
122   bool ok = ED_object_posemode_enter_ex(bmain, ob);
123   if (ok) {
124     WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_POSE, NULL);
125   }
126   return ok;
127 }
128 
ED_object_posemode_exit_ex(struct Main * bmain,Object * ob)129 bool ED_object_posemode_exit_ex(struct Main *bmain, Object *ob)
130 {
131   bool ok = false;
132   if (ob) {
133     ob->restore_mode = ob->mode;
134     ob->mode &= ~OB_MODE_POSE;
135 
136     /* Inform all CoW versions that we changed the mode. */
137     DEG_id_tag_update_ex(bmain, &ob->id, ID_RECALC_COPY_ON_WRITE);
138     ok = true;
139   }
140   return ok;
141 }
ED_object_posemode_exit(bContext * C,Object * ob)142 bool ED_object_posemode_exit(bContext *C, Object *ob)
143 {
144   struct Main *bmain = CTX_data_main(C);
145   bool ok = ED_object_posemode_exit_ex(bmain, ob);
146   if (ok) {
147     WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_OBJECT, NULL);
148   }
149   return ok;
150 }
151 
152 /* if a selected or active bone is protected, throw error (oonly if warn == 1) and return 1 */
153 /* only_selected == 1: the active bone is allowed to be protected */
154 #if 0 /* UNUSED 2.5 */
155 static bool pose_has_protected_selected(Object *ob, short warn)
156 {
157   /* check protection */
158   if (ob->proxy) {
159     bPoseChannel *pchan;
160     bArmature *arm = ob->data;
161 
162     for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
163       if (pchan->bone && (pchan->bone->layer & arm->layer)) {
164         if (pchan->bone->layer & arm->layer_protected) {
165           if (pchan->bone->flag & BONE_SELECTED) {
166             break;
167           }
168         }
169       }
170     }
171     if (pchan) {
172       if (warn) {
173         error("Cannot change Proxy protected bones");
174       }
175       return 1;
176     }
177   }
178   return 0;
179 }
180 #endif
181 
182 /* ********************************************** */
183 /* Motion Paths */
184 
pose_path_convert_range(ePosePathCalcRange range)185 static eAnimvizCalcRange pose_path_convert_range(ePosePathCalcRange range)
186 {
187   switch (range) {
188     case POSE_PATH_CALC_RANGE_CURRENT_FRAME:
189       return ANIMVIZ_CALC_RANGE_CURRENT_FRAME;
190     case POSE_PATH_CALC_RANGE_CHANGED:
191       return ANIMVIZ_CALC_RANGE_CHANGED;
192     case POSE_PATH_CALC_RANGE_FULL:
193       return ANIMVIZ_CALC_RANGE_FULL;
194   }
195   return ANIMVIZ_CALC_RANGE_FULL;
196 }
197 
198 /* For the object with pose/action: update paths for those that have got them
199  * This should selectively update paths that exist...
200  *
201  * To be called from various tools that do incremental updates
202  */
ED_pose_recalculate_paths(bContext * C,Scene * scene,Object * ob,ePosePathCalcRange range)203 void ED_pose_recalculate_paths(bContext *C, Scene *scene, Object *ob, ePosePathCalcRange range)
204 {
205   /* Transform doesn't always have context available to do update. */
206   if (C == NULL) {
207     return;
208   }
209 
210   Main *bmain = CTX_data_main(C);
211   ViewLayer *view_layer = CTX_data_view_layer(C);
212 
213   Depsgraph *depsgraph;
214   bool free_depsgraph = false;
215 
216   ListBase targets = {NULL, NULL};
217   /* set flag to force recalc, then grab the relevant bones to target */
218   ob->pose->avs.recalc |= ANIMVIZ_RECALC_PATHS;
219   animviz_get_object_motionpaths(ob, &targets);
220 
221   /* recalculate paths, then free */
222 #ifdef DEBUG_TIME
223   TIMEIT_START(pose_path_calc);
224 #endif
225 
226   /* For a single frame update it's faster to re-use existing dependency graph and avoid overhead
227    * of building all the relations and so on for a temporary one.  */
228   if (range == POSE_PATH_CALC_RANGE_CURRENT_FRAME) {
229     /* NOTE: Dependency graph will be evaluated at all the frames, but we first need to access some
230      * nested pointers, like animation data. */
231     depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
232     free_depsgraph = false;
233   }
234   else {
235     depsgraph = animviz_depsgraph_build(bmain, scene, view_layer, &targets);
236     free_depsgraph = true;
237   }
238 
239   animviz_calc_motionpaths(
240       depsgraph, bmain, scene, &targets, pose_path_convert_range(range), !free_depsgraph);
241 
242 #ifdef DEBUG_TIME
243   TIMEIT_END(pose_path_calc);
244 #endif
245 
246   BLI_freelistN(&targets);
247 
248   if (range != POSE_PATH_CALC_RANGE_CURRENT_FRAME) {
249     /* Tag armature object for copy on write - so paths will draw/redraw.
250      * For currently frame only we update evaluated object directly. */
251     DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
252   }
253 
254   /* Free temporary depsgraph. */
255   if (free_depsgraph) {
256     DEG_graph_free(depsgraph);
257   }
258 }
259 
260 /* show popup to determine settings */
pose_calculate_paths_invoke(bContext * C,wmOperator * op,const wmEvent * UNUSED (event))261 static int pose_calculate_paths_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
262 {
263   Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
264 
265   if (ELEM(NULL, ob, ob->pose)) {
266     return OPERATOR_CANCELLED;
267   }
268 
269   /* set default settings from existing/stored settings */
270   {
271     bAnimVizSettings *avs = &ob->pose->avs;
272     PointerRNA avs_ptr;
273 
274     RNA_int_set(op->ptr, "start_frame", avs->path_sf);
275     RNA_int_set(op->ptr, "end_frame", avs->path_ef);
276 
277     RNA_pointer_create(NULL, &RNA_AnimVizMotionPaths, avs, &avs_ptr);
278     RNA_enum_set(op->ptr, "bake_location", RNA_enum_get(&avs_ptr, "bake_location"));
279   }
280 
281   /* show popup dialog to allow editing of range... */
282   /* FIXME: hard-coded dimensions here are just arbitrary. */
283   return WM_operator_props_dialog_popup(C, op, 200);
284 }
285 
286 /* For the object with pose/action: create path curves for selected bones
287  * This recalculates the WHOLE path within the pchan->pathsf and pchan->pathef range
288  */
pose_calculate_paths_exec(bContext * C,wmOperator * op)289 static int pose_calculate_paths_exec(bContext *C, wmOperator *op)
290 {
291   Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
292   Scene *scene = CTX_data_scene(C);
293 
294   if (ELEM(NULL, ob, ob->pose)) {
295     return OPERATOR_CANCELLED;
296   }
297 
298   /* grab baking settings from operator settings */
299   {
300     bAnimVizSettings *avs = &ob->pose->avs;
301     PointerRNA avs_ptr;
302 
303     avs->path_sf = RNA_int_get(op->ptr, "start_frame");
304     avs->path_ef = RNA_int_get(op->ptr, "end_frame");
305 
306     RNA_pointer_create(NULL, &RNA_AnimVizMotionPaths, avs, &avs_ptr);
307     RNA_enum_set(&avs_ptr, "bake_location", RNA_enum_get(op->ptr, "bake_location"));
308   }
309 
310   /* set up path data for bones being calculated */
311   CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones_from_active_object) {
312     /* verify makes sure that the selected bone has a bone with the appropriate settings */
313     animviz_verify_motionpaths(op->reports, scene, ob, pchan);
314   }
315   CTX_DATA_END;
316 
317 #ifdef DEBUG_TIME
318   TIMEIT_START(recalc_pose_paths);
319 #endif
320 
321   /* calculate the bones that now have motionpaths... */
322   /* TODO: only make for the selected bones? */
323   ED_pose_recalculate_paths(C, scene, ob, POSE_PATH_CALC_RANGE_FULL);
324 
325 #ifdef DEBUG_TIME
326   TIMEIT_END(recalc_pose_paths);
327 #endif
328 
329   /* notifiers for updates */
330   WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
331 
332   return OPERATOR_FINISHED;
333 }
334 
POSE_OT_paths_calculate(wmOperatorType * ot)335 void POSE_OT_paths_calculate(wmOperatorType *ot)
336 {
337   /* identifiers */
338   ot->name = "Calculate Bone Paths";
339   ot->idname = "POSE_OT_paths_calculate";
340   ot->description = "Calculate paths for the selected bones";
341 
342   /* api callbacks */
343   ot->invoke = pose_calculate_paths_invoke;
344   ot->exec = pose_calculate_paths_exec;
345   ot->poll = ED_operator_posemode_exclusive;
346 
347   /* flags */
348   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
349 
350   /* properties */
351   RNA_def_int(ot->srna,
352               "start_frame",
353               1,
354               MINAFRAME,
355               MAXFRAME,
356               "Start",
357               "First frame to calculate bone paths on",
358               MINFRAME,
359               MAXFRAME / 2.0);
360   RNA_def_int(ot->srna,
361               "end_frame",
362               250,
363               MINAFRAME,
364               MAXFRAME,
365               "End",
366               "Last frame to calculate bone paths on",
367               MINFRAME,
368               MAXFRAME / 2.0);
369 
370   RNA_def_enum(ot->srna,
371                "bake_location",
372                rna_enum_motionpath_bake_location_items,
373                MOTIONPATH_BAKE_HEADS,
374                "Bake Location",
375                "Which point on the bones is used when calculating paths");
376 }
377 
378 /* --------- */
379 
pose_update_paths_poll(bContext * C)380 static bool pose_update_paths_poll(bContext *C)
381 {
382   if (ED_operator_posemode_exclusive(C)) {
383     Object *ob = CTX_data_active_object(C);
384     return (ob->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS) != 0;
385   }
386 
387   return false;
388 }
389 
pose_update_paths_exec(bContext * C,wmOperator * UNUSED (op))390 static int pose_update_paths_exec(bContext *C, wmOperator *UNUSED(op))
391 {
392   Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
393   Scene *scene = CTX_data_scene(C);
394 
395   if (ELEM(NULL, ob, scene)) {
396     return OPERATOR_CANCELLED;
397   }
398 
399   /* calculate the bones that now have motionpaths... */
400   /* TODO: only make for the selected bones? */
401   ED_pose_recalculate_paths(C, scene, ob, POSE_PATH_CALC_RANGE_FULL);
402 
403   /* notifiers for updates */
404   WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
405 
406   return OPERATOR_FINISHED;
407 }
408 
POSE_OT_paths_update(wmOperatorType * ot)409 void POSE_OT_paths_update(wmOperatorType *ot)
410 {
411   /* identifiers */
412   ot->name = "Update Bone Paths";
413   ot->idname = "POSE_OT_paths_update";
414   ot->description = "Recalculate paths for bones that already have them";
415 
416   /* api callbacks */
417   ot->exec = pose_update_paths_exec;
418   ot->poll = pose_update_paths_poll;
419 
420   /* flags */
421   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
422 }
423 
424 /* --------- */
425 
426 /* for the object with pose/action: clear path curves for selected bones only */
ED_pose_clear_paths(Object * ob,bool only_selected)427 static void ED_pose_clear_paths(Object *ob, bool only_selected)
428 {
429   bPoseChannel *pchan;
430   bool skipped = false;
431 
432   if (ELEM(NULL, ob, ob->pose)) {
433     return;
434   }
435 
436   /* free the motionpath blocks for all bones - This is easier for users to quickly clear all */
437   for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
438     if (pchan->mpath) {
439       if ((only_selected == false) || ((pchan->bone) && (pchan->bone->flag & BONE_SELECTED))) {
440         animviz_free_motionpath(pchan->mpath);
441         pchan->mpath = NULL;
442       }
443       else {
444         skipped = true;
445       }
446     }
447   }
448 
449   /* if nothing was skipped, there should be no paths left! */
450   if (skipped == false) {
451     ob->pose->avs.path_bakeflag &= ~MOTIONPATH_BAKE_HAS_PATHS;
452   }
453 
454   /* tag armature object for copy on write - so removed paths don't still show */
455   DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
456 }
457 
458 /* operator callback - wrapper for the backend function  */
pose_clear_paths_exec(bContext * C,wmOperator * op)459 static int pose_clear_paths_exec(bContext *C, wmOperator *op)
460 {
461   Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
462   bool only_selected = RNA_boolean_get(op->ptr, "only_selected");
463 
464   /* only continue if there's an object */
465   if (ELEM(NULL, ob, ob->pose)) {
466     return OPERATOR_CANCELLED;
467   }
468 
469   /* use the backend function for this */
470   ED_pose_clear_paths(ob, only_selected);
471 
472   /* notifiers for updates */
473   WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
474 
475   return OPERATOR_FINISHED;
476 }
477 
478 /* operator callback/wrapper */
pose_clear_paths_invoke(bContext * C,wmOperator * op,const wmEvent * evt)479 static int pose_clear_paths_invoke(bContext *C, wmOperator *op, const wmEvent *evt)
480 {
481   if ((evt->shift) && !RNA_struct_property_is_set(op->ptr, "only_selected")) {
482     RNA_boolean_set(op->ptr, "only_selected", true);
483   }
484   return pose_clear_paths_exec(C, op);
485 }
486 
POSE_OT_paths_clear(wmOperatorType * ot)487 void POSE_OT_paths_clear(wmOperatorType *ot)
488 {
489   /* identifiers */
490   ot->name = "Clear Bone Paths";
491   ot->idname = "POSE_OT_paths_clear";
492   ot->description = "Clear path caches for all bones, hold Shift key for selected bones only";
493 
494   /* api callbacks */
495   ot->invoke = pose_clear_paths_invoke;
496   ot->exec = pose_clear_paths_exec;
497   ot->poll = ED_operator_posemode_exclusive;
498 
499   /* flags */
500   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
501 
502   /* properties */
503   ot->prop = RNA_def_boolean(
504       ot->srna, "only_selected", false, "Only Selected", "Only clear paths from selected bones");
505   RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE);
506 }
507 
508 /* --------- */
509 
pose_update_paths_range_exec(bContext * C,wmOperator * UNUSED (op))510 static int pose_update_paths_range_exec(bContext *C, wmOperator *UNUSED(op))
511 {
512   Scene *scene = CTX_data_scene(C);
513   Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
514 
515   if (ELEM(NULL, scene, ob, ob->pose)) {
516     return OPERATOR_CANCELLED;
517   }
518 
519   /* use Preview Range or Full Frame Range - whichever is in use */
520   ob->pose->avs.path_sf = PSFRA;
521   ob->pose->avs.path_ef = PEFRA;
522 
523   /* tag for updates */
524   DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
525   WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
526 
527   return OPERATOR_FINISHED;
528 }
529 
POSE_OT_paths_range_update(wmOperatorType * ot)530 void POSE_OT_paths_range_update(wmOperatorType *ot)
531 {
532   /* identifiers */
533   ot->name = "Update Range from Scene";
534   ot->idname = "POSE_OT_paths_range_update";
535   ot->description = "Update frame range for motion paths from the Scene's current frame range";
536 
537   /* callbacks */
538   ot->exec = pose_update_paths_range_exec;
539   ot->poll = ED_operator_posemode_exclusive;
540 
541   /* flags */
542   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
543 }
544 
545 /* ********************************************** */
546 
pose_flip_names_exec(bContext * C,wmOperator * op)547 static int pose_flip_names_exec(bContext *C, wmOperator *op)
548 {
549   Main *bmain = CTX_data_main(C);
550   ViewLayer *view_layer = CTX_data_view_layer(C);
551   View3D *v3d = CTX_wm_view3d(C);
552   const bool do_strip_numbers = RNA_boolean_get(op->ptr, "do_strip_numbers");
553 
554   FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) {
555     bArmature *arm = ob->data;
556     ListBase bones_names = {NULL};
557 
558     FOREACH_PCHAN_SELECTED_IN_OBJECT_BEGIN (ob, pchan) {
559       BLI_addtail(&bones_names, BLI_genericNodeN(pchan->name));
560     }
561     FOREACH_PCHAN_SELECTED_IN_OBJECT_END;
562 
563     ED_armature_bones_flip_names(bmain, arm, &bones_names, do_strip_numbers);
564 
565     BLI_freelistN(&bones_names);
566 
567     /* since we renamed stuff... */
568     DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
569 
570     /* note, notifier might evolve */
571     WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
572   }
573   FOREACH_OBJECT_IN_MODE_END;
574 
575   return OPERATOR_FINISHED;
576 }
577 
POSE_OT_flip_names(wmOperatorType * ot)578 void POSE_OT_flip_names(wmOperatorType *ot)
579 {
580   /* identifiers */
581   ot->name = "Flip Names";
582   ot->idname = "POSE_OT_flip_names";
583   ot->description = "Flips (and corrects) the axis suffixes of the names of selected bones";
584 
585   /* api callbacks */
586   ot->exec = pose_flip_names_exec;
587   ot->poll = ED_operator_posemode_local;
588 
589   /* flags */
590   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
591 
592   RNA_def_boolean(ot->srna,
593                   "do_strip_numbers",
594                   false,
595                   "Strip Numbers",
596                   "Try to remove right-most dot-number from flipped names "
597                   "(WARNING: may result in incoherent naming in some cases)");
598 }
599 
600 /* ------------------ */
601 
pose_autoside_names_exec(bContext * C,wmOperator * op)602 static int pose_autoside_names_exec(bContext *C, wmOperator *op)
603 {
604   Main *bmain = CTX_data_main(C);
605   char newname[MAXBONENAME];
606   short axis = RNA_enum_get(op->ptr, "axis");
607   Object *ob_prev = NULL;
608 
609   /* loop through selected bones, auto-naming them */
610   CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, selected_pose_bones, Object *, ob) {
611     bArmature *arm = ob->data;
612     BLI_strncpy(newname, pchan->name, sizeof(newname));
613     if (bone_autoside_name(newname, 1, axis, pchan->bone->head[axis], pchan->bone->tail[axis])) {
614       ED_armature_bone_rename(bmain, arm, pchan->name, newname);
615     }
616 
617     if (ob_prev != ob) {
618       /* since we renamed stuff... */
619       DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
620 
621       /* note, notifier might evolve */
622       WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
623       ob_prev = ob;
624     }
625   }
626   CTX_DATA_END;
627 
628   return OPERATOR_FINISHED;
629 }
630 
POSE_OT_autoside_names(wmOperatorType * ot)631 void POSE_OT_autoside_names(wmOperatorType *ot)
632 {
633   static const EnumPropertyItem axis_items[] = {
634       {0, "XAXIS", 0, "X-Axis", "Left/Right"},
635       {1, "YAXIS", 0, "Y-Axis", "Front/Back"},
636       {2, "ZAXIS", 0, "Z-Axis", "Top/Bottom"},
637       {0, NULL, 0, NULL, NULL},
638   };
639 
640   /* identifiers */
641   ot->name = "AutoName by Axis";
642   ot->idname = "POSE_OT_autoside_names";
643   ot->description =
644       "Automatically renames the selected bones according to which side of the target axis they "
645       "fall on";
646 
647   /* api callbacks */
648   ot->invoke = WM_menu_invoke;
649   ot->exec = pose_autoside_names_exec;
650   ot->poll = ED_operator_posemode;
651 
652   /* flags */
653   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
654 
655   /* settings */
656   ot->prop = RNA_def_enum(ot->srna, "axis", axis_items, 0, "Axis", "Axis tag names with");
657 }
658 
659 /* ********************************************** */
660 
pose_bone_rotmode_exec(bContext * C,wmOperator * op)661 static int pose_bone_rotmode_exec(bContext *C, wmOperator *op)
662 {
663   const int mode = RNA_enum_get(op->ptr, "type");
664   Object *prev_ob = NULL;
665 
666   /* set rotation mode of selected bones  */
667   CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, selected_pose_bones, Object *, ob) {
668     /* use API Method for conversions... */
669     BKE_rotMode_change_values(
670         pchan->quat, pchan->eul, pchan->rotAxis, &pchan->rotAngle, pchan->rotmode, (short)mode);
671 
672     /* finally, set the new rotation type */
673     pchan->rotmode = mode;
674 
675     if (prev_ob != ob) {
676       /* Notifiers and updates. */
677       DEG_id_tag_update((ID *)ob, ID_RECALC_GEOMETRY);
678       WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob);
679       WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob);
680       prev_ob = ob;
681     }
682   }
683   CTX_DATA_END;
684 
685   return OPERATOR_FINISHED;
686 }
687 
POSE_OT_rotation_mode_set(wmOperatorType * ot)688 void POSE_OT_rotation_mode_set(wmOperatorType *ot)
689 {
690   /* identifiers */
691   ot->name = "Set Rotation Mode";
692   ot->idname = "POSE_OT_rotation_mode_set";
693   ot->description = "Set the rotation representation used by selected bones";
694 
695   /* callbacks */
696   ot->invoke = WM_menu_invoke;
697   ot->exec = pose_bone_rotmode_exec;
698   ot->poll = ED_operator_posemode;
699 
700   /* flags */
701   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
702 
703   /* properties */
704   ot->prop = RNA_def_enum(
705       ot->srna, "type", rna_enum_object_rotation_mode_items, 0, "Rotation Mode", "");
706 }
707 
708 /* ********************************************** */
709 
armature_layers_poll(bContext * C)710 static bool armature_layers_poll(bContext *C)
711 {
712   /* Armature layers operators can be used in posemode OR editmode for armatures */
713   return ED_operator_posemode(C) || ED_operator_editarmature(C);
714 }
715 
armature_layers_get_data(Object ** ob)716 static bArmature *armature_layers_get_data(Object **ob)
717 {
718   bArmature *arm = NULL;
719 
720   /* Sanity checking and handling of posemode. */
721   if (*ob) {
722     Object *tob = BKE_object_pose_armature_get(*ob);
723     if (tob) {
724       *ob = tob;
725       arm = (*ob)->data;
726     }
727     else if ((*ob)->type == OB_ARMATURE) {
728       arm = (*ob)->data;
729     }
730   }
731 
732   return arm;
733 }
734 
735 /* Show all armature layers */
736 
pose_armature_layers_showall_exec(bContext * C,wmOperator * op)737 static int pose_armature_layers_showall_exec(bContext *C, wmOperator *op)
738 {
739   Object *ob = CTX_data_active_object(C);
740   bArmature *arm = armature_layers_get_data(&ob);
741   PointerRNA ptr;
742   int maxLayers = (RNA_boolean_get(op->ptr, "all")) ? 32 : 16;
743   /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */
744   bool layers[32] = {false};
745 
746   /* sanity checking */
747   if (arm == NULL) {
748     return OPERATOR_CANCELLED;
749   }
750 
751   /* use RNA to set the layers
752    * although it would be faster to just set directly using bitflags, we still
753    * need to setup a RNA pointer so that we get the "update" callbacks for free...
754    */
755   RNA_id_pointer_create(&arm->id, &ptr);
756 
757   for (int i = 0; i < maxLayers; i++) {
758     layers[i] = 1;
759   }
760 
761   RNA_boolean_set_array(&ptr, "layers", layers);
762 
763   /* note, notifier might evolve */
764   WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
765   DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE);
766 
767   /* done */
768   return OPERATOR_FINISHED;
769 }
770 
ARMATURE_OT_layers_show_all(wmOperatorType * ot)771 void ARMATURE_OT_layers_show_all(wmOperatorType *ot)
772 {
773   /* identifiers */
774   ot->name = "Show All Layers";
775   ot->idname = "ARMATURE_OT_layers_show_all";
776   ot->description = "Make all armature layers visible";
777 
778   /* callbacks */
779   ot->exec = pose_armature_layers_showall_exec;
780   ot->poll = armature_layers_poll;
781 
782   /* flags */
783   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
784 
785   /* properties */
786   ot->prop = RNA_def_boolean(
787       ot->srna, "all", 1, "All Layers", "Enable all layers or just the first 16 (top row)");
788 }
789 
790 /* ------------------- */
791 
792 /* Present a popup to get the layers that should be used */
armature_layers_invoke(bContext * C,wmOperator * op,const wmEvent * event)793 static int armature_layers_invoke(bContext *C, wmOperator *op, const wmEvent *event)
794 {
795   Object *ob = CTX_data_active_object(C);
796   bArmature *arm = armature_layers_get_data(&ob);
797   PointerRNA ptr;
798   /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */
799   bool layers[32];
800 
801   /* sanity checking */
802   if (arm == NULL) {
803     return OPERATOR_CANCELLED;
804   }
805 
806   /* Get RNA pointer to armature data to use that to retrieve the layers as ints
807    * to init the operator. */
808   RNA_id_pointer_create((ID *)arm, &ptr);
809   RNA_boolean_get_array(&ptr, "layers", layers);
810   RNA_boolean_set_array(op->ptr, "layers", layers);
811 
812   /* part to sync with other similar operators... */
813   return WM_operator_props_popup(C, op, event);
814 }
815 
816 /* Set the visible layers for the active armature (edit and pose modes) */
armature_layers_exec(bContext * C,wmOperator * op)817 static int armature_layers_exec(bContext *C, wmOperator *op)
818 {
819   Object *ob = CTX_data_active_object(C);
820   bArmature *arm = armature_layers_get_data(&ob);
821   PointerRNA ptr;
822   /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */
823   bool layers[32];
824 
825   if (arm == NULL) {
826     return OPERATOR_CANCELLED;
827   }
828 
829   /* get the values set in the operator properties */
830   RNA_boolean_get_array(op->ptr, "layers", layers);
831 
832   /* get pointer for armature, and write data there... */
833   RNA_id_pointer_create((ID *)arm, &ptr);
834   RNA_boolean_set_array(&ptr, "layers", layers);
835 
836   /* note, notifier might evolve */
837   WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
838   DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE);
839 
840   return OPERATOR_FINISHED;
841 }
842 
ARMATURE_OT_armature_layers(wmOperatorType * ot)843 void ARMATURE_OT_armature_layers(wmOperatorType *ot)
844 {
845   /* identifiers */
846   ot->name = "Change Armature Layers";
847   ot->idname = "ARMATURE_OT_armature_layers";
848   ot->description = "Change the visible armature layers";
849 
850   /* callbacks */
851   ot->invoke = armature_layers_invoke;
852   ot->exec = armature_layers_exec;
853   ot->poll = armature_layers_poll;
854 
855   /* flags */
856   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
857 
858   /* properties */
859   RNA_def_boolean_layer_member(
860       ot->srna, "layers", 32, NULL, "Layer", "Armature layers to make visible");
861 }
862 
863 /* ------------------- */
864 
865 /* Present a popup to get the layers that should be used */
pose_bone_layers_invoke(bContext * C,wmOperator * op,const wmEvent * event)866 static int pose_bone_layers_invoke(bContext *C, wmOperator *op, const wmEvent *event)
867 {
868   /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */
869   bool layers[32] = {0};
870 
871   /* get layers that are active already */
872   CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones) {
873     short bit;
874 
875     /* loop over the bits for this pchan's layers, adding layers where they're needed */
876     for (bit = 0; bit < 32; bit++) {
877       layers[bit] = (pchan->bone->layer & (1u << bit)) != 0;
878     }
879   }
880   CTX_DATA_END;
881 
882   /* copy layers to operator */
883   RNA_boolean_set_array(op->ptr, "layers", layers);
884 
885   /* part to sync with other similar operators... */
886   return WM_operator_props_popup(C, op, event);
887 }
888 
889 /* Set the visible layers for the active armature (edit and pose modes) */
pose_bone_layers_exec(bContext * C,wmOperator * op)890 static int pose_bone_layers_exec(bContext *C, wmOperator *op)
891 {
892   PointerRNA ptr;
893   /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */
894   bool layers[32];
895 
896   /* get the values set in the operator properties */
897   RNA_boolean_get_array(op->ptr, "layers", layers);
898 
899   Object *prev_ob = NULL;
900 
901   /* Make sure that the pose bone data is up to date.
902    * (May not always be the case after undo/redo e.g.).
903    */
904   struct Main *bmain = CTX_data_main(C);
905   wmWindow *win = CTX_wm_window(C);
906   View3D *v3d = CTX_wm_view3d(C); /* This may be NULL in a lot of cases. */
907   ViewLayer *view_layer = WM_window_get_active_view_layer(win);
908 
909   FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob_iter) {
910     bArmature *arm = ob_iter->data;
911     BKE_pose_ensure(bmain, ob_iter, arm, true);
912   }
913   FOREACH_OBJECT_IN_MODE_END;
914 
915   /* set layers of pchans based on the values set in the operator props */
916   CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, selected_pose_bones, Object *, ob) {
917     /* get pointer for pchan, and write flags this way */
918     RNA_pointer_create((ID *)ob->data, &RNA_Bone, pchan->bone, &ptr);
919     RNA_boolean_set_array(&ptr, "layers", layers);
920 
921     if (prev_ob != ob) {
922       /* Note, notifier might evolve. */
923       WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
924       DEG_id_tag_update((ID *)ob->data, ID_RECALC_COPY_ON_WRITE);
925       prev_ob = ob;
926     }
927   }
928   CTX_DATA_END;
929   return OPERATOR_FINISHED;
930 }
931 
POSE_OT_bone_layers(wmOperatorType * ot)932 void POSE_OT_bone_layers(wmOperatorType *ot)
933 {
934   /* identifiers */
935   ot->name = "Change Bone Layers";
936   ot->idname = "POSE_OT_bone_layers";
937   ot->description = "Change the layers that the selected bones belong to";
938 
939   /* callbacks */
940   ot->invoke = pose_bone_layers_invoke;
941   ot->exec = pose_bone_layers_exec;
942   ot->poll = ED_operator_posemode_exclusive;
943 
944   /* flags */
945   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
946 
947   /* properties */
948   RNA_def_boolean_layer_member(
949       ot->srna, "layers", 32, NULL, "Layer", "Armature layers that bone belongs to");
950 }
951 
952 /* ------------------- */
953 
954 /* Present a popup to get the layers that should be used */
armature_bone_layers_invoke(bContext * C,wmOperator * op,const wmEvent * event)955 static int armature_bone_layers_invoke(bContext *C, wmOperator *op, const wmEvent *event)
956 {
957   /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */
958   bool layers[32] = {0};
959 
960   /* get layers that are active already */
961   CTX_DATA_BEGIN (C, EditBone *, ebone, selected_editable_bones) {
962     short bit;
963 
964     /* loop over the bits for this pchan's layers, adding layers where they're needed */
965     for (bit = 0; bit < 32; bit++) {
966       if (ebone->layer & (1u << bit)) {
967         layers[bit] = 1;
968       }
969     }
970   }
971   CTX_DATA_END;
972 
973   /* copy layers to operator */
974   RNA_boolean_set_array(op->ptr, "layers", layers);
975 
976   /* part to sync with other similar operators... */
977   return WM_operator_props_popup(C, op, event);
978 }
979 
980 /* Set the visible layers for the active armature (edit and pose modes) */
armature_bone_layers_exec(bContext * C,wmOperator * op)981 static int armature_bone_layers_exec(bContext *C, wmOperator *op)
982 {
983   Object *ob = CTX_data_edit_object(C);
984   PointerRNA ptr;
985   /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */
986   bool layers[32];
987 
988   /* get the values set in the operator properties */
989   RNA_boolean_get_array(op->ptr, "layers", layers);
990 
991   /* set layers of pchans based on the values set in the operator props */
992   CTX_DATA_BEGIN_WITH_ID (C, EditBone *, ebone, selected_editable_bones, bArmature *, arm) {
993     /* get pointer for pchan, and write flags this way */
994     RNA_pointer_create((ID *)arm, &RNA_EditBone, ebone, &ptr);
995     RNA_boolean_set_array(&ptr, "layers", layers);
996   }
997   CTX_DATA_END;
998 
999   ED_armature_edit_refresh_layer_used(ob->data);
1000 
1001   /* note, notifier might evolve */
1002   WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
1003 
1004   return OPERATOR_FINISHED;
1005 }
1006 
ARMATURE_OT_bone_layers(wmOperatorType * ot)1007 void ARMATURE_OT_bone_layers(wmOperatorType *ot)
1008 {
1009   /* identifiers */
1010   ot->name = "Change Bone Layers";
1011   ot->idname = "ARMATURE_OT_bone_layers";
1012   ot->description = "Change the layers that the selected bones belong to";
1013 
1014   /* callbacks */
1015   ot->invoke = armature_bone_layers_invoke;
1016   ot->exec = armature_bone_layers_exec;
1017   ot->poll = ED_operator_editarmature;
1018 
1019   /* flags */
1020   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1021 
1022   /* properties */
1023   RNA_def_boolean_layer_member(
1024       ot->srna, "layers", 32, NULL, "Layer", "Armature layers that bone belongs to");
1025 }
1026 
1027 /* ********************************************** */
1028 /* Show/Hide Bones */
1029 
hide_pose_bone_fn(Object * ob,Bone * bone,void * ptr)1030 static int hide_pose_bone_fn(Object *ob, Bone *bone, void *ptr)
1031 {
1032   bArmature *arm = ob->data;
1033   const bool hide_select = (bool)POINTER_AS_INT(ptr);
1034   int count = 0;
1035   if (arm->layer & bone->layer) {
1036     if (((bone->flag & BONE_SELECTED) != 0) == hide_select) {
1037       bone->flag |= BONE_HIDDEN_P;
1038       /* only needed when 'hide_select' is true, but harmless. */
1039       bone->flag &= ~BONE_SELECTED;
1040       if (arm->act_bone == bone) {
1041         arm->act_bone = NULL;
1042       }
1043       count += 1;
1044     }
1045   }
1046   return count;
1047 }
1048 
1049 /* active object is armature in posemode, poll checked */
pose_hide_exec(bContext * C,wmOperator * op)1050 static int pose_hide_exec(bContext *C, wmOperator *op)
1051 {
1052   ViewLayer *view_layer = CTX_data_view_layer(C);
1053   uint objects_len;
1054   Object **objects = BKE_object_pose_array_get_unique(view_layer, CTX_wm_view3d(C), &objects_len);
1055   bool changed_multi = false;
1056 
1057   const int hide_select = !RNA_boolean_get(op->ptr, "unselected");
1058   void *hide_select_p = POINTER_FROM_INT(hide_select);
1059 
1060   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
1061     Object *ob_iter = objects[ob_index];
1062     bArmature *arm = ob_iter->data;
1063 
1064     if (ob_iter->proxy != NULL) {
1065       BKE_report(op->reports, RPT_INFO, "Undo of hiding can only be done with Reveal Selected");
1066     }
1067 
1068     bool changed = bone_looper(ob_iter, arm->bonebase.first, hide_select_p, hide_pose_bone_fn) !=
1069                    0;
1070     if (changed) {
1071       changed_multi = true;
1072       WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob_iter);
1073       DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE);
1074     }
1075   }
1076   MEM_freeN(objects);
1077 
1078   return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
1079 }
1080 
POSE_OT_hide(wmOperatorType * ot)1081 void POSE_OT_hide(wmOperatorType *ot)
1082 {
1083   /* identifiers */
1084   ot->name = "Hide Selected";
1085   ot->idname = "POSE_OT_hide";
1086   ot->description = "Tag selected bones to not be visible in Pose Mode";
1087 
1088   /* api callbacks */
1089   ot->exec = pose_hide_exec;
1090   ot->poll = ED_operator_posemode;
1091 
1092   /* flags */
1093   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1094 
1095   /* props */
1096   RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "");
1097 }
1098 
show_pose_bone_cb(Object * ob,Bone * bone,void * data)1099 static int show_pose_bone_cb(Object *ob, Bone *bone, void *data)
1100 {
1101   const bool select = POINTER_AS_INT(data);
1102 
1103   bArmature *arm = ob->data;
1104   int count = 0;
1105   if (arm->layer & bone->layer) {
1106     if (bone->flag & BONE_HIDDEN_P) {
1107       if (!(bone->flag & BONE_UNSELECTABLE)) {
1108         SET_FLAG_FROM_TEST(bone->flag, select, BONE_SELECTED);
1109       }
1110       bone->flag &= ~BONE_HIDDEN_P;
1111       count += 1;
1112     }
1113   }
1114 
1115   return count;
1116 }
1117 
1118 /* active object is armature in posemode, poll checked */
pose_reveal_exec(bContext * C,wmOperator * op)1119 static int pose_reveal_exec(bContext *C, wmOperator *op)
1120 {
1121   ViewLayer *view_layer = CTX_data_view_layer(C);
1122   uint objects_len;
1123   Object **objects = BKE_object_pose_array_get_unique(view_layer, CTX_wm_view3d(C), &objects_len);
1124   bool changed_multi = false;
1125   const bool select = RNA_boolean_get(op->ptr, "select");
1126   void *select_p = POINTER_FROM_INT(select);
1127 
1128   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
1129     Object *ob_iter = objects[ob_index];
1130     bArmature *arm = ob_iter->data;
1131 
1132     bool changed = bone_looper(ob_iter, arm->bonebase.first, select_p, show_pose_bone_cb);
1133     if (changed) {
1134       changed_multi = true;
1135       WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob_iter);
1136       DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE);
1137     }
1138   }
1139   MEM_freeN(objects);
1140 
1141   return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
1142 }
1143 
POSE_OT_reveal(wmOperatorType * ot)1144 void POSE_OT_reveal(wmOperatorType *ot)
1145 {
1146   /* identifiers */
1147   ot->name = "Reveal Selected";
1148   ot->idname = "POSE_OT_reveal";
1149   ot->description = "Reveal all bones hidden in Pose Mode";
1150 
1151   /* api callbacks */
1152   ot->exec = pose_reveal_exec;
1153   ot->poll = ED_operator_posemode;
1154 
1155   /* flags */
1156   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1157 
1158   RNA_def_boolean(ot->srna, "select", true, "Select", "");
1159 }
1160 
1161 /* ********************************************** */
1162 /* Flip Quats */
1163 
pose_flip_quats_exec(bContext * C,wmOperator * UNUSED (op))1164 static int pose_flip_quats_exec(bContext *C, wmOperator *UNUSED(op))
1165 {
1166   Scene *scene = CTX_data_scene(C);
1167   KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_LOC_ROT_SCALE_ID);
1168 
1169   bool changed_multi = false;
1170 
1171   ViewLayer *view_layer = CTX_data_view_layer(C);
1172   View3D *v3d = CTX_wm_view3d(C);
1173   FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob_iter) {
1174     bool changed = false;
1175     /* loop through all selected pchans, flipping and keying (as needed) */
1176     FOREACH_PCHAN_SELECTED_IN_OBJECT_BEGIN (ob_iter, pchan) {
1177       /* only if bone is using quaternion rotation */
1178       if (pchan->rotmode == ROT_MODE_QUAT) {
1179         changed = true;
1180         /* quaternions have 720 degree range */
1181         negate_v4(pchan->quat);
1182 
1183         ED_autokeyframe_pchan(C, scene, ob_iter, pchan, ks);
1184       }
1185     }
1186     FOREACH_PCHAN_SELECTED_IN_OBJECT_END;
1187 
1188     if (changed) {
1189       changed_multi = true;
1190       /* notifiers and updates */
1191       DEG_id_tag_update(&ob_iter->id, ID_RECALC_GEOMETRY);
1192       WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob_iter);
1193     }
1194   }
1195   FOREACH_OBJECT_IN_MODE_END;
1196 
1197   return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
1198 }
1199 
POSE_OT_quaternions_flip(wmOperatorType * ot)1200 void POSE_OT_quaternions_flip(wmOperatorType *ot)
1201 {
1202   /* identifiers */
1203   ot->name = "Flip Quats";
1204   ot->idname = "POSE_OT_quaternions_flip";
1205   ot->description =
1206       "Flip quaternion values to achieve desired rotations, while maintaining the same "
1207       "orientations";
1208 
1209   /* callbacks */
1210   ot->exec = pose_flip_quats_exec;
1211   ot->poll = ED_operator_posemode;
1212 
1213   /* flags */
1214   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1215 }
1216