CMake: add WITH_LINKER_LLD option for unix platforms
[blender-staging.git] / source / blender / editors / armature / pose_group.c
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) 2008 Blender Foundation
17  * All rights reserved.
18  * Implementation of Bone Groups operators and editing API's
19  */
20
21 /** \file
22  * \ingroup edarmature
23  */
24
25 #include <string.h>
26
27 #include "MEM_guardedalloc.h"
28
29 #include "BLI_blenlib.h"
30
31 #include "DNA_armature_types.h"
32 #include "DNA_object_types.h"
33
34 #include "BKE_action.h"
35 #include "BKE_armature.h"
36 #include "BKE_context.h"
37
38 #include "DEG_depsgraph.h"
39
40 #include "RNA_access.h"
41 #include "RNA_define.h"
42
43 #include "WM_api.h"
44 #include "WM_types.h"
45
46 #include "ED_armature.h"
47 #include "ED_screen.h"
48
49 #include "UI_interface.h"
50 #include "UI_resources.h"
51
52 #include "armature_intern.h"
53
54 /* ********************************************** */
55 /* Bone Groups */
56
57 static int pose_group_add_exec(bContext *C, wmOperator *UNUSED(op))
58 {
59   Object *ob = ED_pose_object_from_context(C);
60
61   /* only continue if there's an object and pose */
62   if (ELEM(NULL, ob, ob->pose)) {
63     return OPERATOR_CANCELLED;
64   }
65
66   /* for now, just call the API function for this */
67   BKE_pose_add_group(ob->pose, NULL);
68
69   /* notifiers for updates */
70   WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
71
72   return OPERATOR_FINISHED;
73 }
74
75 void POSE_OT_group_add(wmOperatorType *ot)
76 {
77   /* identifiers */
78   ot->name = "Add Bone Group";
79   ot->idname = "POSE_OT_group_add";
80   ot->description = "Add a new bone group";
81
82   /* api callbacks */
83   ot->exec = pose_group_add_exec;
84   ot->poll = ED_operator_posemode_context;
85
86   /* flags */
87   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
88 }
89
90 static int pose_group_remove_exec(bContext *C, wmOperator *UNUSED(op))
91 {
92   Object *ob = ED_pose_object_from_context(C);
93
94   /* only continue if there's an object and pose */
95   if (ELEM(NULL, ob, ob->pose)) {
96     return OPERATOR_CANCELLED;
97   }
98
99   /* for now, just call the API function for this */
100   BKE_pose_remove_group_index(ob->pose, ob->pose->active_group);
101
102   /* notifiers for updates */
103   WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
104   DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
105
106   return OPERATOR_FINISHED;
107 }
108
109 void POSE_OT_group_remove(wmOperatorType *ot)
110 {
111   /* identifiers */
112   ot->name = "Remove Bone Group";
113   ot->idname = "POSE_OT_group_remove";
114   ot->description = "Remove the active bone group";
115
116   /* api callbacks */
117   ot->exec = pose_group_remove_exec;
118   ot->poll = ED_operator_posemode_context;
119
120   /* flags */
121   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
122 }
123
124 /* ------------ */
125
126 /* invoke callback which presents a list of bone-groups for the user to choose from */
127 static int pose_groups_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
128 {
129   Object *ob = ED_pose_object_from_context(C);
130   bPose *pose;
131   PropertyRNA *prop = RNA_struct_find_property(op->ptr, "type");
132
133   uiPopupMenu *pup;
134   uiLayout *layout;
135   bActionGroup *grp;
136   int i;
137
138   /* only continue if there's an object, and a pose there too */
139   if (ELEM(NULL, ob, ob->pose)) {
140     return OPERATOR_CANCELLED;
141   }
142   pose = ob->pose;
143
144   /* If group index is set, try to use it! */
145   if (RNA_property_is_set(op->ptr, prop)) {
146     const int num_groups = BLI_listbase_count(&pose->agroups);
147     const int group = RNA_property_int_get(op->ptr, prop);
148
149     /* just use the active group index, and call the exec callback for the calling operator */
150     if (group > 0 && group <= num_groups) {
151       return op->type->exec(C, op);
152     }
153   }
154
155   /* if there's no active group (or active is invalid), create a new menu to find it */
156   if (pose->active_group <= 0) {
157     /* create a new menu, and start populating it with group names */
158     pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE);
159     layout = UI_popup_menu_layout(pup);
160
161     /* special entry - allow to create new group, then use that
162      * (not to be used for removing though)
163      */
164     if (strstr(op->idname, "assign")) {
165       uiItemIntO(layout, "New Group", ICON_NONE, op->idname, "type", 0);
166       uiItemS(layout);
167     }
168
169     /* add entries for each group */
170     for (grp = pose->agroups.first, i = 1; grp; grp = grp->next, i++) {
171       uiItemIntO(layout, grp->name, ICON_NONE, op->idname, "type", i);
172     }
173
174     /* finish building the menu, and process it (should result in calling self again) */
175     UI_popup_menu_end(C, pup);
176
177     return OPERATOR_INTERFACE;
178   }
179   else {
180     /* just use the active group index, and call the exec callback for the calling operator */
181     RNA_int_set(op->ptr, "type", pose->active_group);
182     return op->type->exec(C, op);
183   }
184 }
185
186 /* Assign selected pchans to the bone group that the user selects */
187 static int pose_group_assign_exec(bContext *C, wmOperator *op)
188 {
189   Object *ob = ED_pose_object_from_context(C);
190   bPose *pose;
191   bool done = false;
192
193   /* only continue if there's an object, and a pose there too */
194   if (ELEM(NULL, ob, ob->pose)) {
195     return OPERATOR_CANCELLED;
196   }
197
198   pose = ob->pose;
199
200   /* set the active group number to the one from operator props
201    * - if 0 after this, make a new group...
202    */
203   pose->active_group = RNA_int_get(op->ptr, "type");
204   if (pose->active_group == 0) {
205     BKE_pose_add_group(ob->pose, NULL);
206   }
207
208   /* add selected bones to group then */
209   FOREACH_PCHAN_SELECTED_IN_OBJECT_BEGIN (ob, pchan) {
210     pchan->agrp_index = pose->active_group;
211     done = true;
212   }
213   FOREACH_PCHAN_SELECTED_IN_OBJECT_END;
214
215   /* notifiers for updates */
216   WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
217   DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
218
219   /* report done status */
220   if (done) {
221     return OPERATOR_FINISHED;
222   }
223   else {
224     return OPERATOR_CANCELLED;
225   }
226 }
227
228 void POSE_OT_group_assign(wmOperatorType *ot)
229 {
230   /* identifiers */
231   ot->name = "Add Selected to Bone Group";
232   ot->idname = "POSE_OT_group_assign";
233   ot->description = "Add selected bones to the chosen bone group";
234
235   /* api callbacks */
236   ot->invoke = pose_groups_menu_invoke;
237   ot->exec = pose_group_assign_exec;
238   ot->poll = ED_operator_posemode_context;
239
240   /* flags */
241   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
242
243   /* properties */
244   RNA_def_int(ot->srna, "type", 0, 0, INT_MAX, "Bone Group Index", "", 0, 10);
245 }
246
247 static int pose_group_unassign_exec(bContext *C, wmOperator *UNUSED(op))
248 {
249   Object *ob = ED_pose_object_from_context(C);
250   bool done = false;
251
252   /* only continue if there's an object, and a pose there too */
253   if (ELEM(NULL, ob, ob->pose)) {
254     return OPERATOR_CANCELLED;
255   }
256
257   /* find selected bones to remove from all bone groups */
258   FOREACH_PCHAN_SELECTED_IN_OBJECT_BEGIN (ob, pchan) {
259     if (pchan->agrp_index) {
260       pchan->agrp_index = 0;
261       done = true;
262     }
263   }
264   FOREACH_PCHAN_SELECTED_IN_OBJECT_END;
265
266   /* notifiers for updates */
267   WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
268   DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
269
270   /* report done status */
271   if (done) {
272     return OPERATOR_FINISHED;
273   }
274   else {
275     return OPERATOR_CANCELLED;
276   }
277 }
278
279 void POSE_OT_group_unassign(wmOperatorType *ot)
280 {
281   /* identifiers */
282   ot->name = "Remove Selected from Bone Groups";
283   ot->idname = "POSE_OT_group_unassign";
284   ot->description = "Remove selected bones from all bone groups";
285
286   /* api callbacks */
287   ot->exec = pose_group_unassign_exec;
288   ot->poll = ED_operator_posemode_context;
289
290   /* flags */
291   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
292 }
293
294 static int group_move_exec(bContext *C, wmOperator *op)
295 {
296   Object *ob = ED_pose_object_from_context(C);
297   bPose *pose = (ob) ? ob->pose : NULL;
298   bPoseChannel *pchan;
299   bActionGroup *grp;
300   int dir = RNA_enum_get(op->ptr, "direction");
301
302   if (ELEM(NULL, ob, pose)) {
303     return OPERATOR_CANCELLED;
304   }
305   if (pose->active_group <= 0) {
306     return OPERATOR_CANCELLED;
307   }
308
309   /* get group to move */
310   grp = BLI_findlink(&pose->agroups, pose->active_group - 1);
311   if (grp == NULL) {
312     return OPERATOR_CANCELLED;
313   }
314
315   /* move bone group */
316   if (BLI_listbase_link_move(&pose->agroups, grp, dir)) {
317     int grpIndexA = pose->active_group;
318     int grpIndexB = grpIndexA + dir;
319
320     pose->active_group += dir;
321     /* fix changed bone group indices in bones (swap grpIndexA with grpIndexB) */
322     for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
323       if (pchan->agrp_index == grpIndexB) {
324         pchan->agrp_index = grpIndexA;
325       }
326       else if (pchan->agrp_index == grpIndexA) {
327         pchan->agrp_index = grpIndexB;
328       }
329     }
330
331     /* notifiers for updates */
332     WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
333   }
334
335   return OPERATOR_FINISHED;
336 }
337
338 void POSE_OT_group_move(wmOperatorType *ot)
339 {
340   static const EnumPropertyItem group_slot_move[] = {
341       {-1, "UP", 0, "Up", ""},
342       {1, "DOWN", 0, "Down", ""},
343       {0, NULL, 0, NULL, NULL},
344   };
345
346   /* identifiers */
347   ot->name = "Move Bone Group";
348   ot->idname = "POSE_OT_group_move";
349   ot->description = "Change position of active Bone Group in list of Bone Groups";
350
351   /* api callbacks */
352   ot->exec = group_move_exec;
353   ot->poll = ED_operator_posemode_context;
354
355   /* flags */
356   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
357
358   RNA_def_enum(ot->srna,
359                "direction",
360                group_slot_move,
361                0,
362                "Direction",
363                "Direction to move the active Bone Group towards");
364 }
365
366 /* bone group sort element */
367 typedef struct tSortActionGroup {
368   bActionGroup *agrp;
369   int index;
370 } tSortActionGroup;
371
372 /* compare bone groups by name */
373 static int compare_agroup(const void *sgrp_a_ptr, const void *sgrp_b_ptr)
374 {
375   const tSortActionGroup *sgrp_a = sgrp_a_ptr;
376   const tSortActionGroup *sgrp_b = sgrp_b_ptr;
377
378   return strcmp(sgrp_a->agrp->name, sgrp_b->agrp->name);
379 }
380
381 static int group_sort_exec(bContext *C, wmOperator *UNUSED(op))
382 {
383   Object *ob = ED_pose_object_from_context(C);
384   bPose *pose = (ob) ? ob->pose : NULL;
385   bPoseChannel *pchan;
386   tSortActionGroup *agrp_array;
387   bActionGroup *agrp;
388   int agrp_count;
389   int i;
390
391   if (ELEM(NULL, ob, pose)) {
392     return OPERATOR_CANCELLED;
393   }
394   if (pose->active_group <= 0) {
395     return OPERATOR_CANCELLED;
396   }
397
398   /* create temporary array with bone groups and indices */
399   agrp_count = BLI_listbase_count(&pose->agroups);
400   agrp_array = MEM_mallocN(sizeof(tSortActionGroup) * agrp_count, "sort bone groups");
401   for (agrp = pose->agroups.first, i = 0; agrp; agrp = agrp->next, i++) {
402     BLI_assert(i < agrp_count);
403     agrp_array[i].agrp = agrp;
404     agrp_array[i].index = i + 1;
405   }
406
407   /* sort bone groups by name */
408   qsort(agrp_array, agrp_count, sizeof(tSortActionGroup), compare_agroup);
409
410   /* create sorted bone group list from sorted array */
411   BLI_listbase_clear(&pose->agroups);
412   for (i = 0; i < agrp_count; i++) {
413     BLI_addtail(&pose->agroups, agrp_array[i].agrp);
414   }
415
416   /* fix changed bone group indizes in bones */
417   for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
418     for (i = 0; i < agrp_count; i++) {
419       if (pchan->agrp_index == agrp_array[i].index) {
420         pchan->agrp_index = i + 1;
421         break;
422       }
423     }
424   }
425
426   /* free temp resources */
427   MEM_freeN(agrp_array);
428
429   /* notifiers for updates */
430   WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
431   DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
432
433   return OPERATOR_FINISHED;
434 }
435
436 void POSE_OT_group_sort(wmOperatorType *ot)
437 {
438   /* identifiers */
439   ot->name = "Sort Bone Groups";
440   ot->idname = "POSE_OT_group_sort";
441   ot->description = "Sort Bone Groups by their names in ascending order";
442
443   /* api callbacks */
444   ot->exec = group_sort_exec;
445   ot->poll = ED_operator_posemode_context;
446
447   /* flags */
448   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
449 }
450
451 static void pose_group_select(Object *ob, bool select)
452 {
453   bPose *pose = ob->pose;
454
455   FOREACH_PCHAN_VISIBLE_IN_OBJECT_BEGIN (ob, pchan) {
456     if ((pchan->bone->flag & BONE_UNSELECTABLE) == 0) {
457       if (select) {
458         if (pchan->agrp_index == pose->active_group) {
459           pchan->bone->flag |= BONE_SELECTED;
460         }
461       }
462       else {
463         if (pchan->agrp_index == pose->active_group) {
464           pchan->bone->flag &= ~BONE_SELECTED;
465         }
466       }
467     }
468   }
469   FOREACH_PCHAN_VISIBLE_IN_OBJECT_END;
470 }
471
472 static int pose_group_select_exec(bContext *C, wmOperator *UNUSED(op))
473 {
474   Object *ob = ED_pose_object_from_context(C);
475
476   /* only continue if there's an object, and a pose there too */
477   if (ELEM(NULL, ob, ob->pose)) {
478     return OPERATOR_CANCELLED;
479   }
480
481   pose_group_select(ob, 1);
482
483   /* notifiers for updates */
484   bArmature *arm = ob->data;
485   DEG_id_tag_update(&arm->id, ID_RECALC_SELECT);
486   WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
487
488   return OPERATOR_FINISHED;
489 }
490
491 void POSE_OT_group_select(wmOperatorType *ot)
492 {
493   /* identifiers */
494   ot->name = "Select Bones of Bone Group";
495   ot->idname = "POSE_OT_group_select";
496   ot->description = "Select bones in active Bone Group";
497
498   /* api callbacks */
499   ot->exec = pose_group_select_exec;
500   ot->poll = ED_operator_posemode_context;
501
502   /* flags */
503   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
504 }
505
506 static int pose_group_deselect_exec(bContext *C, wmOperator *UNUSED(op))
507 {
508   Object *ob = ED_pose_object_from_context(C);
509
510   /* only continue if there's an object, and a pose there too */
511   if (ELEM(NULL, ob, ob->pose)) {
512     return OPERATOR_CANCELLED;
513   }
514
515   pose_group_select(ob, 0);
516
517   /* notifiers for updates */
518   bArmature *arm = ob->data;
519   DEG_id_tag_update(&arm->id, ID_RECALC_SELECT);
520   WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
521
522   return OPERATOR_FINISHED;
523 }
524
525 void POSE_OT_group_deselect(wmOperatorType *ot)
526 {
527   /* identifiers */
528   ot->name = "Deselect Bone Group";
529   ot->idname = "POSE_OT_group_deselect";
530   ot->description = "Deselect bones of active Bone Group";
531
532   /* api callbacks */
533   ot->exec = pose_group_deselect_exec;
534   ot->poll = ED_operator_posemode_context;
535
536   /* flags */
537   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
538 }
539
540 /* ********************************************** */