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