b7a436cc20909af046627fe07a8ade87a6bb8ac4
[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 dep = 2;
327                                         }
328                                         else {
329                                                 /* bone found */
330                                                 if (findunsel) {
331                                                         if ((ebone->flag & BONE_SELECTED) == 0)
332                                                                 dep = 2;
333                                                         else
334                                                                 dep = 3;
335                                                 }
336                                                 else dep = 3;
337                                         }
338
339                                         if (ebone == ebone_next_act) {
340                                                 dep -= 1;
341                                         }
342
343                                         if (dep < mindep) {
344                                                 mindep = dep;
345                                                 besthitresult = hitresult;
346                                         }
347                                 }
348                         }
349                 }
350                 
351                 if (!(besthitresult & BONESEL_NOSEL)) {
352                         
353                         ebone = BLI_findlink(edbo, besthitresult & ~BONESEL_ANY);
354                         
355                         *selmask = 0;
356                         if (besthitresult & BONESEL_ROOT)
357                                 *selmask |= BONE_ROOTSEL;
358                         if (besthitresult & BONESEL_TIP)
359                                 *selmask |= BONE_TIPSEL;
360                         if (besthitresult & BONESEL_BONE)
361                                 *selmask |= BONE_SELECTED;
362                         return ebone;
363                 }
364         }
365         *selmask = 0;
366         return NULL;
367 }
368
369
370
371 /* toggle==0: deselect
372  * toggle==1: swap (based on test)
373  * toggle==2: swap (no test), CURRENTLY UNUSED
374  */
375 void ED_armature_deselect_all(Object *obedit, int toggle)
376 {
377         bArmature *arm = obedit->data;
378         EditBone    *eBone;
379         int sel = 1;
380         
381         if (toggle == 1) {
382                 /* Determine if there are any selected bones
383                  * and therefore whether we are selecting or deselecting */
384                 for (eBone = arm->edbo->first; eBone; eBone = eBone->next) {
385                         //                      if (arm->layer & eBone->layer) {
386                         if (eBone->flag & (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL)) {
387                                 sel = 0;
388                                 break;
389                         }
390                         //                      }
391                 }
392         }
393         else sel = toggle;
394         
395         /*      Set the flags */
396         for (eBone = arm->edbo->first; eBone; eBone = eBone->next) {
397                 if (sel == 2) {
398                         /* invert selection of bone */
399                         if (EBONE_VISIBLE(arm, eBone)) {
400                                 eBone->flag ^= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
401                                 if (arm->act_edbone == eBone)
402                                         arm->act_edbone = NULL;
403                         }
404                 }
405                 else if (sel == 1) {
406                         /* select bone */
407                         if (EBONE_VISIBLE(arm, eBone)) {
408                                 eBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
409                                 if (eBone->parent)
410                                         eBone->parent->flag |= (BONE_TIPSEL);
411                         }
412                 }
413                 else {
414                         /* deselect bone */
415                         eBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
416                         if (arm->act_edbone == eBone)
417                                 arm->act_edbone = NULL;
418                 }
419         }
420         
421         ED_armature_sync_selection(arm->edbo);
422 }
423
424 void ED_armature_deselect_all_visible(Object *obedit)
425 {
426         bArmature *arm = obedit->data;
427         EditBone    *ebone;
428
429         for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
430                 /* first and foremost, bone must be visible and selected */
431                 if (EBONE_SELECTABLE(arm, ebone)) {
432                         ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
433                 }
434         }
435
436         ED_armature_sync_selection(arm->edbo);
437 }
438
439 /* accounts for connected parents */
440 static int ebone_select_flag(EditBone *ebone)
441 {
442         if (ebone->parent && (ebone->flag & BONE_CONNECTED)) {
443                 return ((ebone->parent->flag & BONE_TIPSEL) ? BONE_ROOTSEL : 0) | (ebone->flag & (BONE_SELECTED | BONE_TIPSEL));
444         }
445         else {
446                 return ebone->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL);
447         }
448 }
449
450 /* context: editmode armature in view3d */
451 int mouse_armature(bContext *C, const int mval[2], int extend, int deselect, int toggle)
452 {
453         Object *obedit = CTX_data_edit_object(C);
454         bArmature *arm = obedit->data;
455         ViewContext vc;
456         EditBone *nearBone = NULL;
457         int selmask;
458
459         view3d_set_viewcontext(C, &vc);
460         
461         BIF_sk_selectStroke(C, mval, extend);
462         
463         nearBone = get_nearest_editbonepoint(&vc, mval, arm->edbo, 1, &selmask);
464         if (nearBone) {
465
466                 if (!extend && !deselect && !toggle)
467                         ED_armature_deselect_all(obedit, 0);
468                 
469                 /* by definition the non-root connected bones have no root point drawn,
470                  * so a root selection needs to be delivered to the parent tip */
471                 
472                 if (selmask & BONE_SELECTED) {
473                         if (nearBone->parent && (nearBone->flag & BONE_CONNECTED)) {
474                                 /* click in a chain */
475                                 if (extend) {
476                                         /* select this bone */
477                                         nearBone->flag |= BONE_TIPSEL;
478                                         nearBone->parent->flag |= BONE_TIPSEL;
479                                 }
480                                 else if (deselect) {
481                                         /* deselect this bone */
482                                         nearBone->flag &= ~(BONE_TIPSEL | BONE_SELECTED);
483                                         /* only deselect parent tip if it is not selected */
484                                         if (!(nearBone->parent->flag & BONE_SELECTED))
485                                                 nearBone->parent->flag &= ~BONE_TIPSEL;
486                                 }
487                                 else if (toggle) {
488                                         /* hold shift inverts this bone's selection */
489                                         if (nearBone->flag & BONE_SELECTED) {
490                                                 /* deselect this bone */
491                                                 nearBone->flag &= ~(BONE_TIPSEL | BONE_SELECTED);
492                                                 /* only deselect parent tip if it is not selected */
493                                                 if (!(nearBone->parent->flag & BONE_SELECTED))
494                                                         nearBone->parent->flag &= ~BONE_TIPSEL;
495                                         }
496                                         else {
497                                                 /* select this bone */
498                                                 nearBone->flag |= BONE_TIPSEL;
499                                                 nearBone->parent->flag |= BONE_TIPSEL;
500                                         }
501                                 }
502                                 else {
503                                         /* select this bone */
504                                         nearBone->flag |= BONE_TIPSEL;
505                                         nearBone->parent->flag |= BONE_TIPSEL;
506                                 }
507                         }
508                         else {
509                                 if (extend) {
510                                         nearBone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
511                                 }
512                                 else if (deselect) {
513                                         nearBone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL);
514                                 }
515                                 else if (toggle) {
516                                         /* hold shift inverts this bone's selection */
517                                         if (nearBone->flag & BONE_SELECTED)
518                                                 nearBone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL);
519                                         else
520                                                 nearBone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
521                                 }
522                                 else
523                                         nearBone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
524                         }
525                 }
526                 else {
527                         if (extend)
528                                 nearBone->flag |= selmask;
529                         else if (deselect)
530                                 nearBone->flag &= ~selmask;
531                         else if (toggle && (nearBone->flag & selmask))
532                                 nearBone->flag &= ~selmask;
533                         else
534                                 nearBone->flag |= selmask;
535                 }
536                 
537                 ED_armature_sync_selection(arm->edbo);
538                 
539                 if (nearBone) {
540                         /* then now check for active status */
541                         if (ebone_select_flag(nearBone)) {
542                                 arm->act_edbone = nearBone;
543                         }
544                 }
545                 
546                 WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, vc.obedit);
547                 return 1;
548         }
549
550         return 0;
551 }
552
553
554 /* ****************  Selections  ******************/
555
556 static int armature_select_inverse_exec(bContext *C, wmOperator *UNUSED(op))
557 {
558         /*      Set the flags */
559         CTX_DATA_BEGIN(C, EditBone *, ebone, visible_bones)
560         {
561                 /* ignore bone if selection can't change */
562                 if ((ebone->flag & BONE_UNSELECTABLE) == 0) {
563                         /* select bone */
564                         ebone->flag ^= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
565                 }
566         }
567         CTX_DATA_END;
568
569         WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, NULL);
570         
571         return OPERATOR_FINISHED;
572 }
573
574 void ARMATURE_OT_select_inverse(wmOperatorType *ot)
575 {
576         /* identifiers */
577         ot->name = "Select Inverse";
578         ot->idname = "ARMATURE_OT_select_inverse";
579         ot->description = "Flip the selection status of bones (selected -> unselected, unselected -> selected)";
580         
581         /* api callbacks */
582         ot->exec = armature_select_inverse_exec;
583         ot->poll = ED_operator_editarmature;
584         
585         /* flags */
586         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
587         
588 }
589 static int armature_de_select_all_exec(bContext *C, wmOperator *op)
590 {
591         int action = RNA_enum_get(op->ptr, "action");
592
593         if (action == SEL_TOGGLE) {
594                 action = SEL_SELECT;
595                 /* Determine if there are any selected bones
596                  * And therefore whether we are selecting or deselecting */
597                 if (CTX_DATA_COUNT(C, selected_bones) > 0)
598                         action = SEL_DESELECT;
599         }
600         
601         /*      Set the flags */
602         CTX_DATA_BEGIN(C, EditBone *, ebone, visible_bones)
603         {
604                 /* ignore bone if selection can't change */
605                 if ((ebone->flag & BONE_UNSELECTABLE) == 0) {
606                         switch (action) {
607                                 case SEL_SELECT:
608                                         ebone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
609                                         if (ebone->parent)
610                                                 ebone->parent->flag |= (BONE_TIPSEL);
611                                         break;
612                                 case SEL_DESELECT:
613                                         ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
614                                         break;
615                                 case SEL_INVERT:
616                                         if (ebone->flag & BONE_SELECTED) {
617                                                 ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
618                                         }
619                                         else {
620                                                 ebone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
621                                                 if (ebone->parent)
622                                                         ebone->parent->flag |= (BONE_TIPSEL);
623                                         }
624                                         break;
625                         }
626                 }
627         }
628         CTX_DATA_END;
629
630         WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, NULL);
631         
632         return OPERATOR_FINISHED;
633 }
634
635 void ARMATURE_OT_select_all(wmOperatorType *ot)
636 {
637         /* identifiers */
638         ot->name = "(De)select All";
639         ot->idname = "ARMATURE_OT_select_all";
640         ot->description = "Toggle selection status of all bones";
641         
642         /* api callbacks */
643         ot->exec = armature_de_select_all_exec;
644         ot->poll = ED_operator_editarmature;
645         
646         /* flags */
647         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
648         
649         WM_operator_properties_select_all(ot);
650 }
651
652 enum {
653         SIMEDBONE_LENGTH = 1,
654         SIMEDBONE_DIRECTION,
655         SIMEDBONE_PREFIX,
656         SIMEDBONE_SUFFIX,
657         SIMEDBONE_LAYER
658 };
659
660 static EnumPropertyItem prop_similar_types[] = {
661         {SIMEDBONE_LENGTH, "LENGTH", 0, "Length", ""},
662         {SIMEDBONE_DIRECTION, "DIRECTION", 0, "Direction (Y axis)", ""},
663         {SIMEDBONE_PREFIX, "PREFIX", 0, "Prefix", ""},
664         {SIMEDBONE_SUFFIX, "SUFFIX", 0, "Suffix", ""},
665         {SIMEDBONE_LAYER, "LAYER", 0, "Layer", ""},
666         {0, NULL, 0, NULL, NULL}
667 };
668
669 /* could be used in more places */
670 static void ED_armature_edit_bone_select(EditBone *ebone)
671 {
672         BLI_assert((ebone->flag & BONE_UNSELECTABLE) == 0);
673         ebone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
674
675         if ((ebone->flag & BONE_CONNECTED) && (ebone->parent != NULL)) {
676                 ebone->parent->flag |= BONE_TIPSEL;
677         }
678 }
679
680 static void select_similar_length(bArmature *arm, EditBone *ebone_act, const float thresh)
681 {
682         EditBone *ebone;
683
684         /* thresh is always relative to current length */
685         const float len_min = ebone_act->length / (1.0f + thresh);
686         const float len_max = ebone_act->length * (1.0f + thresh);
687
688         for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
689                 if (EBONE_SELECTABLE(arm, ebone)) {
690                         if ((ebone->length >= len_min) &&
691                             (ebone->length <= len_max))
692                         {
693                                 ED_armature_edit_bone_select(ebone);
694                         }
695                 }
696         }
697 }
698
699 static void select_similar_direction(bArmature *arm, EditBone *ebone_act, const float thresh)
700 {
701         EditBone *ebone;
702         float dir_act[3];
703         sub_v3_v3v3(dir_act, ebone_act->head, ebone_act->tail);
704
705         for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
706                 if (EBONE_SELECTABLE(arm, ebone)) {
707                         float dir[3];
708                         sub_v3_v3v3(dir, ebone->head, ebone->tail);
709
710                         if (angle_v3v3(dir_act, dir) / (float)M_PI < thresh) {
711                                 ED_armature_edit_bone_select(ebone);
712                         }
713                 }
714         }
715 }
716
717 static void select_similar_layer(bArmature *arm, EditBone *ebone_act)
718 {
719         EditBone *ebone;
720
721         for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
722                 if (EBONE_SELECTABLE(arm, ebone)) {
723                         if (ebone->layer & ebone_act->layer) {
724                                 ED_armature_edit_bone_select(ebone);
725                         }
726                 }
727         }
728 }
729
730 static void select_similar_prefix(bArmature *arm, EditBone *ebone_act)
731 {
732         EditBone *ebone;
733
734         char body_tmp[MAX_VGROUP_NAME];
735         char prefix_act[MAX_VGROUP_NAME];
736
737         BKE_deform_split_prefix(ebone_act->name, prefix_act, body_tmp);
738
739         if (prefix_act[0] == '\0')
740                 return;
741
742         /* Find matches */
743         for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
744                 if (EBONE_SELECTABLE(arm, ebone)) {
745                         char prefix_other[MAX_VGROUP_NAME];
746                         BKE_deform_split_prefix(ebone->name, prefix_other, body_tmp);
747                         if (!strcmp(prefix_act, prefix_other)) {
748                                 ED_armature_edit_bone_select(ebone);
749                         }
750                 }
751         }
752 }
753
754 static void select_similar_suffix(bArmature *arm, EditBone *ebone_act)
755 {
756         EditBone *ebone;
757
758         char body_tmp[MAX_VGROUP_NAME];
759         char suffix_act[MAX_VGROUP_NAME];
760
761         BKE_deform_split_suffix(ebone_act->name, body_tmp, suffix_act);
762
763         if (suffix_act[0] == '\0')
764                 return;
765
766         /* Find matches */
767         for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
768                 if (EBONE_SELECTABLE(arm, ebone)) {
769                         char suffix_other[MAX_VGROUP_NAME];
770                         BKE_deform_split_suffix(ebone->name, body_tmp, suffix_other);
771                         if (!strcmp(suffix_act, suffix_other)) {
772                                 ED_armature_edit_bone_select(ebone);
773                         }
774                 }
775         }
776 }
777
778 static int armature_select_similar_exec(bContext *C, wmOperator *op)
779 {
780         Object *obedit = CTX_data_edit_object(C);
781         bArmature *arm = obedit->data;
782         EditBone *ebone_act = CTX_data_active_bone(C);
783
784         /* Get props */
785         int type = RNA_enum_get(op->ptr, "type");
786         float thresh = RNA_float_get(op->ptr, "threshold");
787
788         /* Check for active bone */
789         if (ebone_act == NULL) {
790                 BKE_report(op->reports, RPT_ERROR, "Operation requires an active bone");
791                 return OPERATOR_CANCELLED;
792         }
793
794         switch (type) {
795                 case SIMEDBONE_LENGTH:
796                         select_similar_length(arm, ebone_act, thresh);
797                         break;
798                 case SIMEDBONE_DIRECTION:
799                         select_similar_direction(arm, ebone_act, thresh);
800                         break;
801                 case SIMEDBONE_PREFIX:
802                         select_similar_prefix(arm, ebone_act);
803                         break;
804                 case SIMEDBONE_SUFFIX:
805                         select_similar_suffix(arm, ebone_act);
806                         break;
807                 case SIMEDBONE_LAYER:
808                         select_similar_layer(arm, ebone_act);
809                         break;
810         }
811
812         WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);
813
814         return OPERATOR_FINISHED;
815 }
816
817 void ARMATURE_OT_select_similar(wmOperatorType *ot)
818 {
819         /* identifiers */
820         ot->name = "Select Similar";
821         ot->idname = "ARMATURE_OT_select_similar";
822
823         /* callback functions */
824         ot->invoke = WM_menu_invoke;
825         ot->exec = armature_select_similar_exec;
826         ot->poll = ED_operator_editarmature;
827         ot->description = "Select similar bones by property types";
828
829         /* flags */
830         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
831
832         /* properties */
833         ot->prop = RNA_def_enum(ot->srna, "type", prop_similar_types, 0, "Type", "");
834         RNA_def_float(ot->srna, "threshold", 0.1f, 0.0f, 1.0f, "Threshold", "", 0.0f, 1.0f);
835 }
836
837 /* ********************* select hierarchy operator ************** */
838
839 /* Get the first available child of an editbone */
840 static EditBone *editbone_get_child(bArmature *arm, EditBone *pabone, short use_visibility)
841 {
842         EditBone *curbone, *chbone = NULL;
843         
844         for (curbone = arm->edbo->first; curbone; curbone = curbone->next) {
845                 if (curbone->parent == pabone) {
846                         if (use_visibility) {
847                                 if ((arm->layer & curbone->layer) && !(pabone->flag & BONE_HIDDEN_A)) {
848                                         chbone = curbone;
849                                 }
850                         }
851                         else
852                                 chbone = curbone;
853                 }
854         }
855         
856         return chbone;
857 }
858
859 static int armature_select_hierarchy_exec(bContext *C, wmOperator *op)
860 {
861         Object *obedit = CTX_data_edit_object(C);
862         Object *ob;
863         bArmature *arm;
864         EditBone *curbone, *pabone, *chbone;
865         int direction = RNA_enum_get(op->ptr, "direction");
866         int add_to_sel = RNA_boolean_get(op->ptr, "extend");
867         
868         ob = obedit;
869         arm = (bArmature *)ob->data;
870         
871         for (curbone = arm->edbo->first; curbone; curbone = curbone->next) {
872                 /* only work on bone if it is visible and its selection can change */
873                 if (EBONE_SELECTABLE(arm, curbone)) {
874                         if (curbone == arm->act_edbone) {
875                                 if (direction == BONE_SELECT_PARENT) {
876                                         if (curbone->parent == NULL) continue;
877                                         else pabone = curbone->parent;
878                                         
879                                         if (EBONE_VISIBLE(arm, pabone)) {
880                                                 pabone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
881                                                 arm->act_edbone = pabone;
882                                                 if (pabone->parent) pabone->parent->flag |= BONE_TIPSEL;
883                                                 
884                                                 if (!add_to_sel) curbone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
885                                                 break;
886                                         }
887                                         
888                                 }
889                                 else { // BONE_SELECT_CHILD
890                                         chbone = editbone_get_child(arm, curbone, 1);
891                                         if (chbone == NULL) continue;
892                                         
893                                         if (EBONE_SELECTABLE(arm, chbone)) {
894                                                 chbone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
895                                                 arm->act_edbone = chbone;
896                                                 
897                                                 if (!add_to_sel) {
898                                                         curbone->flag &= ~(BONE_SELECTED | BONE_ROOTSEL);
899                                                         if (curbone->parent) curbone->parent->flag &= ~BONE_TIPSEL;
900                                                 }
901                                                 break;
902                                         }
903                                 }
904                         }
905                 }
906         }
907         
908         ED_armature_sync_selection(arm->edbo);
909         
910         WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob);
911         
912         return OPERATOR_FINISHED;
913 }
914
915 void ARMATURE_OT_select_hierarchy(wmOperatorType *ot)
916 {
917         static EnumPropertyItem direction_items[] = {
918                 {BONE_SELECT_PARENT, "PARENT", 0, "Select Parent", ""},
919                 {BONE_SELECT_CHILD, "CHILD", 0, "Select Child", ""},
920                 {0, NULL, 0, NULL, NULL}
921         };
922         
923         /* identifiers */
924         ot->name = "Select Hierarchy";
925         ot->idname = "ARMATURE_OT_select_hierarchy";
926         ot->description = "Select immediate parent/children of selected bones";
927         
928         /* api callbacks */
929         ot->exec = armature_select_hierarchy_exec;
930         ot->poll = ED_operator_editarmature;
931         
932         /* flags */
933         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
934
935         /* props */
936         RNA_def_enum(ot->srna, "direction", direction_items,
937                      BONE_SELECT_PARENT, "Direction", "");
938         RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
939 }
940