aed074907b71f4f339aac66b45e6fa385365a2aa
[blender.git] / source / blender / editors / object / object_select.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17  * All rights reserved.
18  */
19
20 /** \file \ingroup edobj
21  */
22
23
24 #include <ctype.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include "DNA_anim_types.h"
30 #include "DNA_collection_types.h"
31 #include "DNA_material_types.h"
32 #include "DNA_modifier_types.h"
33 #include "DNA_scene_types.h"
34 #include "DNA_armature_types.h"
35 #include "DNA_lamp_types.h"
36 #include "DNA_workspace_types.h"
37 #include "DNA_gpencil_types.h"
38
39 #include "BLI_math.h"
40 #include "BLI_math_bits.h"
41 #include "BLI_listbase.h"
42 #include "BLI_rand.h"
43 #include "BLI_string_utils.h"
44 #include "BLI_utildefines.h"
45
46 #include "BLT_translation.h"
47
48 #include "BKE_action.h"
49 #include "BKE_armature.h"
50 #include "BKE_collection.h"
51 #include "BKE_context.h"
52 #include "BKE_deform.h"
53 #include "BKE_layer.h"
54 #include "BKE_library.h"
55 #include "BKE_main.h"
56 #include "BKE_material.h"
57 #include "BKE_object.h"
58 #include "BKE_paint.h"
59 #include "BKE_particle.h"
60 #include "BKE_report.h"
61 #include "BKE_scene.h"
62 #include "BKE_workspace.h"
63
64 #include "DEG_depsgraph.h"
65
66 #include "WM_api.h"
67 #include "WM_types.h"
68
69 #include "ED_armature.h"
70 #include "ED_object.h"
71 #include "ED_screen.h"
72 #include "ED_select_utils.h"
73 #include "ED_keyframing.h"
74
75 #include "UI_interface.h"
76 #include "UI_resources.h"
77
78 #include "RNA_access.h"
79 #include "RNA_define.h"
80 #include "RNA_enum_types.h"
81
82 #include "object_intern.h"
83
84 /************************ Exported **************************/
85
86 /* simple API for object selection, rather than just using the flag
87  * this takes into account the 'restrict selection in 3d view' flag.
88  * deselect works always, the restriction just prevents selection */
89
90  /* Note: send a NC_SCENE|ND_OB_SELECT notifier yourself! (or
91   * or a NC_SCENE|ND_OB_VISIBLE in case of visibility toggling */
92
93 void ED_object_base_select(Base *base, eObjectSelect_Mode mode)
94 {
95         if (mode == BA_INVERT) {
96                 mode = (base->flag & BASE_SELECTED) != 0 ? BA_DESELECT : BA_SELECT;
97         }
98
99         if (base) {
100                 switch (mode) {
101                         case BA_SELECT:
102                                 if ((base->flag & BASE_SELECTABLE) != 0) {
103                                         base->flag |= BASE_SELECTED;
104                                 }
105                                 break;
106                         case BA_DESELECT:
107                                 base->flag &= ~BASE_SELECTED;
108                                 break;
109                         case BA_INVERT:
110                                 /* Never happens. */
111                                 break;
112                 }
113                 BKE_scene_object_base_flag_sync_from_base(base);
114         }
115 }
116
117 /**
118  * Change active base, it includes the notifier
119  */
120 void ED_object_base_activate(bContext *C, Base *base)
121 {
122         Scene *scene = CTX_data_scene(C);
123         ViewLayer *view_layer = CTX_data_view_layer(C);
124         view_layer->basact = base;
125
126         WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
127         DEG_id_tag_update(&CTX_data_scene(C)->id, ID_RECALC_SELECT);
128 }
129
130 bool ED_object_base_deselect_all_ex(ViewLayer *view_layer, View3D *v3d, int action, bool *r_any_visible)
131 {
132         if (action == SEL_TOGGLE) {
133                 action = SEL_SELECT;
134                 FOREACH_VISIBLE_BASE_BEGIN(view_layer, v3d, base) {
135                         if (v3d && ((v3d->object_type_exclude_select & (1 << base->object->type)) != 0)) {
136                                 continue;
137                         }
138                         if ((base->flag & BASE_SELECTED) != 0) {
139                                 action = SEL_DESELECT;
140                                 break;
141                         }
142                 }
143                 FOREACH_VISIBLE_BASE_END;
144         }
145
146         bool any_visible = false;
147         bool changed = false;
148         FOREACH_VISIBLE_BASE_BEGIN(view_layer, v3d, base) {
149                 if (v3d && ((v3d->object_type_exclude_select & (1 << base->object->type)) != 0)) {
150                         continue;
151                 }
152                 switch (action) {
153                         case SEL_SELECT:
154                                 if ((base->flag & BASE_SELECTED) == 0) {
155                                         ED_object_base_select(base, BA_SELECT);
156                                         changed = true;
157                                 }
158                                 break;
159                         case SEL_DESELECT:
160                                 if ((base->flag & BASE_SELECTED) != 0) {
161                                         ED_object_base_select(base, BA_DESELECT);
162                                         changed = true;
163                                 }
164                                 break;
165                         case SEL_INVERT:
166                                 if ((base->flag & BASE_SELECTED) != 0) {
167                                         ED_object_base_select(base, BA_DESELECT);
168                                         changed = true;
169                                 }
170                                 else {
171                                         ED_object_base_select(base, BA_SELECT);
172                                         changed = true;
173                                 }
174                                 break;
175                 }
176                 any_visible = true;
177         }
178         FOREACH_VISIBLE_BASE_END;
179         if (r_any_visible) {
180                 *r_any_visible = any_visible;
181         }
182         return changed;
183 }
184
185
186 bool ED_object_base_deselect_all(ViewLayer *view_layer, View3D *v3d, int action)
187 {
188         return ED_object_base_deselect_all_ex(view_layer, v3d, action, NULL);
189 }
190
191 /********************** Jump To Object Utilities **********************/
192
193 static int get_base_select_priority(Base *base)
194 {
195         if (base->flag & BASE_VISIBLE) {
196                 if (base->flag & BASE_SELECTABLE) {
197                         return 3;
198                 }
199                 else {
200                         return 2;
201                 }
202         }
203         else {
204                 return 1;
205         }
206 }
207
208 /**
209  * If id is not already an Object, try to find an object that uses it as data.
210  * Prefers active, then selected, then visible/selectable.
211  */
212 Base *ED_object_find_first_by_data_id(ViewLayer *view_layer, ID *id)
213 {
214         BLI_assert(OB_DATA_SUPPORT_ID(GS(id->name)));
215
216         /* Try active object. */
217         Base *basact = view_layer->basact;
218
219         if (basact && basact->object && basact->object->data == id) {
220                 return basact;
221         }
222
223         /* Try all objects. */
224         Base *base_best = NULL;
225         int priority_best = 0;
226
227         for (Base *base = view_layer->object_bases.first; base; base = base->next) {
228                 if (base->object && base->object->data == id) {
229                         if (base->flag & BASE_SELECTED) {
230                                 return base;
231                         }
232                         else {
233                                 int priority_test = get_base_select_priority(base);
234
235                                 if (priority_test > priority_best) {
236                                         priority_best = priority_test;
237                                         base_best = base;
238                                 }
239                         }
240                 }
241         }
242
243         return base_best;
244 }
245
246 /**
247  * Select and make the target object active in the view layer.
248  * If already selected, selection isn't changed.
249  *
250  * \returns false if not found in current view layer
251  */
252 bool ED_object_jump_to_object(
253         bContext *C, Object *ob, const bool UNUSED(reveal_hidden))
254 {
255         ViewLayer *view_layer = CTX_data_view_layer(C);
256         View3D *v3d = CTX_wm_view3d(C);
257         Base *base = BKE_view_layer_base_find(view_layer, ob);
258
259         if (base == NULL) {
260                 return false;
261         }
262
263         /* TODO, use 'reveal_hidden', as is done with bones. */
264
265         if (view_layer->basact != base || !(base->flag & BASE_SELECTED)) {
266                 /* Select if not selected. */
267                 if (!(base->flag & BASE_SELECTED)) {
268                         ED_object_base_deselect_all(view_layer, v3d, SEL_DESELECT);
269
270                         if (base->flag & BASE_VISIBLE) {
271                                 ED_object_base_select(base, BA_SELECT);
272                         }
273
274                         WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, CTX_data_scene(C));
275                 }
276
277                 /* Make active if not active. */
278                 ED_object_base_activate(C, base);
279         }
280
281         return true;
282 }
283
284 /**
285  * Select and make the target object and bone active.
286  * Switches to Pose mode if in Object mode so the selection is visible.
287  * Unhides the target bone and bone layer if necessary.
288  *
289  * \returns false if object not in layer, bone not found, or other error
290  */
291 bool ED_object_jump_to_bone(
292         bContext *C, Object *ob, const char *bone_name,
293         const bool reveal_hidden)
294 {
295         /* Verify it's a valid armature object. */
296         if (ob == NULL || ob->type != OB_ARMATURE) {
297                 return false;
298         }
299
300         bArmature *arm = ob->data;
301
302         /* Activate the armature object. */
303         if (!ED_object_jump_to_object(C, ob, reveal_hidden)) {
304                 return false;
305         }
306
307         /* Switch to pose mode from object mode. */
308         if (!ELEM(ob->mode, OB_MODE_EDIT, OB_MODE_POSE)) {
309                 ED_object_mode_set(C, OB_MODE_POSE);
310         }
311
312         if (ob->mode == OB_MODE_EDIT && arm->edbo != NULL) {
313                 /* In Edit mode select and activate the target Edit-Bone. */
314                 EditBone *ebone = ED_armature_ebone_find_name(arm->edbo, bone_name);
315                 if (ebone != NULL) {
316                         if (reveal_hidden) {
317                                 /* Unhide the bone. */
318                                 ebone->flag &= ~BONE_HIDDEN_A;
319
320                                 if ((arm->layer & ebone->layer) == 0) {
321                                         arm->layer |= 1U << bitscan_forward_uint(ebone->layer);
322                                 }
323                         }
324
325                         /* Select it. */
326                         ED_armature_edit_deselect_all(ob);
327
328                         if (EBONE_SELECTABLE(arm, ebone)) {
329                                 ED_armature_ebone_select_set(ebone, true);
330                                 ED_armature_edit_sync_selection(arm->edbo);
331                         }
332
333                         arm->act_edbone = ebone;
334
335                         ED_pose_bone_select_tag_update(ob);
336                         return true;
337                 }
338         }
339         else if (ob->mode == OB_MODE_POSE && ob->pose != NULL) {
340                 /* In Pose mode select and activate the target Bone/Pose-Channel. */
341                 bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, bone_name);
342                 if (pchan != NULL) {
343                         if (reveal_hidden) {
344                                 /* Unhide the bone. */
345                                 pchan->bone->flag &= ~BONE_HIDDEN_P;
346
347                                 if ((arm->layer & pchan->bone->layer) == 0) {
348                                         arm->layer |= 1U << bitscan_forward_uint(pchan->bone->layer);
349                                 }
350                         }
351
352                         /* Select it. */
353                         ED_pose_deselect_all(ob, SEL_DESELECT, true);
354                         ED_pose_bone_select(ob, pchan, true);
355
356                         arm->act_bone = pchan->bone;
357
358                         ED_pose_bone_select_tag_update(ob);
359                         return true;
360                 }
361         }
362
363         return false;
364 }
365
366 /********************** Selection Operators **********************/
367
368 static bool objects_selectable_poll(bContext *C)
369 {
370         /* we don't check for linked scenes here, selection is
371          * still allowed then for inspection of scene */
372         Object *obact = CTX_data_active_object(C);
373
374         if (CTX_data_edit_object(C))
375                 return 0;
376         if (obact && obact->mode)
377                 return 0;
378
379         return 1;
380 }
381
382 /************************ Select by Type *************************/
383
384 static int object_select_by_type_exec(bContext *C, wmOperator *op)
385 {
386         ViewLayer *view_layer = CTX_data_view_layer(C);
387         View3D *v3d = CTX_wm_view3d(C);
388         short obtype, extend;
389
390         obtype = RNA_enum_get(op->ptr, "type");
391         extend = RNA_boolean_get(op->ptr, "extend");
392
393         if (extend == 0) {
394                 ED_object_base_deselect_all(view_layer, v3d, SEL_DESELECT);
395         }
396
397         CTX_DATA_BEGIN (C, Base *, base, visible_bases)
398         {
399                 if (base->object->type == obtype) {
400                         ED_object_base_select(base, BA_SELECT);
401                 }
402         }
403         CTX_DATA_END;
404
405         Scene *scene = CTX_data_scene(C);
406         DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
407         WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
408
409         return OPERATOR_FINISHED;
410 }
411
412 void OBJECT_OT_select_by_type(wmOperatorType *ot)
413 {
414         /* identifiers */
415         ot->name = "Select By Type";
416         ot->description = "Select all visible objects that are of a type";
417         ot->idname = "OBJECT_OT_select_by_type";
418
419         /* api callbacks */
420         ot->invoke = WM_menu_invoke;
421         ot->exec = object_select_by_type_exec;
422         ot->poll = objects_selectable_poll;
423
424         /* flags */
425         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
426
427         /* properties */
428         RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend selection instead of deselecting everything first");
429         ot->prop = RNA_def_enum(ot->srna, "type", rna_enum_object_type_items, 1, "Type", "");
430         RNA_def_property_translation_context(ot->prop, BLT_I18NCONTEXT_ID_ID);
431 }
432
433 /*********************** Selection by Links *********************/
434
435 enum {
436         OBJECT_SELECT_LINKED_IPO = 1,
437         OBJECT_SELECT_LINKED_OBDATA,
438         OBJECT_SELECT_LINKED_MATERIAL,
439         OBJECT_SELECT_LINKED_DUPGROUP,
440         OBJECT_SELECT_LINKED_PARTICLE,
441         OBJECT_SELECT_LINKED_LIBRARY,
442         OBJECT_SELECT_LINKED_LIBRARY_OBDATA,
443 };
444
445 static const EnumPropertyItem prop_select_linked_types[] = {
446         //{OBJECT_SELECT_LINKED_IPO, "IPO", 0, "Object IPO", ""}, // XXX deprecated animation system stuff...
447         {OBJECT_SELECT_LINKED_OBDATA, "OBDATA", 0, "Object Data", ""},
448         {OBJECT_SELECT_LINKED_MATERIAL, "MATERIAL", 0, "Material", ""},
449         {OBJECT_SELECT_LINKED_DUPGROUP, "DUPGROUP", 0, "Instanced Collection", ""},
450         {OBJECT_SELECT_LINKED_PARTICLE, "PARTICLE", 0, "Particle System", ""},
451         {OBJECT_SELECT_LINKED_LIBRARY, "LIBRARY", 0, "Library", ""},
452         {OBJECT_SELECT_LINKED_LIBRARY_OBDATA, "LIBRARY_OBDATA", 0, "Library (Object Data)", ""},
453         {0, NULL, 0, NULL, NULL},
454 };
455
456 static bool object_select_all_by_obdata(bContext *C, void *obdata)
457 {
458         bool changed = false;
459
460         CTX_DATA_BEGIN (C, Base *, base, visible_bases)
461         {
462                 if (((base->flag & BASE_SELECTED) == 0) && ((base->flag & BASE_SELECTABLE) != 0)) {
463                         if (base->object->data == obdata) {
464                                 ED_object_base_select(base, BA_SELECT);
465                                 changed = true;
466                         }
467                 }
468         }
469         CTX_DATA_END;
470
471         return changed;
472 }
473
474 static bool object_select_all_by_material(bContext *C, Material *mat)
475 {
476         bool changed = false;
477
478         CTX_DATA_BEGIN (C, Base *, base, visible_bases)
479         {
480                 if (((base->flag & BASE_SELECTED) == 0) && ((base->flag & BASE_SELECTABLE) != 0)) {
481                         Object *ob = base->object;
482                         Material *mat1;
483                         int a;
484
485                         for (a = 1; a <= ob->totcol; a++) {
486                                 mat1 = give_current_material(ob, a);
487
488                                 if (mat1 == mat) {
489                                         ED_object_base_select(base, BA_SELECT);
490                                         changed = true;
491                                 }
492                         }
493                 }
494         }
495         CTX_DATA_END;
496
497         return changed;
498 }
499
500 static bool object_select_all_by_dup_group(bContext *C, Object *ob)
501 {
502         bool changed = false;
503         Collection *dup_group = (ob->transflag & OB_DUPLICOLLECTION) ? ob->dup_group : NULL;
504
505         CTX_DATA_BEGIN (C, Base *, base, visible_bases)
506         {
507                 if (((base->flag & BASE_SELECTED) == 0) && ((base->flag & BASE_SELECTABLE) != 0)) {
508                         Collection *dup_group_other = (base->object->transflag & OB_DUPLICOLLECTION) ? base->object->dup_group : NULL;
509                         if (dup_group == dup_group_other) {
510                                 ED_object_base_select(base, BA_SELECT);
511                                 changed = true;
512                         }
513                 }
514         }
515         CTX_DATA_END;
516
517         return changed;
518 }
519
520 static bool object_select_all_by_particle(bContext *C, Object *ob)
521 {
522         ParticleSystem *psys_act = psys_get_current(ob);
523         bool changed = false;
524
525         CTX_DATA_BEGIN (C, Base *, base, visible_bases)
526         {
527                 if (((base->flag & BASE_SELECTED) == 0) && ((base->flag & BASE_SELECTABLE) != 0)) {
528                         /* loop through other particles*/
529                         ParticleSystem *psys;
530
531                         for (psys = base->object->particlesystem.first; psys; psys = psys->next) {
532                                 if (psys->part == psys_act->part) {
533                                         ED_object_base_select(base, BA_SELECT);
534                                         changed = true;
535                                         break;
536                                 }
537
538                                 if (base->flag & BASE_SELECTED) {
539                                         break;
540                                 }
541                         }
542                 }
543         }
544         CTX_DATA_END;
545
546         return changed;
547 }
548
549 static bool object_select_all_by_library(bContext *C, Library *lib)
550 {
551         bool changed = false;
552
553         CTX_DATA_BEGIN (C, Base *, base, visible_bases)
554         {
555                 if (((base->flag & BASE_SELECTED) == 0) && ((base->flag & BASE_SELECTABLE) != 0)) {
556                         if (lib == base->object->id.lib) {
557                                 ED_object_base_select(base, BA_SELECT);
558                                 changed = true;
559                         }
560                 }
561         }
562         CTX_DATA_END;
563
564         return changed;
565 }
566
567 static bool object_select_all_by_library_obdata(bContext *C, Library *lib)
568 {
569         bool changed = false;
570
571         CTX_DATA_BEGIN (C, Base *, base, visible_bases)
572         {
573                 if (((base->flag & BASE_SELECTED) == 0) && ((base->flag & BASE_SELECTABLE) != 0)) {
574                         if (base->object->data && lib == ((ID *)base->object->data)->lib) {
575                                 ED_object_base_select(base, BA_SELECT);
576                                 changed = true;
577                         }
578                 }
579         }
580         CTX_DATA_END;
581
582         return changed;
583 }
584
585 void ED_object_select_linked_by_id(bContext *C, ID *id)
586 {
587         int idtype = GS(id->name);
588         bool changed = false;
589
590         if (OB_DATA_SUPPORT_ID(idtype)) {
591                 changed = object_select_all_by_obdata(C, id);
592         }
593         else if (idtype == ID_MA) {
594                 changed = object_select_all_by_material(C, (Material *)id);
595         }
596         else if (idtype == ID_LI) {
597                 changed = object_select_all_by_library(C, (Library *) id);
598         }
599
600         if (changed) {
601                 Scene *scene = CTX_data_scene(C);
602                 DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
603                 WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
604         }
605 }
606
607 static int object_select_linked_exec(bContext *C, wmOperator *op)
608 {
609         Scene *scene = CTX_data_scene(C);
610         ViewLayer *view_layer = CTX_data_view_layer(C);
611         View3D *v3d = CTX_wm_view3d(C);
612         Object *ob;
613         int nr = RNA_enum_get(op->ptr, "type");
614         bool changed = false, extend;
615
616         extend = RNA_boolean_get(op->ptr, "extend");
617
618         if (extend == 0) {
619                 ED_object_base_deselect_all(view_layer, v3d, SEL_DESELECT);
620         }
621
622         ob = OBACT(view_layer);
623         if (ob == NULL) {
624                 BKE_report(op->reports, RPT_ERROR, "No active object");
625                 return OPERATOR_CANCELLED;
626         }
627
628         if (nr == OBJECT_SELECT_LINKED_IPO) {
629                 // XXX old animation system
630                 //if (ob->ipo == 0) return OPERATOR_CANCELLED;
631                 //object_select_all_by_ipo(C, ob->ipo)
632                 return OPERATOR_CANCELLED;
633         }
634         else if (nr == OBJECT_SELECT_LINKED_OBDATA) {
635                 if (ob->data == NULL)
636                         return OPERATOR_CANCELLED;
637
638                 changed = object_select_all_by_obdata(C, ob->data);
639         }
640         else if (nr == OBJECT_SELECT_LINKED_MATERIAL) {
641                 Material *mat = NULL;
642
643                 mat = give_current_material(ob, ob->actcol);
644                 if (mat == NULL) return OPERATOR_CANCELLED;
645
646                 changed = object_select_all_by_material(C, mat);
647         }
648         else if (nr == OBJECT_SELECT_LINKED_DUPGROUP) {
649                 if (ob->dup_group == NULL)
650                         return OPERATOR_CANCELLED;
651
652                 changed = object_select_all_by_dup_group(C, ob);
653         }
654         else if (nr == OBJECT_SELECT_LINKED_PARTICLE) {
655                 if (BLI_listbase_is_empty(&ob->particlesystem))
656                         return OPERATOR_CANCELLED;
657
658                 changed = object_select_all_by_particle(C, ob);
659         }
660         else if (nr == OBJECT_SELECT_LINKED_LIBRARY) {
661                 /* do nothing */
662                 changed = object_select_all_by_library(C, ob->id.lib);
663         }
664         else if (nr == OBJECT_SELECT_LINKED_LIBRARY_OBDATA) {
665                 if (ob->data == NULL)
666                         return OPERATOR_CANCELLED;
667
668                 changed = object_select_all_by_library_obdata(C, ((ID *) ob->data)->lib);
669         }
670         else
671                 return OPERATOR_CANCELLED;
672
673         if (changed) {
674                 DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
675                 WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
676                 return OPERATOR_FINISHED;
677         }
678
679         return OPERATOR_CANCELLED;
680 }
681
682 void OBJECT_OT_select_linked(wmOperatorType *ot)
683 {
684         /* identifiers */
685         ot->name = "Select Linked";
686         ot->description = "Select all visible objects that are linked";
687         ot->idname = "OBJECT_OT_select_linked";
688
689         /* api callbacks */
690         ot->invoke = WM_menu_invoke;
691         ot->exec = object_select_linked_exec;
692         ot->poll = objects_selectable_poll;
693
694         /* flags */
695         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
696
697         /* properties */
698         RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend selection instead of deselecting everything first");
699         ot->prop = RNA_def_enum(ot->srna, "type", prop_select_linked_types, 0, "Type", "");
700 }
701
702 /*********************** Selected Grouped ********************/
703
704 enum {
705         OBJECT_GRPSEL_CHILDREN_RECURSIVE =  0,
706         OBJECT_GRPSEL_CHILDREN           =  1,
707         OBJECT_GRPSEL_PARENT             =  2,
708         OBJECT_GRPSEL_SIBLINGS           =  3,
709         OBJECT_GRPSEL_TYPE               =  4,
710         OBJECT_GRPSEL_COLLECTION         =  5,
711         OBJECT_GRPSEL_HOOK               =  7,
712         OBJECT_GRPSEL_PASS               =  8,
713         OBJECT_GRPSEL_COLOR              =  9,
714         OBJECT_GRPSEL_KEYINGSET          = 10,
715         OBJECT_GRPSEL_LIGHT_TYPE          = 11,
716 };
717
718 static const EnumPropertyItem prop_select_grouped_types[] = {
719         {OBJECT_GRPSEL_CHILDREN_RECURSIVE, "CHILDREN_RECURSIVE", 0, "Children", ""},
720         {OBJECT_GRPSEL_CHILDREN, "CHILDREN", 0, "Immediate Children", ""},
721         {OBJECT_GRPSEL_PARENT, "PARENT", 0, "Parent", ""},
722         {OBJECT_GRPSEL_SIBLINGS, "SIBLINGS", 0, "Siblings", "Shared Parent"},
723         {OBJECT_GRPSEL_TYPE, "TYPE", 0, "Type", "Shared object type"},
724         {OBJECT_GRPSEL_COLLECTION, "COLLECTION", 0, "Collection", "Shared collection"},
725         {OBJECT_GRPSEL_HOOK, "HOOK", 0, "Hook", ""},
726         {OBJECT_GRPSEL_PASS, "PASS", 0, "Pass", "Render pass Index"},
727         {OBJECT_GRPSEL_COLOR, "COLOR", 0, "Color", "Object Color"},
728         {OBJECT_GRPSEL_KEYINGSET, "KEYINGSET", 0, "Keying Set", "Objects included in active Keying Set"},
729         {OBJECT_GRPSEL_LIGHT_TYPE, "LIGHT_TYPE", 0, "Light Type", "Matching light types"},
730         {0, NULL, 0, NULL, NULL},
731 };
732
733 static bool select_grouped_children(bContext *C, Object *ob, const bool recursive)
734 {
735         bool changed = false;
736
737         CTX_DATA_BEGIN (C, Base *, base, selectable_bases)
738         {
739                 if (ob == base->object->parent) {
740                         if ((base->flag & BASE_SELECTED) == 0) {
741                                 ED_object_base_select(base, BA_SELECT);
742                                 changed = true;
743                         }
744
745                         if (recursive) {
746                                 changed |= select_grouped_children(C, base->object, 1);
747                         }
748                 }
749         }
750         CTX_DATA_END;
751         return changed;
752 }
753
754 static bool select_grouped_parent(bContext *C) /* Makes parent active and de-selected OBACT */
755 {
756         ViewLayer *view_layer = CTX_data_view_layer(C);
757         View3D *v3d = CTX_wm_view3d(C);
758         Base *baspar, *basact = CTX_data_active_base(C);
759         bool changed = false;
760
761         if (!basact || !(basact->object->parent)) {
762                 return 0;  /* we know OBACT is valid */
763         }
764
765         baspar = BKE_view_layer_base_find(view_layer, basact->object->parent);
766
767         /* can be NULL if parent in other scene */
768         if (baspar && BASE_SELECTABLE(v3d, baspar)) {
769                 ED_object_base_select(baspar, BA_SELECT);
770                 ED_object_base_activate(C, baspar);
771                 changed = true;
772         }
773         return changed;
774 }
775
776
777 #define COLLECTION_MENU_MAX  24
778 /* Select objects in the same group as the active */
779 static bool select_grouped_collection(bContext *C, Object *ob)
780 {
781         bool changed = false;
782         Collection *collection, *ob_collections[COLLECTION_MENU_MAX];
783         int collection_count = 0, i;
784         uiPopupMenu *pup;
785         uiLayout *layout;
786
787         for (collection = CTX_data_main(C)->collection.first; collection && collection_count < COLLECTION_MENU_MAX; collection = collection->id.next) {
788                 if (BKE_collection_has_object(collection, ob)) {
789                         ob_collections[collection_count] = collection;
790                         collection_count++;
791                 }
792         }
793
794         if (!collection_count)
795                 return 0;
796         else if (collection_count == 1) {
797                 collection = ob_collections[0];
798                 CTX_DATA_BEGIN (C, Base *, base, visible_bases)
799                 {
800                         if (((base->flag & BASE_SELECTED) == 0) && ((base->flag & BASE_SELECTABLE) != 0)) {
801                                 if (BKE_collection_has_object(collection, base->object)) {
802                                         ED_object_base_select(base, BA_SELECT);
803                                         changed = true;
804                                 }
805                         }
806                 }
807                 CTX_DATA_END;
808                 return changed;
809         }
810
811         /* build the menu. */
812         pup = UI_popup_menu_begin(C, IFACE_("Select Collection"), ICON_NONE);
813         layout = UI_popup_menu_layout(pup);
814
815         for (i = 0; i < collection_count; i++) {
816                 collection = ob_collections[i];
817                 uiItemStringO(layout, collection->id.name + 2, 0, "OBJECT_OT_select_same_collection", "collection", collection->id.name + 2);
818         }
819
820         UI_popup_menu_end(C, pup);
821         return changed;  /* The operator already handle this! */
822 }
823
824 static bool select_grouped_object_hooks(bContext *C, Object *ob)
825 {
826         ViewLayer *view_layer = CTX_data_view_layer(C);
827         View3D *v3d = CTX_wm_view3d(C);
828
829         bool changed = false;
830         Base *base;
831         ModifierData *md;
832         HookModifierData *hmd;
833
834         for (md = ob->modifiers.first; md; md = md->next) {
835                 if (md->type == eModifierType_Hook) {
836                         hmd = (HookModifierData *) md;
837                         if (hmd->object) {
838                                 base = BKE_view_layer_base_find(view_layer, hmd->object);
839                                 if (base && ((base->flag & BASE_SELECTED) == 0) && (BASE_SELECTABLE(v3d, base))) {
840                                         ED_object_base_select(base, BA_SELECT);
841                                         changed = true;
842                                 }
843                         }
844                 }
845         }
846         return changed;
847 }
848
849 /* Select objects with the same parent as the active (siblings),
850  * parent can be NULL also */
851 static bool select_grouped_siblings(bContext *C, Object *ob)
852 {
853         bool changed = false;
854
855         CTX_DATA_BEGIN (C, Base *, base, selectable_bases)
856         {
857                 if ((base->object->parent == ob->parent) && ((base->flag & BASE_SELECTED) == 0)) {
858                         ED_object_base_select(base, BA_SELECT);
859                         changed = true;
860                 }
861         }
862         CTX_DATA_END;
863         return changed;
864 }
865 static bool select_grouped_lamptype(bContext *C, Object *ob)
866 {
867         Lamp *la = ob->data;
868
869         bool changed = false;
870
871         CTX_DATA_BEGIN (C, Base *, base, selectable_bases)
872         {
873                 if (base->object->type == OB_LAMP) {
874                         Lamp *la_test = base->object->data;
875                         if ((la->type == la_test->type) && ((base->flag & BASE_SELECTED) == 0)) {
876                                 ED_object_base_select(base, BA_SELECT);
877                                 changed = true;
878                         }
879                 }
880         }
881         CTX_DATA_END;
882         return changed;
883 }
884 static bool select_grouped_type(bContext *C, Object *ob)
885 {
886         bool changed = false;
887
888         CTX_DATA_BEGIN (C, Base *, base, selectable_bases)
889         {
890                 if ((base->object->type == ob->type) && ((base->flag & BASE_SELECTED) == 0)) {
891                         ED_object_base_select(base, BA_SELECT);
892                         changed = true;
893                 }
894         }
895         CTX_DATA_END;
896         return changed;
897 }
898
899 static bool select_grouped_index_object(bContext *C, Object *ob)
900 {
901         bool changed = false;
902
903         CTX_DATA_BEGIN (C, Base *, base, selectable_bases)
904         {
905                 if ((base->object->index == ob->index) && ((base->flag & BASE_SELECTED) == 0)) {
906                         ED_object_base_select(base, BA_SELECT);
907                         changed = true;
908                 }
909         }
910         CTX_DATA_END;
911         return changed;
912 }
913
914 static bool select_grouped_color(bContext *C, Object *ob)
915 {
916         bool changed = false;
917
918         CTX_DATA_BEGIN (C, Base *, base, selectable_bases)
919         {
920                 if (((base->flag & BASE_SELECTED) == 0) && (compare_v3v3(base->object->col, ob->col, 0.005f))) {
921                         ED_object_base_select(base, BA_SELECT);
922                         changed = true;
923                 }
924         }
925         CTX_DATA_END;
926         return changed;
927 }
928
929 static bool select_grouped_keyingset(bContext *C, Object *UNUSED(ob), ReportList *reports)
930 {
931         KeyingSet *ks = ANIM_scene_get_active_keyingset(CTX_data_scene(C));
932         bool changed = false;
933
934         /* firstly, validate KeyingSet */
935         if (ks == NULL) {
936                 BKE_report(reports, RPT_ERROR, "No active Keying Set to use");
937                 return false;
938         }
939         else if (ANIM_validate_keyingset(C, NULL, ks) != 0) {
940                 if (ks->paths.first == NULL) {
941                         if ((ks->flag & KEYINGSET_ABSOLUTE) == 0) {
942                                 BKE_report(reports, RPT_ERROR,
943                                            "Use another Keying Set, as the active one depends on the currently "
944                                            "selected objects or cannot find any targets due to unsuitable context");
945                         }
946                         else {
947                                 BKE_report(reports, RPT_ERROR, "Keying Set does not contain any paths");
948                         }
949                 }
950                 return false;
951         }
952
953         /* select each object that Keying Set refers to */
954         /* TODO: perhaps to be more in line with the rest of these, we should only take objects
955          * if the passed in object is included in this too */
956         CTX_DATA_BEGIN (C, Base *, base, selectable_bases)
957         {
958                 /* only check for this object if it isn't selected already, to limit time wasted */
959                 if ((base->flag & BASE_SELECTED) == 0) {
960                         KS_Path *ksp;
961
962                         /* this is the slow way... we could end up with > 500 items here,
963                          * with none matching, but end up doing this on 1000 objects...
964                          */
965                         for (ksp = ks->paths.first; ksp; ksp = ksp->next) {
966                                 /* if id matches, select then stop looping (match found) */
967                                 if (ksp->id == (ID *)base->object) {
968                                         ED_object_base_select(base, BA_SELECT);
969                                         changed = true;
970                                         break;
971                                 }
972                         }
973                 }
974         }
975         CTX_DATA_END;
976
977         return changed;
978 }
979
980 static int object_select_grouped_exec(bContext *C, wmOperator *op)
981 {
982         Scene *scene = CTX_data_scene(C);
983         ViewLayer *view_layer = CTX_data_view_layer(C);
984         View3D *v3d = CTX_wm_view3d(C);
985         Object *ob;
986         const int type = RNA_enum_get(op->ptr, "type");
987         bool changed = false, extend;
988
989         extend = RNA_boolean_get(op->ptr, "extend");
990
991         if (extend == 0) {
992                 changed = ED_object_base_deselect_all(view_layer, v3d, SEL_DESELECT);
993         }
994
995         ob = OBACT(view_layer);
996         if (ob == NULL) {
997                 BKE_report(op->reports, RPT_ERROR, "No active object");
998                 return OPERATOR_CANCELLED;
999         }
1000
1001         switch (type) {
1002                 case OBJECT_GRPSEL_CHILDREN_RECURSIVE:
1003                         changed |= select_grouped_children(C, ob, true);
1004                         break;
1005                 case OBJECT_GRPSEL_CHILDREN:
1006                         changed |= select_grouped_children(C, ob, false);
1007                         break;
1008                 case OBJECT_GRPSEL_PARENT:
1009                         changed |= select_grouped_parent(C);
1010                         break;
1011                 case OBJECT_GRPSEL_SIBLINGS:
1012                         changed |= select_grouped_siblings(C, ob);
1013                         break;
1014                 case OBJECT_GRPSEL_TYPE:
1015                         changed |= select_grouped_type(C, ob);
1016                         break;
1017                 case OBJECT_GRPSEL_COLLECTION:
1018                         changed |= select_grouped_collection(C, ob);
1019                         break;
1020                 case OBJECT_GRPSEL_HOOK:
1021                         changed |= select_grouped_object_hooks(C, ob);
1022                         break;
1023                 case OBJECT_GRPSEL_PASS:
1024                         changed |= select_grouped_index_object(C, ob);
1025                         break;
1026                 case OBJECT_GRPSEL_COLOR:
1027                         changed |= select_grouped_color(C, ob);
1028                         break;
1029                 case OBJECT_GRPSEL_KEYINGSET:
1030                         changed |= select_grouped_keyingset(C, ob, op->reports);
1031                         break;
1032                 case OBJECT_GRPSEL_LIGHT_TYPE:
1033                         if (ob->type != OB_LAMP) {
1034                                 BKE_report(op->reports, RPT_ERROR, "Active object must be a light");
1035                                 break;
1036                         }
1037                         changed |= select_grouped_lamptype(C, ob);
1038                         break;
1039                 default:
1040                         break;
1041         }
1042
1043         if (changed) {
1044                 DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
1045                 WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
1046                 return OPERATOR_FINISHED;
1047         }
1048
1049         return OPERATOR_CANCELLED;
1050 }
1051
1052 void OBJECT_OT_select_grouped(wmOperatorType *ot)
1053 {
1054         /* identifiers */
1055         ot->name = "Select Grouped";
1056         ot->description = "Select all visible objects grouped by various properties";
1057         ot->idname = "OBJECT_OT_select_grouped";
1058
1059         /* api callbacks */
1060         ot->invoke = WM_menu_invoke;
1061         ot->exec = object_select_grouped_exec;
1062         ot->poll = objects_selectable_poll;
1063
1064         /* flags */
1065         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1066
1067         /* properties */
1068         RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend selection instead of deselecting everything first");
1069         ot->prop = RNA_def_enum(ot->srna, "type", prop_select_grouped_types, 0, "Type", "");
1070 }
1071
1072 /**************************** (De)select All ****************************/
1073
1074 static int object_select_all_exec(bContext *C, wmOperator *op)
1075 {
1076         ViewLayer *view_layer = CTX_data_view_layer(C);
1077         View3D *v3d = CTX_wm_view3d(C);
1078         int action = RNA_enum_get(op->ptr, "action");
1079         bool any_visible = false;
1080
1081         bool changed = ED_object_base_deselect_all_ex(view_layer, v3d, action, &any_visible);
1082
1083         if (changed) {
1084                 Scene *scene = CTX_data_scene(C);
1085                 DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
1086                 WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
1087
1088                 return OPERATOR_FINISHED;
1089         }
1090         else if (any_visible == false) {
1091                 /* TODO(campbell): Looks like we could remove this,
1092                  * if not comment should say why its needed. */
1093                 return OPERATOR_PASS_THROUGH;
1094         }
1095         else {
1096                 return OPERATOR_CANCELLED;
1097         }
1098 }
1099
1100 void OBJECT_OT_select_all(wmOperatorType *ot)
1101 {
1102
1103         /* identifiers */
1104         ot->name = "(De)select All";
1105         ot->description = "Change selection of all visible objects in scene";
1106         ot->idname = "OBJECT_OT_select_all";
1107
1108         /* api callbacks */
1109         ot->exec = object_select_all_exec;
1110         ot->poll = objects_selectable_poll;
1111
1112         /* flags */
1113         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1114
1115         WM_operator_properties_select_all(ot);
1116 }
1117
1118 /**************************** Select In The Same Collection ****************************/
1119
1120 static int object_select_same_collection_exec(bContext *C, wmOperator *op)
1121 {
1122         Main *bmain = CTX_data_main(C);
1123         Collection *collection;
1124         char collection_name[MAX_ID_NAME];
1125
1126         /* passthrough if no objects are visible */
1127         if (CTX_DATA_COUNT(C, visible_bases) == 0) return OPERATOR_PASS_THROUGH;
1128
1129         RNA_string_get(op->ptr, "collection", collection_name);
1130
1131         collection = (Collection *)BKE_libblock_find_name(bmain, ID_GR, collection_name);
1132
1133         if (!collection) {
1134                 return OPERATOR_PASS_THROUGH;
1135         }
1136
1137         CTX_DATA_BEGIN (C, Base *, base, visible_bases)
1138         {
1139                 if (((base->flag & BASE_SELECTED) == 0) && ((base->flag & BASE_SELECTABLE) != 0)) {
1140                         if (BKE_collection_has_object(collection, base->object)) {
1141                                 ED_object_base_select(base, BA_SELECT);
1142                         }
1143                 }
1144         }
1145         CTX_DATA_END;
1146
1147         Scene *scene = CTX_data_scene(C);
1148         DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
1149         WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
1150
1151         return OPERATOR_FINISHED;
1152 }
1153
1154 void OBJECT_OT_select_same_collection(wmOperatorType *ot)
1155 {
1156
1157         /* identifiers */
1158         ot->name = "Select Same Collection";
1159         ot->description = "Select object in the same collection";
1160         ot->idname = "OBJECT_OT_select_same_collection";
1161
1162         /* api callbacks */
1163         ot->exec = object_select_same_collection_exec;
1164         ot->poll = objects_selectable_poll;
1165
1166         /* flags */
1167         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1168
1169         RNA_def_string(ot->srna, "collection", NULL, MAX_ID_NAME, "Collection", "Name of the collection to select");
1170 }
1171
1172 /**************************** Select Mirror ****************************/
1173 static int object_select_mirror_exec(bContext *C, wmOperator *op)
1174 {
1175         Main *bmain = CTX_data_main(C);
1176         Scene *scene = CTX_data_scene(C);
1177         ViewLayer *view_layer = CTX_data_view_layer(C);
1178         bool extend;
1179
1180         extend = RNA_boolean_get(op->ptr, "extend");
1181
1182         CTX_DATA_BEGIN (C, Base *, primbase, selected_bases)
1183         {
1184                 char name_flip[MAXBONENAME];
1185
1186                 BLI_string_flip_side_name(name_flip, primbase->object->id.name + 2, true, sizeof(name_flip));
1187
1188                 if (!STREQ(name_flip, primbase->object->id.name + 2)) {
1189                         Object *ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, name_flip);
1190                         if (ob) {
1191                                 Base *secbase = BKE_view_layer_base_find(view_layer, ob);
1192
1193                                 if (secbase) {
1194                                         ED_object_base_select(secbase, BA_SELECT);
1195                                 }
1196                         }
1197                 }
1198
1199                 if (extend == false) ED_object_base_select(primbase, BA_DESELECT);
1200
1201         }
1202         CTX_DATA_END;
1203
1204         /* undo? */
1205         DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
1206         WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
1207
1208         return OPERATOR_FINISHED;
1209 }
1210
1211 void OBJECT_OT_select_mirror(wmOperatorType *ot)
1212 {
1213
1214         /* identifiers */
1215         ot->name = "Select Mirror";
1216         ot->description = "Select the Mirror objects of the selected object eg. L.sword -> R.sword";
1217         ot->idname = "OBJECT_OT_select_mirror";
1218
1219         /* api callbacks */
1220         ot->exec = object_select_mirror_exec;
1221         ot->poll = objects_selectable_poll;
1222
1223         /* flags */
1224         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1225
1226         RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend selection instead of deselecting everything first");
1227 }
1228
1229
1230 /** \name Select More/Less
1231  * \{ */
1232
1233 static bool object_select_more_less(bContext *C, const bool select)
1234 {
1235         ViewLayer *view_layer = CTX_data_view_layer(C);
1236
1237         for (Base *base = view_layer->object_bases.first; base; base = base->next) {
1238                 Object *ob = base->object;
1239                 ob->flag &= ~OB_DONE;
1240                 ob->id.tag &= ~LIB_TAG_DOIT;
1241                 /* parent may be in another scene */
1242                 if (ob->parent) {
1243                         ob->parent->flag &= ~OB_DONE;
1244                         ob->parent->id.tag &= ~LIB_TAG_DOIT;
1245                 }
1246         }
1247
1248         ListBase ctx_base_list;
1249         CollectionPointerLink *ctx_base;
1250         CTX_data_selectable_bases(C, &ctx_base_list);
1251
1252         CTX_DATA_BEGIN (C, Object *, ob, selected_objects)
1253         {
1254                 ob->flag |= OB_DONE;
1255         }
1256         CTX_DATA_END;
1257
1258
1259
1260         for (ctx_base = ctx_base_list.first; ctx_base; ctx_base = ctx_base->next) {
1261                 Object *ob = ((Base *)ctx_base->ptr.data)->object;
1262                 if (ob->parent) {
1263                         if ((ob->flag & OB_DONE) != (ob->parent->flag & OB_DONE)) {
1264                                 ob->id.tag         |= LIB_TAG_DOIT;
1265                                 ob->parent->id.tag |= LIB_TAG_DOIT;
1266                         }
1267                 }
1268         }
1269
1270         bool changed = false;
1271         const short select_mode = select ? BA_SELECT : BA_DESELECT;
1272         const short select_flag = select ? BASE_SELECTED : 0;
1273
1274         for (ctx_base = ctx_base_list.first; ctx_base; ctx_base = ctx_base->next) {
1275                 Base *base = ctx_base->ptr.data;
1276                 Object *ob = base->object;
1277                 if ((ob->id.tag & LIB_TAG_DOIT) && ((base->flag & BASE_SELECTED) != select_flag)) {
1278                         ED_object_base_select(base, select_mode);
1279                         changed = true;
1280                 }
1281         }
1282
1283         BLI_freelistN(&ctx_base_list);
1284
1285         return changed;
1286 }
1287
1288 static int object_select_more_exec(bContext *C, wmOperator *UNUSED(op))
1289 {
1290         bool changed = object_select_more_less(C, true);
1291
1292         if (changed) {
1293                 Scene *scene = CTX_data_scene(C);
1294                 DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
1295                 WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
1296                 return OPERATOR_FINISHED;
1297         }
1298         else {
1299                 return OPERATOR_CANCELLED;
1300         }
1301 }
1302
1303 void OBJECT_OT_select_more(wmOperatorType *ot)
1304 {
1305         /* identifiers */
1306         ot->name = "Select More";
1307         ot->idname = "OBJECT_OT_select_more";
1308         ot->description = "Select connected parent/child objects";
1309
1310         /* api callbacks */
1311         ot->exec = object_select_more_exec;
1312         ot->poll = ED_operator_objectmode;
1313
1314         /* flags */
1315         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1316 }
1317
1318 static int object_select_less_exec(bContext *C, wmOperator *UNUSED(op))
1319 {
1320         bool changed = object_select_more_less(C, false);
1321
1322         if (changed) {
1323                 Scene *scene = CTX_data_scene(C);
1324                 DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
1325                 WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
1326                 return OPERATOR_FINISHED;
1327         }
1328         else {
1329                 return OPERATOR_CANCELLED;
1330         }
1331 }
1332
1333 void OBJECT_OT_select_less(wmOperatorType *ot)
1334 {
1335         /* identifiers */
1336         ot->name = "Select Less";
1337         ot->idname = "OBJECT_OT_select_less";
1338         ot->description = "Deselect objects at the boundaries of parent/child relationships";
1339
1340         /* api callbacks */
1341         ot->exec = object_select_less_exec;
1342         ot->poll = ED_operator_objectmode;
1343
1344         /* flags */
1345         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1346 }
1347
1348 /** \} */
1349
1350
1351 /**************************** Select Random ****************************/
1352
1353 static int object_select_random_exec(bContext *C, wmOperator *op)
1354 {
1355         const float randfac = RNA_float_get(op->ptr, "percent") / 100.0f;
1356         const int seed = WM_operator_properties_select_random_seed_increment_get(op);
1357         const bool select = (RNA_enum_get(op->ptr, "action") == SEL_SELECT);
1358
1359         RNG *rng = BLI_rng_new_srandom(seed);
1360
1361         CTX_DATA_BEGIN (C, Base *, base, selectable_bases)
1362         {
1363                 if (BLI_rng_get_float(rng) < randfac) {
1364                         ED_object_base_select(base, select);
1365                 }
1366         }
1367         CTX_DATA_END;
1368
1369         BLI_rng_free(rng);
1370
1371         Scene *scene = CTX_data_scene(C);
1372         DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
1373         WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
1374
1375         return OPERATOR_FINISHED;
1376 }
1377
1378 void OBJECT_OT_select_random(wmOperatorType *ot)
1379 {
1380         /* identifiers */
1381         ot->name = "Select Random";
1382         ot->description = "Set select on random visible objects";
1383         ot->idname = "OBJECT_OT_select_random";
1384
1385         /* api callbacks */
1386         /*ot->invoke = object_select_random_invoke XXX - need a number popup ;*/
1387         ot->exec = object_select_random_exec;
1388         ot->poll = objects_selectable_poll;
1389
1390         /* flags */
1391         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1392
1393         /* properties */
1394         WM_operator_properties_select_random(ot);
1395 }