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