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