code cleanup: favor braces when blocks have mixed brace use.
[blender.git] / source / blender / editors / armature / armature_select.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) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * Contributor(s): Blender Foundation, 2002-2009 full recode.
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  *
25  * API's and Operators for selecting armature bones in EditMode
26  */
27
28 /** \file blender/editors/armature/armature_select.c
29  *  \ingroup edarmature
30  */
31
32 #include "DNA_armature_types.h"
33 #include "DNA_object_types.h"
34 #include "DNA_scene_types.h"
35
36 #include "BLI_blenlib.h"
37 #include "BLI_math.h"
38
39 #include "BKE_context.h"
40 #include "BKE_deform.h"
41 #include "BKE_report.h"
42
43 #include "BIF_gl.h"
44
45 #include "RNA_access.h"
46 #include "RNA_define.h"
47
48 #include "WM_api.h"
49 #include "WM_types.h"
50
51 #include "ED_armature.h"
52 #include "ED_screen.h"
53 #include "ED_view3d.h"
54
55 #include "armature_intern.h"
56
57 /* **************** PoseMode & EditMode Selection Buffer Queries *************************** */
58
59 /* only for opengl selection indices */
60 Bone *get_indexed_bone(Object *ob, int index)
61 {
62         bPoseChannel *pchan;
63         if (ob->pose == NULL) return NULL;
64         index >>= 16;     // bone selection codes use left 2 bytes
65         
66         pchan = BLI_findlink(&ob->pose->chanbase, index);
67         return pchan ? pchan->bone : NULL;
68 }
69
70 /* See if there are any selected bones in this buffer */
71 /* only bones from base are checked on */
72 void *get_bone_from_selectbuffer(Scene *scene, Base *base, unsigned int *buffer, short hits, short findunsel)
73 {
74         Object *obedit = scene->obedit; // XXX get from context
75         Bone *bone;
76         EditBone *ebone;
77         void *firstunSel = NULL, *firstSel = NULL, *data;
78         unsigned int hitresult;
79         short i, takeNext = 0, sel;
80         
81         for (i = 0; i < hits; i++) {
82                 hitresult = buffer[3 + (i * 4)];
83                 
84                 if (!(hitresult & BONESEL_NOSEL)) { // -1
85                         if (hitresult & BONESEL_ANY) {  // to avoid including objects in selection
86                                 
87                                 hitresult &= ~(BONESEL_ANY);
88                                 /* Determine what the current bone is */
89                                 if (obedit == NULL || base->object != obedit) {
90                                         /* no singular posemode, so check for correct object */
91                                         if (base->selcol == (hitresult & 0xFFFF)) {
92                                                 bone = get_indexed_bone(base->object, hitresult);
93                                                 
94                                                 if (findunsel)
95                                                         sel = (bone->flag & BONE_SELECTED);
96                                                 else
97                                                         sel = !(bone->flag & BONE_SELECTED);
98                                                 
99                                                 data = bone;
100                                         }
101                                         else {
102                                                 data = NULL;
103                                                 sel = 0;
104                                         }
105                                 }
106                                 else {
107                                         bArmature *arm = obedit->data;
108                                         
109                                         ebone = BLI_findlink(arm->edbo, hitresult);
110                                         if (findunsel)
111                                                 sel = (ebone->flag & BONE_SELECTED);
112                                         else
113                                                 sel = !(ebone->flag & BONE_SELECTED);
114                                         
115                                         data = ebone;
116                                 }
117                                 
118                                 if (data) {
119                                         if (sel) {
120                                                 if (!firstSel) firstSel = data;
121                                                 takeNext = 1;
122                                         }
123                                         else {
124                                                 if (!firstunSel)
125                                                         firstunSel = data;
126                                                 if (takeNext)
127                                                         return data;
128                                         }
129                                 }
130                         }
131                 }
132         }
133         
134         if (firstunSel)
135                 return firstunSel;
136         else 
137                 return firstSel;
138 }
139
140 /* used by posemode as well editmode */
141 /* only checks scene->basact! */
142 /* x and y are mouse coords (area space) */
143 void *get_nearest_bone(bContext *C, short findunsel, int x, int y)
144 {
145         ViewContext vc;
146         rcti rect;
147         unsigned int buffer[MAXPICKBUF];
148         short hits;
149         
150         view3d_set_viewcontext(C, &vc);
151         
152         // rect.xmin = ... mouseco!
153         rect.xmin = rect.xmax = x;
154         rect.ymin = rect.ymax = y;
155         
156         glInitNames();
157         hits = view3d_opengl_select(&vc, buffer, MAXPICKBUF, &rect);
158
159         if (hits > 0)
160                 return get_bone_from_selectbuffer(vc.scene, vc.scene->basact, buffer, hits, findunsel);
161         
162         return NULL;
163 }
164
165 /* **************** EditMode stuff ********************** */
166
167 /* called in space.c */
168 /* previously "selectconnected_armature" */
169 static int armature_select_linked_invoke(bContext *C, wmOperator *op, wmEvent *event)
170 {
171         bArmature *arm;
172         EditBone *bone, *curBone, *next;
173         int extend = RNA_boolean_get(op->ptr, "extend");
174         Object *obedit = CTX_data_edit_object(C);
175         arm = obedit->data;
176
177         view3d_operator_needs_opengl(C);
178
179         if (extend)
180                 bone = get_nearest_bone(C, 0, event->mval[0], event->mval[1]);
181         else
182                 bone = get_nearest_bone(C, 1, event->mval[0], event->mval[1]);
183
184         if (!bone)
185                 return OPERATOR_CANCELLED;
186
187         /* Select parents */
188         for (curBone = bone; curBone; curBone = next) {
189                 if ((curBone->flag & BONE_UNSELECTABLE) == 0) {
190                         if (extend) {
191                                 curBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
192                         }
193                         else {
194                                 curBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
195                         }
196                 }
197                 
198                 if (curBone->flag & BONE_CONNECTED)
199                         next = curBone->parent;
200                 else
201                         next = NULL;
202         }
203
204         /* Select children */
205         while (bone) {
206                 for (curBone = arm->edbo->first; curBone; curBone = next) {
207                         next = curBone->next;
208                         if ((curBone->parent == bone) && (curBone->flag & BONE_UNSELECTABLE) == 0) {
209                                 if (curBone->flag & BONE_CONNECTED) {
210                                         if (extend)
211                                                 curBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
212                                         else
213                                                 curBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
214                                         bone = curBone;
215                                         break;
216                                 }
217                                 else {
218                                         bone = NULL;
219                                         break;
220                                 }
221                         }
222                 }
223                 if (!curBone)
224                         bone = NULL;
225         }
226         
227         ED_armature_sync_selection(arm->edbo);
228         
229         WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);
230         
231         return OPERATOR_FINISHED;
232 }
233
234 static int armature_select_linked_poll(bContext *C)
235 {
236         return (ED_operator_view3d_active(C) && ED_operator_editarmature(C) );
237 }
238
239 void ARMATURE_OT_select_linked(wmOperatorType *ot)
240 {
241         /* identifiers */
242         ot->name = "Select Connected";
243         ot->idname = "ARMATURE_OT_select_linked";
244         ot->description = "Select bones related to selected ones by parent/child relationships";
245         
246         /* api callbacks */
247         ot->exec = NULL;
248         ot->invoke = armature_select_linked_invoke;
249         ot->poll = armature_select_linked_poll;
250         
251         /* flags */
252         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
253         
254         /* properties */
255         RNA_def_boolean(ot->srna, "extend", FALSE, "Extend", "Extend selection instead of deselecting everything first");
256 }
257
258 /* does bones and points */
259 /* note that BONE ROOT only gets drawn for root bones (or without IK) */
260 static EditBone *get_nearest_editbonepoint(ViewContext *vc, const int mval[2],
261                                            ListBase *edbo, int findunsel, int *selmask)
262 {
263         bArmature *arm = (bArmature *)vc->obedit->data;
264         EditBone *ebone_next_act = arm->act_edbone;
265
266         EditBone *ebone;
267         rcti rect;
268         unsigned int buffer[MAXPICKBUF];
269         unsigned int hitresult, besthitresult = BONESEL_NOSEL;
270         int i, mindep = 4;
271         short hits;
272
273         glInitNames();
274         
275         /* find the bone after the current active bone, so as to bump up its chances in selection.
276          * this way overlapping bones will cycle selection state as with objects. */
277         if (ebone_next_act &&
278             EBONE_VISIBLE(arm, ebone_next_act) &&
279             ebone_next_act->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL))
280         {
281                 ebone_next_act = ebone_next_act->next ? ebone_next_act->next : arm->edbo->first;
282         }
283         else {
284                 ebone_next_act = NULL;
285         }
286
287         rect.xmin = mval[0] - 5;
288         rect.xmax = mval[0] + 5;
289         rect.ymin = mval[1] - 5;
290         rect.ymax = mval[1] + 5;
291
292         hits = view3d_opengl_select(vc, buffer, MAXPICKBUF, &rect);
293         if (hits == 0) {
294                 rect.xmin = mval[0] - 12;
295                 rect.xmax = mval[0] + 12;
296                 rect.ymin = mval[1] - 12;
297                 rect.ymax = mval[1] + 12;
298                 hits = view3d_opengl_select(vc, buffer, MAXPICKBUF, &rect);
299         }
300         /* See if there are any selected bones in this group */
301         if (hits > 0) {
302                 
303                 if (hits == 1) {
304                         if (!(buffer[3] & BONESEL_NOSEL)) 
305                                 besthitresult = buffer[3];
306                 }
307                 else {
308                         for (i = 0; i < hits; i++) {
309                                 hitresult = buffer[3 + (i * 4)];
310                                 if (!(hitresult & BONESEL_NOSEL)) {
311                                         int dep;
312                                         
313                                         ebone = BLI_findlink(edbo, hitresult & ~BONESEL_ANY);
314                                         
315                                         /* clicks on bone points get advantage */
316                                         if (hitresult & (BONESEL_ROOT | BONESEL_TIP)) {
317                                                 /* but also the unselected one */
318                                                 if (findunsel) {
319                                                         if ( (hitresult & BONESEL_ROOT) && (ebone->flag & BONE_ROOTSEL) == 0)
320                                                                 dep = 1;
321                                                         else if ( (hitresult & BONESEL_TIP) && (ebone->flag & BONE_TIPSEL) == 0)
322                                                                 dep = 1;
323                                                         else 
324                                                                 dep = 2;
325                                                 }
326                                                 else {
327                                                         dep = 2;
328                                                 }
329                                         }
330                                         else {
331                                                 /* bone found */
332                                                 if (findunsel) {
333                                                         if ((ebone->flag & BONE_SELECTED) == 0)
334                                                                 dep = 2;
335                                                         else
336                                                                 dep = 3;
337                                                 }
338                                                 else {
339                                                         dep = 3;
340                                                 }
341                                         }
342
343                                         if (ebone == ebone_next_act) {
344                                                 dep -= 1;
345                                         }
346
347                                         if (dep < mindep) {
348                                                 mindep = dep;
349                                                 besthitresult = hitresult;
350                                         }
351                                 }
352                         }
353                 }
354                 
355                 if (!(besthitresult & BONESEL_NOSEL)) {
356                         
357                         ebone = BLI_findlink(edbo, besthitresult & ~BONESEL_ANY);
358                         
359                         *selmask = 0;
360                         if (besthitresult & BONESEL_ROOT)
361                                 *selmask |= BONE_ROOTSEL;
362                         if (besthitresult & BONESEL_TIP)
363                                 *selmask |= BONE_TIPSEL;
364                         if (besthitresult & BONESEL_BONE)
365                                 *selmask |= BONE_SELECTED;
366                         return ebone;
367                 }
368         }
369         *selmask = 0;
370         return NULL;
371 }
372
373
374
375 /* toggle==0: deselect
376  * toggle==1: swap (based on test)
377  * toggle==2: swap (no test), CURRENTLY UNUSED
378  */
379 void ED_armature_deselect_all(Object *obedit, int toggle)
380 {
381         bArmature *arm = obedit->data;
382         EditBone    *eBone;
383         int sel = 1;
384         
385         if (toggle == 1) {
386                 /* Determine if there are any selected bones
387                  * and therefore whether we are selecting or deselecting */
388                 for (eBone = arm->edbo->first; eBone; eBone = eBone->next) {
389                         //                      if (arm->layer & eBone->layer) {
390                         if (eBone->flag & (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL)) {
391                                 sel = 0;
392                                 break;
393                         }
394                         //                      }
395                 }
396         }
397         else {
398                 sel = toggle;
399         }
400
401         /*      Set the flags */
402         for (eBone = arm->edbo->first; eBone; eBone = eBone->next) {
403                 if (sel == 2) {
404                         /* invert selection of bone */
405                         if (EBONE_VISIBLE(arm, eBone)) {
406                                 eBone->flag ^= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
407                                 if (arm->act_edbone == eBone)
408                                         arm->act_edbone = NULL;
409                         }
410                 }
411                 else if (sel == 1) {
412                         /* select bone */
413                         if (EBONE_VISIBLE(arm, eBone)) {
414                                 eBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
415                                 if (eBone->parent)
416                                         eBone->parent->flag |= (BONE_TIPSEL);
417                         }
418                 }
419                 else {
420                         /* deselect bone */
421                         eBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
422                         if (arm->act_edbone == eBone)
423                                 arm->act_edbone = NULL;
424                 }
425         }
426         
427         ED_armature_sync_selection(arm->edbo);
428 }
429
430 void ED_armature_deselect_all_visible(Object *obedit)
431 {
432         bArmature *arm = obedit->data;
433         EditBone    *ebone;
434
435         for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
436                 /* first and foremost, bone must be visible and selected */
437                 if (EBONE_SELECTABLE(arm, ebone)) {
438                         ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
439                 }
440         }
441
442         ED_armature_sync_selection(arm->edbo);
443 }
444
445 /* accounts for connected parents */
446 static int ebone_select_flag(EditBone *ebone)
447 {
448         if (ebone->parent && (ebone->flag & BONE_CONNECTED)) {
449                 return ((ebone->parent->flag & BONE_TIPSEL) ? BONE_ROOTSEL : 0) | (ebone->flag & (BONE_SELECTED | BONE_TIPSEL));
450         }
451         else {
452                 return ebone->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL);
453         }
454 }
455
456 /* context: editmode armature in view3d */
457 int mouse_armature(bContext *C, const int mval[2], int extend, int deselect, int toggle)
458 {
459         Object *obedit = CTX_data_edit_object(C);
460         bArmature *arm = obedit->data;
461         ViewContext vc;
462         EditBone *nearBone = NULL;
463         int selmask;
464
465         view3d_set_viewcontext(C, &vc);
466         
467         BIF_sk_selectStroke(C, mval, extend);
468         
469         nearBone = get_nearest_editbonepoint(&vc, mval, arm->edbo, 1, &selmask);
470         if (nearBone) {
471
472                 if (!extend && !deselect && !toggle)
473                         ED_armature_deselect_all(obedit, 0);
474                 
475                 /* by definition the non-root connected bones have no root point drawn,
476                  * so a root selection needs to be delivered to the parent tip */
477                 
478                 if (selmask & BONE_SELECTED) {
479                         if (nearBone->parent && (nearBone->flag & BONE_CONNECTED)) {
480                                 /* click in a chain */
481                                 if (extend) {
482                                         /* select this bone */
483                                         nearBone->flag |= BONE_TIPSEL;
484                                         nearBone->parent->flag |= BONE_TIPSEL;
485                                 }
486                                 else if (deselect) {
487                                         /* deselect this bone */
488                                         nearBone->flag &= ~(BONE_TIPSEL | BONE_SELECTED);
489                                         /* only deselect parent tip if it is not selected */
490                                         if (!(nearBone->parent->flag & BONE_SELECTED))
491                                                 nearBone->parent->flag &= ~BONE_TIPSEL;
492                                 }
493                                 else if (toggle) {
494                                         /* hold shift inverts this bone's selection */
495                                         if (nearBone->flag & BONE_SELECTED) {
496                                                 /* deselect this bone */
497                                                 nearBone->flag &= ~(BONE_TIPSEL | BONE_SELECTED);
498                                                 /* only deselect parent tip if it is not selected */
499                                                 if (!(nearBone->parent->flag & BONE_SELECTED))
500                                                         nearBone->parent->flag &= ~BONE_TIPSEL;
501                                         }
502                                         else {
503                                                 /* select this bone */
504                                                 nearBone->flag |= BONE_TIPSEL;
505                                                 nearBone->parent->flag |= BONE_TIPSEL;
506                                         }
507                                 }
508                                 else {
509                                         /* select this bone */
510                                         nearBone->flag |= BONE_TIPSEL;
511                                         nearBone->parent->flag |= BONE_TIPSEL;
512                                 }
513                         }
514                         else {
515                                 if (extend) {
516                                         nearBone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
517                                 }
518                                 else if (deselect) {
519                                         nearBone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL);
520                                 }
521                                 else if (toggle) {
522                                         /* hold shift inverts this bone's selection */
523                                         if (nearBone->flag & BONE_SELECTED)
524                                                 nearBone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL);
525                                         else
526                                                 nearBone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
527                                 }
528                                 else
529                                         nearBone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
530                         }
531                 }
532                 else {
533                         if (extend)
534                                 nearBone->flag |= selmask;
535                         else if (deselect)
536                                 nearBone->flag &= ~selmask;
537                         else if (toggle && (nearBone->flag & selmask))
538                                 nearBone->flag &= ~selmask;
539                         else
540                                 nearBone->flag |= selmask;
541                 }
542                 
543                 ED_armature_sync_selection(arm->edbo);
544                 
545                 if (nearBone) {
546                         /* then now check for active status */
547                         if (ebone_select_flag(nearBone)) {
548                                 arm->act_edbone = nearBone;
549                         }
550                 }
551                 
552                 WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, vc.obedit);
553                 return 1;
554         }
555
556         return 0;
557 }
558
559
560 /* ****************  Selections  ******************/
561
562 static int armature_select_inverse_exec(bContext *C, wmOperator *UNUSED(op))
563 {
564         /*      Set the flags */
565         CTX_DATA_BEGIN(C, EditBone *, ebone, visible_bones)
566         {
567                 /* ignore bone if selection can't change */
568                 if ((ebone->flag & BONE_UNSELECTABLE) == 0) {
569                         /* select bone */
570                         ebone->flag ^= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
571                 }
572         }
573         CTX_DATA_END;
574
575         WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, NULL);
576         
577         return OPERATOR_FINISHED;
578 }
579
580 void ARMATURE_OT_select_inverse(wmOperatorType *ot)
581 {
582         /* identifiers */
583         ot->name = "Select Inverse";
584         ot->idname = "ARMATURE_OT_select_inverse";
585         ot->description = "Flip the selection status of bones (selected -> unselected, unselected -> selected)";
586         
587         /* api callbacks */
588         ot->exec = armature_select_inverse_exec;
589         ot->poll = ED_operator_editarmature;
590         
591         /* flags */
592         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
593         
594 }
595 static int armature_de_select_all_exec(bContext *C, wmOperator *op)
596 {
597         int action = RNA_enum_get(op->ptr, "action");
598
599         if (action == SEL_TOGGLE) {
600                 action = SEL_SELECT;
601                 /* Determine if there are any selected bones
602                  * And therefore whether we are selecting or deselecting */
603                 if (CTX_DATA_COUNT(C, selected_bones) > 0)
604                         action = SEL_DESELECT;
605         }
606         
607         /*      Set the flags */
608         CTX_DATA_BEGIN(C, EditBone *, ebone, visible_bones)
609         {
610                 /* ignore bone if selection can't change */
611                 if ((ebone->flag & BONE_UNSELECTABLE) == 0) {
612                         switch (action) {
613                                 case SEL_SELECT:
614                                         ebone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
615                                         if (ebone->parent)
616                                                 ebone->parent->flag |= (BONE_TIPSEL);
617                                         break;
618                                 case SEL_DESELECT:
619                                         ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
620                                         break;
621                                 case SEL_INVERT:
622                                         if (ebone->flag & BONE_SELECTED) {
623                                                 ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
624                                         }
625                                         else {
626                                                 ebone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
627                                                 if (ebone->parent)
628                                                         ebone->parent->flag |= (BONE_TIPSEL);
629                                         }
630                                         break;
631                         }
632                 }
633         }
634         CTX_DATA_END;
635
636         WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, NULL);
637         
638         return OPERATOR_FINISHED;
639 }
640
641 void ARMATURE_OT_select_all(wmOperatorType *ot)
642 {
643         /* identifiers */
644         ot->name = "(De)select All";
645         ot->idname = "ARMATURE_OT_select_all";
646         ot->description = "Toggle selection status of all bones";
647         
648         /* api callbacks */
649         ot->exec = armature_de_select_all_exec;
650         ot->poll = ED_operator_editarmature;
651         
652         /* flags */
653         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
654         
655         WM_operator_properties_select_all(ot);
656 }
657
658 enum {
659         SIMEDBONE_LENGTH = 1,
660         SIMEDBONE_DIRECTION,
661         SIMEDBONE_PREFIX,
662         SIMEDBONE_SUFFIX,
663         SIMEDBONE_LAYER
664 };
665
666 static EnumPropertyItem prop_similar_types[] = {
667         {SIMEDBONE_LENGTH, "LENGTH", 0, "Length", ""},
668         {SIMEDBONE_DIRECTION, "DIRECTION", 0, "Direction (Y axis)", ""},
669         {SIMEDBONE_PREFIX, "PREFIX", 0, "Prefix", ""},
670         {SIMEDBONE_SUFFIX, "SUFFIX", 0, "Suffix", ""},
671         {SIMEDBONE_LAYER, "LAYER", 0, "Layer", ""},
672         {0, NULL, 0, NULL, NULL}
673 };
674
675 /* could be used in more places */
676 static void ED_armature_edit_bone_select(EditBone *ebone)
677 {
678         BLI_assert((ebone->flag & BONE_UNSELECTABLE) == 0);
679         ebone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
680
681         if ((ebone->flag & BONE_CONNECTED) && (ebone->parent != NULL)) {
682                 ebone->parent->flag |= BONE_TIPSEL;
683         }
684 }
685
686 static void select_similar_length(bArmature *arm, EditBone *ebone_act, const float thresh)
687 {
688         EditBone *ebone;
689
690         /* thresh is always relative to current length */
691         const float len_min = ebone_act->length / (1.0f + thresh);
692         const float len_max = ebone_act->length * (1.0f + thresh);
693
694         for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
695                 if (EBONE_SELECTABLE(arm, ebone)) {
696                         if ((ebone->length >= len_min) &&
697                             (ebone->length <= len_max))
698                         {
699                                 ED_armature_edit_bone_select(ebone);
700                         }
701                 }
702         }
703 }
704
705 static void select_similar_direction(bArmature *arm, EditBone *ebone_act, const float thresh)
706 {
707         EditBone *ebone;
708         float dir_act[3];
709         sub_v3_v3v3(dir_act, ebone_act->head, ebone_act->tail);
710
711         for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
712                 if (EBONE_SELECTABLE(arm, ebone)) {
713                         float dir[3];
714                         sub_v3_v3v3(dir, ebone->head, ebone->tail);
715
716                         if (angle_v3v3(dir_act, dir) / (float)M_PI < thresh) {
717                                 ED_armature_edit_bone_select(ebone);
718                         }
719                 }
720         }
721 }
722
723 static void select_similar_layer(bArmature *arm, EditBone *ebone_act)
724 {
725         EditBone *ebone;
726
727         for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
728                 if (EBONE_SELECTABLE(arm, ebone)) {
729                         if (ebone->layer & ebone_act->layer) {
730                                 ED_armature_edit_bone_select(ebone);
731                         }
732                 }
733         }
734 }
735
736 static void select_similar_prefix(bArmature *arm, EditBone *ebone_act)
737 {
738         EditBone *ebone;
739
740         char body_tmp[MAX_VGROUP_NAME];
741         char prefix_act[MAX_VGROUP_NAME];
742
743         BKE_deform_split_prefix(ebone_act->name, prefix_act, body_tmp);
744
745         if (prefix_act[0] == '\0')
746                 return;
747
748         /* Find matches */
749         for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
750                 if (EBONE_SELECTABLE(arm, ebone)) {
751                         char prefix_other[MAX_VGROUP_NAME];
752                         BKE_deform_split_prefix(ebone->name, prefix_other, body_tmp);
753                         if (!strcmp(prefix_act, prefix_other)) {
754                                 ED_armature_edit_bone_select(ebone);
755                         }
756                 }
757         }
758 }
759
760 static void select_similar_suffix(bArmature *arm, EditBone *ebone_act)
761 {
762         EditBone *ebone;
763
764         char body_tmp[MAX_VGROUP_NAME];
765         char suffix_act[MAX_VGROUP_NAME];
766
767         BKE_deform_split_suffix(ebone_act->name, body_tmp, suffix_act);
768
769         if (suffix_act[0] == '\0')
770                 return;
771
772         /* Find matches */
773         for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
774                 if (EBONE_SELECTABLE(arm, ebone)) {
775                         char suffix_other[MAX_VGROUP_NAME];
776                         BKE_deform_split_suffix(ebone->name, body_tmp, suffix_other);
777                         if (!strcmp(suffix_act, suffix_other)) {
778                                 ED_armature_edit_bone_select(ebone);
779                         }
780                 }
781         }
782 }
783
784 static int armature_select_similar_exec(bContext *C, wmOperator *op)
785 {
786         Object *obedit = CTX_data_edit_object(C);
787         bArmature *arm = obedit->data;
788         EditBone *ebone_act = CTX_data_active_bone(C);
789
790         /* Get props */
791         int type = RNA_enum_get(op->ptr, "type");
792         float thresh = RNA_float_get(op->ptr, "threshold");
793
794         /* Check for active bone */
795         if (ebone_act == NULL) {
796                 BKE_report(op->reports, RPT_ERROR, "Operation requires an active bone");
797                 return OPERATOR_CANCELLED;
798         }
799
800         switch (type) {
801                 case SIMEDBONE_LENGTH:
802                         select_similar_length(arm, ebone_act, thresh);
803                         break;
804                 case SIMEDBONE_DIRECTION:
805                         select_similar_direction(arm, ebone_act, thresh);
806                         break;
807                 case SIMEDBONE_PREFIX:
808                         select_similar_prefix(arm, ebone_act);
809                         break;
810                 case SIMEDBONE_SUFFIX:
811                         select_similar_suffix(arm, ebone_act);
812                         break;
813                 case SIMEDBONE_LAYER:
814                         select_similar_layer(arm, ebone_act);
815                         break;
816         }
817
818         WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);
819
820         return OPERATOR_FINISHED;
821 }
822
823 void ARMATURE_OT_select_similar(wmOperatorType *ot)
824 {
825         /* identifiers */
826         ot->name = "Select Similar";
827         ot->idname = "ARMATURE_OT_select_similar";
828
829         /* callback functions */
830         ot->invoke = WM_menu_invoke;
831         ot->exec = armature_select_similar_exec;
832         ot->poll = ED_operator_editarmature;
833         ot->description = "Select similar bones by property types";
834
835         /* flags */
836         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
837
838         /* properties */
839         ot->prop = RNA_def_enum(ot->srna, "type", prop_similar_types, 0, "Type", "");
840         RNA_def_float(ot->srna, "threshold", 0.1f, 0.0f, 1.0f, "Threshold", "", 0.0f, 1.0f);
841 }
842
843 /* ********************* select hierarchy operator ************** */
844
845 /* Get the first available child of an editbone */
846 static EditBone *editbone_get_child(bArmature *arm, EditBone *pabone, short use_visibility)
847 {
848         EditBone *curbone, *chbone = NULL;
849         
850         for (curbone = arm->edbo->first; curbone; curbone = curbone->next) {
851                 if (curbone->parent == pabone) {
852                         if (use_visibility) {
853                                 if ((arm->layer & curbone->layer) && !(pabone->flag & BONE_HIDDEN_A)) {
854                                         chbone = curbone;
855                                 }
856                         }
857                         else
858                                 chbone = curbone;
859                 }
860         }
861         
862         return chbone;
863 }
864
865 static int armature_select_hierarchy_exec(bContext *C, wmOperator *op)
866 {
867         Object *obedit = CTX_data_edit_object(C);
868         Object *ob;
869         bArmature *arm;
870         EditBone *curbone, *pabone, *chbone;
871         int direction = RNA_enum_get(op->ptr, "direction");
872         int add_to_sel = RNA_boolean_get(op->ptr, "extend");
873         
874         ob = obedit;
875         arm = (bArmature *)ob->data;
876         
877         for (curbone = arm->edbo->first; curbone; curbone = curbone->next) {
878                 /* only work on bone if it is visible and its selection can change */
879                 if (EBONE_SELECTABLE(arm, curbone)) {
880                         if (curbone == arm->act_edbone) {
881                                 if (direction == BONE_SELECT_PARENT) {
882                                         if (curbone->parent == NULL) continue;
883                                         else pabone = curbone->parent;
884                                         
885                                         if (EBONE_VISIBLE(arm, pabone)) {
886                                                 pabone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
887                                                 arm->act_edbone = pabone;
888                                                 if (pabone->parent) pabone->parent->flag |= BONE_TIPSEL;
889                                                 
890                                                 if (!add_to_sel) curbone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
891                                                 break;
892                                         }
893                                         
894                                 }
895                                 else { // BONE_SELECT_CHILD
896                                         chbone = editbone_get_child(arm, curbone, 1);
897                                         if (chbone == NULL) continue;
898                                         
899                                         if (EBONE_SELECTABLE(arm, chbone)) {
900                                                 chbone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
901                                                 arm->act_edbone = chbone;
902                                                 
903                                                 if (!add_to_sel) {
904                                                         curbone->flag &= ~(BONE_SELECTED | BONE_ROOTSEL);
905                                                         if (curbone->parent) curbone->parent->flag &= ~BONE_TIPSEL;
906                                                 }
907                                                 break;
908                                         }
909                                 }
910                         }
911                 }
912         }
913         
914         ED_armature_sync_selection(arm->edbo);
915         
916         WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob);
917         
918         return OPERATOR_FINISHED;
919 }
920
921 void ARMATURE_OT_select_hierarchy(wmOperatorType *ot)
922 {
923         static EnumPropertyItem direction_items[] = {
924                 {BONE_SELECT_PARENT, "PARENT", 0, "Select Parent", ""},
925                 {BONE_SELECT_CHILD, "CHILD", 0, "Select Child", ""},
926                 {0, NULL, 0, NULL, NULL}
927         };
928         
929         /* identifiers */
930         ot->name = "Select Hierarchy";
931         ot->idname = "ARMATURE_OT_select_hierarchy";
932         ot->description = "Select immediate parent/children of selected bones";
933         
934         /* api callbacks */
935         ot->exec = armature_select_hierarchy_exec;
936         ot->poll = ED_operator_editarmature;
937         
938         /* flags */
939         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
940
941         /* props */
942         RNA_def_enum(ot->srna, "direction", direction_items,
943                      BONE_SELECT_PARENT, "Direction", "");
944         RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
945 }
946