1465 lines
		
	
	
		
			39 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1465 lines
		
	
	
		
			39 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * This program is free software; you can redistribute it and/or
 | 
						|
 * modify it under the terms of the GNU General Public License
 | 
						|
 * as published by the Free Software Foundation; either version 2
 | 
						|
 * of the License, or (at your option) any later version.
 | 
						|
 *
 | 
						|
 * This program is distributed in the hope that it will be useful,
 | 
						|
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
 * GNU General Public License for more details.
 | 
						|
 *
 | 
						|
 * You should have received a copy of the GNU General Public License
 | 
						|
 * along with this program; if not, write to the Free Software Foundation,
 | 
						|
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 | 
						|
 *
 | 
						|
 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
 | 
						|
 * All rights reserved.
 | 
						|
 */
 | 
						|
 | 
						|
/** \file
 | 
						|
 * \ingroup edobj
 | 
						|
 */
 | 
						|
 | 
						|
#include <ctype.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
 | 
						|
#include "DNA_anim_types.h"
 | 
						|
#include "DNA_collection_types.h"
 | 
						|
#include "DNA_material_types.h"
 | 
						|
#include "DNA_modifier_types.h"
 | 
						|
#include "DNA_scene_types.h"
 | 
						|
#include "DNA_armature_types.h"
 | 
						|
#include "DNA_light_types.h"
 | 
						|
#include "DNA_workspace_types.h"
 | 
						|
#include "DNA_gpencil_types.h"
 | 
						|
 | 
						|
#include "BLI_math.h"
 | 
						|
#include "BLI_math_bits.h"
 | 
						|
#include "BLI_listbase.h"
 | 
						|
#include "BLI_rand.h"
 | 
						|
#include "BLI_string_utils.h"
 | 
						|
#include "BLI_utildefines.h"
 | 
						|
 | 
						|
#include "BLT_translation.h"
 | 
						|
 | 
						|
#include "BKE_action.h"
 | 
						|
#include "BKE_armature.h"
 | 
						|
#include "BKE_collection.h"
 | 
						|
#include "BKE_context.h"
 | 
						|
#include "BKE_deform.h"
 | 
						|
#include "BKE_layer.h"
 | 
						|
#include "BKE_library.h"
 | 
						|
#include "BKE_main.h"
 | 
						|
#include "BKE_material.h"
 | 
						|
#include "BKE_object.h"
 | 
						|
#include "BKE_paint.h"
 | 
						|
#include "BKE_particle.h"
 | 
						|
#include "BKE_report.h"
 | 
						|
#include "BKE_scene.h"
 | 
						|
#include "BKE_workspace.h"
 | 
						|
 | 
						|
#include "DEG_depsgraph.h"
 | 
						|
 | 
						|
#include "WM_api.h"
 | 
						|
#include "WM_types.h"
 | 
						|
#include "WM_message.h"
 | 
						|
 | 
						|
#include "ED_armature.h"
 | 
						|
#include "ED_object.h"
 | 
						|
#include "ED_screen.h"
 | 
						|
#include "ED_select_utils.h"
 | 
						|
#include "ED_keyframing.h"
 | 
						|
 | 
						|
#include "UI_interface.h"
 | 
						|
#include "UI_resources.h"
 | 
						|
 | 
						|
#include "RNA_access.h"
 | 
						|
#include "RNA_define.h"
 | 
						|
#include "RNA_enum_types.h"
 | 
						|
 | 
						|
#include "object_intern.h"
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/** \name Public Object Selection API
 | 
						|
 * \{ */
 | 
						|
 | 
						|
/**
 | 
						|
 * simple API for object selection, rather than just using the flag
 | 
						|
 * this takes into account the 'restrict selection in 3d view' flag.
 | 
						|
 * deselect works always, the restriction just prevents selection
 | 
						|
 *
 | 
						|
 * \note Caller must send a `NC_SCENE | ND_OB_SELECT` notifier
 | 
						|
 * (or a `NC_SCENE | ND_OB_VISIBLE` in case of visibility toggling).
 | 
						|
 */
 | 
						|
void ED_object_base_select(Base *base, eObjectSelect_Mode mode)
 | 
						|
{
 | 
						|
  if (mode == BA_INVERT) {
 | 
						|
    mode = (base->flag & BASE_SELECTED) != 0 ? BA_DESELECT : BA_SELECT;
 | 
						|
  }
 | 
						|
 | 
						|
  if (base) {
 | 
						|
    switch (mode) {
 | 
						|
      case BA_SELECT:
 | 
						|
        if ((base->flag & BASE_SELECTABLE) != 0) {
 | 
						|
          base->flag |= BASE_SELECTED;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      case BA_DESELECT:
 | 
						|
        base->flag &= ~BASE_SELECTED;
 | 
						|
        break;
 | 
						|
      case BA_INVERT:
 | 
						|
        /* Never happens. */
 | 
						|
        break;
 | 
						|
    }
 | 
						|
    BKE_scene_object_base_flag_sync_from_base(base);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Change active base, it includes the notifier
 | 
						|
 */
 | 
						|
void ED_object_base_activate(bContext *C, Base *base)
 | 
						|
{
 | 
						|
  struct wmMsgBus *mbus = CTX_wm_message_bus(C);
 | 
						|
  Scene *scene = CTX_data_scene(C);
 | 
						|
  ViewLayer *view_layer = CTX_data_view_layer(C);
 | 
						|
  view_layer->basact = base;
 | 
						|
 | 
						|
  WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
 | 
						|
  WM_msg_publish_rna_prop(mbus, &scene->id, view_layer, LayerObjects, active);
 | 
						|
  DEG_id_tag_update(&CTX_data_scene(C)->id, ID_RECALC_SELECT);
 | 
						|
}
 | 
						|
 | 
						|
bool ED_object_base_deselect_all_ex(ViewLayer *view_layer,
 | 
						|
                                    View3D *v3d,
 | 
						|
                                    int action,
 | 
						|
                                    bool *r_any_visible)
 | 
						|
{
 | 
						|
  if (action == SEL_TOGGLE) {
 | 
						|
    action = SEL_SELECT;
 | 
						|
    FOREACH_VISIBLE_BASE_BEGIN (view_layer, v3d, base) {
 | 
						|
      if (v3d && ((v3d->object_type_exclude_select & (1 << base->object->type)) != 0)) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
      if ((base->flag & BASE_SELECTED) != 0) {
 | 
						|
        action = SEL_DESELECT;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    FOREACH_VISIBLE_BASE_END;
 | 
						|
  }
 | 
						|
 | 
						|
  bool any_visible = false;
 | 
						|
  bool changed = false;
 | 
						|
  FOREACH_VISIBLE_BASE_BEGIN (view_layer, v3d, base) {
 | 
						|
    if (v3d && ((v3d->object_type_exclude_select & (1 << base->object->type)) != 0)) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    switch (action) {
 | 
						|
      case SEL_SELECT:
 | 
						|
        if ((base->flag & BASE_SELECTED) == 0) {
 | 
						|
          ED_object_base_select(base, BA_SELECT);
 | 
						|
          changed = true;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      case SEL_DESELECT:
 | 
						|
        if ((base->flag & BASE_SELECTED) != 0) {
 | 
						|
          ED_object_base_select(base, BA_DESELECT);
 | 
						|
          changed = true;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      case SEL_INVERT:
 | 
						|
        if ((base->flag & BASE_SELECTED) != 0) {
 | 
						|
          ED_object_base_select(base, BA_DESELECT);
 | 
						|
          changed = true;
 | 
						|
        }
 | 
						|
        else {
 | 
						|
          ED_object_base_select(base, BA_SELECT);
 | 
						|
          changed = true;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
    }
 | 
						|
    any_visible = true;
 | 
						|
  }
 | 
						|
  FOREACH_VISIBLE_BASE_END;
 | 
						|
  if (r_any_visible) {
 | 
						|
    *r_any_visible = any_visible;
 | 
						|
  }
 | 
						|
  return changed;
 | 
						|
}
 | 
						|
 | 
						|
bool ED_object_base_deselect_all(ViewLayer *view_layer, View3D *v3d, int action)
 | 
						|
{
 | 
						|
  return ED_object_base_deselect_all_ex(view_layer, v3d, action, NULL);
 | 
						|
}
 | 
						|
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/** \name Jump To Object Utilities
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static int get_base_select_priority(Base *base)
 | 
						|
{
 | 
						|
  if (base->flag & BASE_VISIBLE) {
 | 
						|
    if (base->flag & BASE_SELECTABLE) {
 | 
						|
      return 3;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      return 2;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    return 1;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * If id is not already an Object, try to find an object that uses it as data.
 | 
						|
 * Prefers active, then selected, then visible/selectable.
 | 
						|
 */
 | 
						|
Base *ED_object_find_first_by_data_id(ViewLayer *view_layer, ID *id)
 | 
						|
{
 | 
						|
  BLI_assert(OB_DATA_SUPPORT_ID(GS(id->name)));
 | 
						|
 | 
						|
  /* Try active object. */
 | 
						|
  Base *basact = view_layer->basact;
 | 
						|
 | 
						|
  if (basact && basact->object && basact->object->data == id) {
 | 
						|
    return basact;
 | 
						|
  }
 | 
						|
 | 
						|
  /* Try all objects. */
 | 
						|
  Base *base_best = NULL;
 | 
						|
  int priority_best = 0;
 | 
						|
 | 
						|
  for (Base *base = view_layer->object_bases.first; base; base = base->next) {
 | 
						|
    if (base->object && base->object->data == id) {
 | 
						|
      if (base->flag & BASE_SELECTED) {
 | 
						|
        return base;
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        int priority_test = get_base_select_priority(base);
 | 
						|
 | 
						|
        if (priority_test > priority_best) {
 | 
						|
          priority_best = priority_test;
 | 
						|
          base_best = base;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return base_best;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Select and make the target object active in the view layer.
 | 
						|
 * If already selected, selection isn't changed.
 | 
						|
 *
 | 
						|
 * \returns false if not found in current view layer
 | 
						|
 */
 | 
						|
bool ED_object_jump_to_object(bContext *C, Object *ob, const bool UNUSED(reveal_hidden))
 | 
						|
{
 | 
						|
  ViewLayer *view_layer = CTX_data_view_layer(C);
 | 
						|
  View3D *v3d = CTX_wm_view3d(C);
 | 
						|
  Base *base = BKE_view_layer_base_find(view_layer, ob);
 | 
						|
 | 
						|
  if (base == NULL) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  /* TODO, use 'reveal_hidden', as is done with bones. */
 | 
						|
 | 
						|
  if (view_layer->basact != base || !(base->flag & BASE_SELECTED)) {
 | 
						|
    /* Select if not selected. */
 | 
						|
    if (!(base->flag & BASE_SELECTED)) {
 | 
						|
      ED_object_base_deselect_all(view_layer, v3d, SEL_DESELECT);
 | 
						|
 | 
						|
      if (base->flag & BASE_VISIBLE) {
 | 
						|
        ED_object_base_select(base, BA_SELECT);
 | 
						|
      }
 | 
						|
 | 
						|
      WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, CTX_data_scene(C));
 | 
						|
    }
 | 
						|
 | 
						|
    /* Make active if not active. */
 | 
						|
    ED_object_base_activate(C, base);
 | 
						|
  }
 | 
						|
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Select and make the target object and bone active.
 | 
						|
 * Switches to Pose mode if in Object mode so the selection is visible.
 | 
						|
 * Unhides the target bone and bone layer if necessary.
 | 
						|
 *
 | 
						|
 * \returns false if object not in layer, bone not found, or other error
 | 
						|
 */
 | 
						|
bool ED_object_jump_to_bone(bContext *C,
 | 
						|
                            Object *ob,
 | 
						|
                            const char *bone_name,
 | 
						|
                            const bool reveal_hidden)
 | 
						|
{
 | 
						|
  /* Verify it's a valid armature object. */
 | 
						|
  if (ob == NULL || ob->type != OB_ARMATURE) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  bArmature *arm = ob->data;
 | 
						|
 | 
						|
  /* Activate the armature object. */
 | 
						|
  if (!ED_object_jump_to_object(C, ob, reveal_hidden)) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  /* Switch to pose mode from object mode. */
 | 
						|
  if (!ELEM(ob->mode, OB_MODE_EDIT, OB_MODE_POSE)) {
 | 
						|
    ED_object_mode_set(C, OB_MODE_POSE);
 | 
						|
  }
 | 
						|
 | 
						|
  if (ob->mode == OB_MODE_EDIT && arm->edbo != NULL) {
 | 
						|
    /* In Edit mode select and activate the target Edit-Bone. */
 | 
						|
    EditBone *ebone = ED_armature_ebone_find_name(arm->edbo, bone_name);
 | 
						|
    if (ebone != NULL) {
 | 
						|
      if (reveal_hidden) {
 | 
						|
        /* Unhide the bone. */
 | 
						|
        ebone->flag &= ~BONE_HIDDEN_A;
 | 
						|
 | 
						|
        if ((arm->layer & ebone->layer) == 0) {
 | 
						|
          arm->layer |= 1U << bitscan_forward_uint(ebone->layer);
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      /* Select it. */
 | 
						|
      ED_armature_edit_deselect_all(ob);
 | 
						|
 | 
						|
      if (EBONE_SELECTABLE(arm, ebone)) {
 | 
						|
        ED_armature_ebone_select_set(ebone, true);
 | 
						|
        ED_armature_edit_sync_selection(arm->edbo);
 | 
						|
      }
 | 
						|
 | 
						|
      arm->act_edbone = ebone;
 | 
						|
 | 
						|
      ED_pose_bone_select_tag_update(ob);
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else if (ob->mode == OB_MODE_POSE && ob->pose != NULL) {
 | 
						|
    /* In Pose mode select and activate the target Bone/Pose-Channel. */
 | 
						|
    bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, bone_name);
 | 
						|
    if (pchan != NULL) {
 | 
						|
      if (reveal_hidden) {
 | 
						|
        /* Unhide the bone. */
 | 
						|
        pchan->bone->flag &= ~BONE_HIDDEN_P;
 | 
						|
 | 
						|
        if ((arm->layer & pchan->bone->layer) == 0) {
 | 
						|
          arm->layer |= 1U << bitscan_forward_uint(pchan->bone->layer);
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      /* Select it. */
 | 
						|
      ED_pose_deselect_all(ob, SEL_DESELECT, true);
 | 
						|
      ED_pose_bone_select(ob, pchan, true);
 | 
						|
 | 
						|
      arm->act_bone = pchan->bone;
 | 
						|
 | 
						|
      ED_pose_bone_select_tag_update(ob);
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/** \name Select Operator Utils
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static bool objects_selectable_poll(bContext *C)
 | 
						|
{
 | 
						|
  /* we don't check for linked scenes here, selection is
 | 
						|
   * still allowed then for inspection of scene */
 | 
						|
  Object *obact = CTX_data_active_object(C);
 | 
						|
 | 
						|
  if (CTX_data_edit_object(C)) {
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
  if (obact && obact->mode) {
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  return 1;
 | 
						|
}
 | 
						|
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/** \name Select by Type
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static int object_select_by_type_exec(bContext *C, wmOperator *op)
 | 
						|
{
 | 
						|
  ViewLayer *view_layer = CTX_data_view_layer(C);
 | 
						|
  View3D *v3d = CTX_wm_view3d(C);
 | 
						|
  short obtype, extend;
 | 
						|
 | 
						|
  obtype = RNA_enum_get(op->ptr, "type");
 | 
						|
  extend = RNA_boolean_get(op->ptr, "extend");
 | 
						|
 | 
						|
  if (extend == 0) {
 | 
						|
    ED_object_base_deselect_all(view_layer, v3d, SEL_DESELECT);
 | 
						|
  }
 | 
						|
 | 
						|
  CTX_DATA_BEGIN (C, Base *, base, visible_bases) {
 | 
						|
    if (base->object->type == obtype) {
 | 
						|
      ED_object_base_select(base, BA_SELECT);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  CTX_DATA_END;
 | 
						|
 | 
						|
  Scene *scene = CTX_data_scene(C);
 | 
						|
  DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
 | 
						|
  WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
 | 
						|
 | 
						|
  return OPERATOR_FINISHED;
 | 
						|
}
 | 
						|
 | 
						|
void OBJECT_OT_select_by_type(wmOperatorType *ot)
 | 
						|
{
 | 
						|
  /* identifiers */
 | 
						|
  ot->name = "Select By Type";
 | 
						|
  ot->description = "Select all visible objects that are of a type";
 | 
						|
  ot->idname = "OBJECT_OT_select_by_type";
 | 
						|
 | 
						|
  /* api callbacks */
 | 
						|
  ot->invoke = WM_menu_invoke;
 | 
						|
  ot->exec = object_select_by_type_exec;
 | 
						|
  ot->poll = objects_selectable_poll;
 | 
						|
 | 
						|
  /* flags */
 | 
						|
  ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 | 
						|
 | 
						|
  /* properties */
 | 
						|
  RNA_def_boolean(ot->srna,
 | 
						|
                  "extend",
 | 
						|
                  false,
 | 
						|
                  "Extend",
 | 
						|
                  "Extend selection instead of deselecting everything first");
 | 
						|
  ot->prop = RNA_def_enum(ot->srna, "type", rna_enum_object_type_items, 1, "Type", "");
 | 
						|
  RNA_def_property_translation_context(ot->prop, BLT_I18NCONTEXT_ID_ID);
 | 
						|
}
 | 
						|
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/** \name Selection by Links
 | 
						|
 * \{ */
 | 
						|
 | 
						|
enum {
 | 
						|
  OBJECT_SELECT_LINKED_IPO = 1,
 | 
						|
  OBJECT_SELECT_LINKED_OBDATA,
 | 
						|
  OBJECT_SELECT_LINKED_MATERIAL,
 | 
						|
  OBJECT_SELECT_LINKED_DUPGROUP,
 | 
						|
  OBJECT_SELECT_LINKED_PARTICLE,
 | 
						|
  OBJECT_SELECT_LINKED_LIBRARY,
 | 
						|
  OBJECT_SELECT_LINKED_LIBRARY_OBDATA,
 | 
						|
};
 | 
						|
 | 
						|
static const EnumPropertyItem prop_select_linked_types[] = {
 | 
						|
    /* XXX deprecated animation system stuff. */
 | 
						|
    // {OBJECT_SELECT_LINKED_IPO, "IPO", 0, "Object IPO", ""},
 | 
						|
    {OBJECT_SELECT_LINKED_OBDATA, "OBDATA", 0, "Object Data", ""},
 | 
						|
    {OBJECT_SELECT_LINKED_MATERIAL, "MATERIAL", 0, "Material", ""},
 | 
						|
    {OBJECT_SELECT_LINKED_DUPGROUP, "DUPGROUP", 0, "Instanced Collection", ""},
 | 
						|
    {OBJECT_SELECT_LINKED_PARTICLE, "PARTICLE", 0, "Particle System", ""},
 | 
						|
    {OBJECT_SELECT_LINKED_LIBRARY, "LIBRARY", 0, "Library", ""},
 | 
						|
    {OBJECT_SELECT_LINKED_LIBRARY_OBDATA, "LIBRARY_OBDATA", 0, "Library (Object Data)", ""},
 | 
						|
    {0, NULL, 0, NULL, NULL},
 | 
						|
};
 | 
						|
 | 
						|
static bool object_select_all_by_obdata(bContext *C, void *obdata)
 | 
						|
{
 | 
						|
  bool changed = false;
 | 
						|
 | 
						|
  CTX_DATA_BEGIN (C, Base *, base, visible_bases) {
 | 
						|
    if (((base->flag & BASE_SELECTED) == 0) && ((base->flag & BASE_SELECTABLE) != 0)) {
 | 
						|
      if (base->object->data == obdata) {
 | 
						|
        ED_object_base_select(base, BA_SELECT);
 | 
						|
        changed = true;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  CTX_DATA_END;
 | 
						|
 | 
						|
  return changed;
 | 
						|
}
 | 
						|
 | 
						|
static bool object_select_all_by_material(bContext *C, Material *mat)
 | 
						|
{
 | 
						|
  bool changed = false;
 | 
						|
 | 
						|
  CTX_DATA_BEGIN (C, Base *, base, visible_bases) {
 | 
						|
    if (((base->flag & BASE_SELECTED) == 0) && ((base->flag & BASE_SELECTABLE) != 0)) {
 | 
						|
      Object *ob = base->object;
 | 
						|
      Material *mat1;
 | 
						|
      int a;
 | 
						|
 | 
						|
      for (a = 1; a <= ob->totcol; a++) {
 | 
						|
        mat1 = give_current_material(ob, a);
 | 
						|
 | 
						|
        if (mat1 == mat) {
 | 
						|
          ED_object_base_select(base, BA_SELECT);
 | 
						|
          changed = true;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  CTX_DATA_END;
 | 
						|
 | 
						|
  return changed;
 | 
						|
}
 | 
						|
 | 
						|
static bool object_select_all_by_instance_collection(bContext *C, Object *ob)
 | 
						|
{
 | 
						|
  bool changed = false;
 | 
						|
  Collection *instance_collection = (ob->transflag & OB_DUPLICOLLECTION) ?
 | 
						|
                                        ob->instance_collection :
 | 
						|
                                        NULL;
 | 
						|
 | 
						|
  CTX_DATA_BEGIN (C, Base *, base, visible_bases) {
 | 
						|
    if (((base->flag & BASE_SELECTED) == 0) && ((base->flag & BASE_SELECTABLE) != 0)) {
 | 
						|
      Collection *instance_collection_other = (base->object->transflag & OB_DUPLICOLLECTION) ?
 | 
						|
                                                  base->object->instance_collection :
 | 
						|
                                                  NULL;
 | 
						|
      if (instance_collection == instance_collection_other) {
 | 
						|
        ED_object_base_select(base, BA_SELECT);
 | 
						|
        changed = true;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  CTX_DATA_END;
 | 
						|
 | 
						|
  return changed;
 | 
						|
}
 | 
						|
 | 
						|
static bool object_select_all_by_particle(bContext *C, Object *ob)
 | 
						|
{
 | 
						|
  ParticleSystem *psys_act = psys_get_current(ob);
 | 
						|
  bool changed = false;
 | 
						|
 | 
						|
  CTX_DATA_BEGIN (C, Base *, base, visible_bases) {
 | 
						|
    if (((base->flag & BASE_SELECTED) == 0) && ((base->flag & BASE_SELECTABLE) != 0)) {
 | 
						|
      /* loop through other particles*/
 | 
						|
      ParticleSystem *psys;
 | 
						|
 | 
						|
      for (psys = base->object->particlesystem.first; psys; psys = psys->next) {
 | 
						|
        if (psys->part == psys_act->part) {
 | 
						|
          ED_object_base_select(base, BA_SELECT);
 | 
						|
          changed = true;
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        if (base->flag & BASE_SELECTED) {
 | 
						|
          break;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  CTX_DATA_END;
 | 
						|
 | 
						|
  return changed;
 | 
						|
}
 | 
						|
 | 
						|
static bool object_select_all_by_library(bContext *C, Library *lib)
 | 
						|
{
 | 
						|
  bool changed = false;
 | 
						|
 | 
						|
  CTX_DATA_BEGIN (C, Base *, base, visible_bases) {
 | 
						|
    if (((base->flag & BASE_SELECTED) == 0) && ((base->flag & BASE_SELECTABLE) != 0)) {
 | 
						|
      if (lib == base->object->id.lib) {
 | 
						|
        ED_object_base_select(base, BA_SELECT);
 | 
						|
        changed = true;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  CTX_DATA_END;
 | 
						|
 | 
						|
  return changed;
 | 
						|
}
 | 
						|
 | 
						|
static bool object_select_all_by_library_obdata(bContext *C, Library *lib)
 | 
						|
{
 | 
						|
  bool changed = false;
 | 
						|
 | 
						|
  CTX_DATA_BEGIN (C, Base *, base, visible_bases) {
 | 
						|
    if (((base->flag & BASE_SELECTED) == 0) && ((base->flag & BASE_SELECTABLE) != 0)) {
 | 
						|
      if (base->object->data && lib == ((ID *)base->object->data)->lib) {
 | 
						|
        ED_object_base_select(base, BA_SELECT);
 | 
						|
        changed = true;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  CTX_DATA_END;
 | 
						|
 | 
						|
  return changed;
 | 
						|
}
 | 
						|
 | 
						|
void ED_object_select_linked_by_id(bContext *C, ID *id)
 | 
						|
{
 | 
						|
  int idtype = GS(id->name);
 | 
						|
  bool changed = false;
 | 
						|
 | 
						|
  if (OB_DATA_SUPPORT_ID(idtype)) {
 | 
						|
    changed = object_select_all_by_obdata(C, id);
 | 
						|
  }
 | 
						|
  else if (idtype == ID_MA) {
 | 
						|
    changed = object_select_all_by_material(C, (Material *)id);
 | 
						|
  }
 | 
						|
  else if (idtype == ID_LI) {
 | 
						|
    changed = object_select_all_by_library(C, (Library *)id);
 | 
						|
  }
 | 
						|
 | 
						|
  if (changed) {
 | 
						|
    Scene *scene = CTX_data_scene(C);
 | 
						|
    DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
 | 
						|
    WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static int object_select_linked_exec(bContext *C, wmOperator *op)
 | 
						|
{
 | 
						|
  Scene *scene = CTX_data_scene(C);
 | 
						|
  ViewLayer *view_layer = CTX_data_view_layer(C);
 | 
						|
  View3D *v3d = CTX_wm_view3d(C);
 | 
						|
  Object *ob;
 | 
						|
  int nr = RNA_enum_get(op->ptr, "type");
 | 
						|
  bool changed = false, extend;
 | 
						|
 | 
						|
  extend = RNA_boolean_get(op->ptr, "extend");
 | 
						|
 | 
						|
  if (extend == 0) {
 | 
						|
    ED_object_base_deselect_all(view_layer, v3d, SEL_DESELECT);
 | 
						|
  }
 | 
						|
 | 
						|
  ob = OBACT(view_layer);
 | 
						|
  if (ob == NULL) {
 | 
						|
    BKE_report(op->reports, RPT_ERROR, "No active object");
 | 
						|
    return OPERATOR_CANCELLED;
 | 
						|
  }
 | 
						|
 | 
						|
  if (nr == OBJECT_SELECT_LINKED_IPO) {
 | 
						|
    // XXX old animation system
 | 
						|
    //if (ob->ipo == 0) return OPERATOR_CANCELLED;
 | 
						|
    //object_select_all_by_ipo(C, ob->ipo)
 | 
						|
    return OPERATOR_CANCELLED;
 | 
						|
  }
 | 
						|
  else if (nr == OBJECT_SELECT_LINKED_OBDATA) {
 | 
						|
    if (ob->data == NULL) {
 | 
						|
      return OPERATOR_CANCELLED;
 | 
						|
    }
 | 
						|
 | 
						|
    changed = object_select_all_by_obdata(C, ob->data);
 | 
						|
  }
 | 
						|
  else if (nr == OBJECT_SELECT_LINKED_MATERIAL) {
 | 
						|
    Material *mat = NULL;
 | 
						|
 | 
						|
    mat = give_current_material(ob, ob->actcol);
 | 
						|
    if (mat == NULL) {
 | 
						|
      return OPERATOR_CANCELLED;
 | 
						|
    }
 | 
						|
 | 
						|
    changed = object_select_all_by_material(C, mat);
 | 
						|
  }
 | 
						|
  else if (nr == OBJECT_SELECT_LINKED_DUPGROUP) {
 | 
						|
    if (ob->instance_collection == NULL) {
 | 
						|
      return OPERATOR_CANCELLED;
 | 
						|
    }
 | 
						|
 | 
						|
    changed = object_select_all_by_instance_collection(C, ob);
 | 
						|
  }
 | 
						|
  else if (nr == OBJECT_SELECT_LINKED_PARTICLE) {
 | 
						|
    if (BLI_listbase_is_empty(&ob->particlesystem)) {
 | 
						|
      return OPERATOR_CANCELLED;
 | 
						|
    }
 | 
						|
 | 
						|
    changed = object_select_all_by_particle(C, ob);
 | 
						|
  }
 | 
						|
  else if (nr == OBJECT_SELECT_LINKED_LIBRARY) {
 | 
						|
    /* do nothing */
 | 
						|
    changed = object_select_all_by_library(C, ob->id.lib);
 | 
						|
  }
 | 
						|
  else if (nr == OBJECT_SELECT_LINKED_LIBRARY_OBDATA) {
 | 
						|
    if (ob->data == NULL) {
 | 
						|
      return OPERATOR_CANCELLED;
 | 
						|
    }
 | 
						|
 | 
						|
    changed = object_select_all_by_library_obdata(C, ((ID *)ob->data)->lib);
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    return OPERATOR_CANCELLED;
 | 
						|
  }
 | 
						|
 | 
						|
  if (changed) {
 | 
						|
    DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
 | 
						|
    WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
 | 
						|
    return OPERATOR_FINISHED;
 | 
						|
  }
 | 
						|
 | 
						|
  return OPERATOR_CANCELLED;
 | 
						|
}
 | 
						|
 | 
						|
void OBJECT_OT_select_linked(wmOperatorType *ot)
 | 
						|
{
 | 
						|
  /* identifiers */
 | 
						|
  ot->name = "Select Linked";
 | 
						|
  ot->description = "Select all visible objects that are linked";
 | 
						|
  ot->idname = "OBJECT_OT_select_linked";
 | 
						|
 | 
						|
  /* api callbacks */
 | 
						|
  ot->invoke = WM_menu_invoke;
 | 
						|
  ot->exec = object_select_linked_exec;
 | 
						|
  ot->poll = objects_selectable_poll;
 | 
						|
 | 
						|
  /* flags */
 | 
						|
  ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 | 
						|
 | 
						|
  /* properties */
 | 
						|
  RNA_def_boolean(ot->srna,
 | 
						|
                  "extend",
 | 
						|
                  false,
 | 
						|
                  "Extend",
 | 
						|
                  "Extend selection instead of deselecting everything first");
 | 
						|
  ot->prop = RNA_def_enum(ot->srna, "type", prop_select_linked_types, 0, "Type", "");
 | 
						|
}
 | 
						|
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/** \name Selected Grouped
 | 
						|
 * \{ */
 | 
						|
 | 
						|
enum {
 | 
						|
  OBJECT_GRPSEL_CHILDREN_RECURSIVE = 0,
 | 
						|
  OBJECT_GRPSEL_CHILDREN = 1,
 | 
						|
  OBJECT_GRPSEL_PARENT = 2,
 | 
						|
  OBJECT_GRPSEL_SIBLINGS = 3,
 | 
						|
  OBJECT_GRPSEL_TYPE = 4,
 | 
						|
  OBJECT_GRPSEL_COLLECTION = 5,
 | 
						|
  OBJECT_GRPSEL_HOOK = 7,
 | 
						|
  OBJECT_GRPSEL_PASS = 8,
 | 
						|
  OBJECT_GRPSEL_COLOR = 9,
 | 
						|
  OBJECT_GRPSEL_KEYINGSET = 10,
 | 
						|
  OBJECT_GRPSEL_LIGHT_TYPE = 11,
 | 
						|
};
 | 
						|
 | 
						|
static const EnumPropertyItem prop_select_grouped_types[] = {
 | 
						|
    {OBJECT_GRPSEL_CHILDREN_RECURSIVE, "CHILDREN_RECURSIVE", 0, "Children", ""},
 | 
						|
    {OBJECT_GRPSEL_CHILDREN, "CHILDREN", 0, "Immediate Children", ""},
 | 
						|
    {OBJECT_GRPSEL_PARENT, "PARENT", 0, "Parent", ""},
 | 
						|
    {OBJECT_GRPSEL_SIBLINGS, "SIBLINGS", 0, "Siblings", "Shared Parent"},
 | 
						|
    {OBJECT_GRPSEL_TYPE, "TYPE", 0, "Type", "Shared object type"},
 | 
						|
    {OBJECT_GRPSEL_COLLECTION, "COLLECTION", 0, "Collection", "Shared collection"},
 | 
						|
    {OBJECT_GRPSEL_HOOK, "HOOK", 0, "Hook", ""},
 | 
						|
    {OBJECT_GRPSEL_PASS, "PASS", 0, "Pass", "Render pass Index"},
 | 
						|
    {OBJECT_GRPSEL_COLOR, "COLOR", 0, "Color", "Object Color"},
 | 
						|
    {OBJECT_GRPSEL_KEYINGSET,
 | 
						|
     "KEYINGSET",
 | 
						|
     0,
 | 
						|
     "Keying Set",
 | 
						|
     "Objects included in active Keying Set"},
 | 
						|
    {OBJECT_GRPSEL_LIGHT_TYPE, "LIGHT_TYPE", 0, "Light Type", "Matching light types"},
 | 
						|
    {0, NULL, 0, NULL, NULL},
 | 
						|
};
 | 
						|
 | 
						|
static bool select_grouped_children(bContext *C, Object *ob, const bool recursive)
 | 
						|
{
 | 
						|
  bool changed = false;
 | 
						|
 | 
						|
  CTX_DATA_BEGIN (C, Base *, base, selectable_bases) {
 | 
						|
    if (ob == base->object->parent) {
 | 
						|
      if ((base->flag & BASE_SELECTED) == 0) {
 | 
						|
        ED_object_base_select(base, BA_SELECT);
 | 
						|
        changed = true;
 | 
						|
      }
 | 
						|
 | 
						|
      if (recursive) {
 | 
						|
        changed |= select_grouped_children(C, base->object, 1);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  CTX_DATA_END;
 | 
						|
  return changed;
 | 
						|
}
 | 
						|
 | 
						|
static bool select_grouped_parent(bContext *C) /* Makes parent active and de-selected OBACT */
 | 
						|
{
 | 
						|
  ViewLayer *view_layer = CTX_data_view_layer(C);
 | 
						|
  View3D *v3d = CTX_wm_view3d(C);
 | 
						|
  Base *baspar, *basact = CTX_data_active_base(C);
 | 
						|
  bool changed = false;
 | 
						|
 | 
						|
  if (!basact || !(basact->object->parent)) {
 | 
						|
    return 0; /* we know OBACT is valid */
 | 
						|
  }
 | 
						|
 | 
						|
  baspar = BKE_view_layer_base_find(view_layer, basact->object->parent);
 | 
						|
 | 
						|
  /* can be NULL if parent in other scene */
 | 
						|
  if (baspar && BASE_SELECTABLE(v3d, baspar)) {
 | 
						|
    ED_object_base_select(baspar, BA_SELECT);
 | 
						|
    ED_object_base_activate(C, baspar);
 | 
						|
    changed = true;
 | 
						|
  }
 | 
						|
  return changed;
 | 
						|
}
 | 
						|
 | 
						|
#define COLLECTION_MENU_MAX 24
 | 
						|
/* Select objects in the same group as the active */
 | 
						|
static bool select_grouped_collection(bContext *C, Object *ob)
 | 
						|
{
 | 
						|
  bool changed = false;
 | 
						|
  Collection *collection, *ob_collections[COLLECTION_MENU_MAX];
 | 
						|
  int collection_count = 0, i;
 | 
						|
  uiPopupMenu *pup;
 | 
						|
  uiLayout *layout;
 | 
						|
 | 
						|
  for (collection = CTX_data_main(C)->collections.first;
 | 
						|
       collection && collection_count < COLLECTION_MENU_MAX;
 | 
						|
       collection = collection->id.next) {
 | 
						|
    if (BKE_collection_has_object(collection, ob)) {
 | 
						|
      ob_collections[collection_count] = collection;
 | 
						|
      collection_count++;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (!collection_count) {
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
  else if (collection_count == 1) {
 | 
						|
    collection = ob_collections[0];
 | 
						|
    CTX_DATA_BEGIN (C, Base *, base, visible_bases) {
 | 
						|
      if (((base->flag & BASE_SELECTED) == 0) && ((base->flag & BASE_SELECTABLE) != 0)) {
 | 
						|
        if (BKE_collection_has_object(collection, base->object)) {
 | 
						|
          ED_object_base_select(base, BA_SELECT);
 | 
						|
          changed = true;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
    CTX_DATA_END;
 | 
						|
    return changed;
 | 
						|
  }
 | 
						|
 | 
						|
  /* build the menu. */
 | 
						|
  pup = UI_popup_menu_begin(C, IFACE_("Select Collection"), ICON_NONE);
 | 
						|
  layout = UI_popup_menu_layout(pup);
 | 
						|
 | 
						|
  for (i = 0; i < collection_count; i++) {
 | 
						|
    collection = ob_collections[i];
 | 
						|
    uiItemStringO(layout,
 | 
						|
                  collection->id.name + 2,
 | 
						|
                  0,
 | 
						|
                  "OBJECT_OT_select_same_collection",
 | 
						|
                  "collection",
 | 
						|
                  collection->id.name + 2);
 | 
						|
  }
 | 
						|
 | 
						|
  UI_popup_menu_end(C, pup);
 | 
						|
  return changed; /* The operator already handle this! */
 | 
						|
}
 | 
						|
 | 
						|
static bool select_grouped_object_hooks(bContext *C, Object *ob)
 | 
						|
{
 | 
						|
  ViewLayer *view_layer = CTX_data_view_layer(C);
 | 
						|
  View3D *v3d = CTX_wm_view3d(C);
 | 
						|
 | 
						|
  bool changed = false;
 | 
						|
  Base *base;
 | 
						|
  ModifierData *md;
 | 
						|
  HookModifierData *hmd;
 | 
						|
 | 
						|
  for (md = ob->modifiers.first; md; md = md->next) {
 | 
						|
    if (md->type == eModifierType_Hook) {
 | 
						|
      hmd = (HookModifierData *)md;
 | 
						|
      if (hmd->object) {
 | 
						|
        base = BKE_view_layer_base_find(view_layer, hmd->object);
 | 
						|
        if (base && ((base->flag & BASE_SELECTED) == 0) && (BASE_SELECTABLE(v3d, base))) {
 | 
						|
          ED_object_base_select(base, BA_SELECT);
 | 
						|
          changed = true;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return changed;
 | 
						|
}
 | 
						|
 | 
						|
/* Select objects with the same parent as the active (siblings),
 | 
						|
 * parent can be NULL also */
 | 
						|
static bool select_grouped_siblings(bContext *C, Object *ob)
 | 
						|
{
 | 
						|
  bool changed = false;
 | 
						|
 | 
						|
  CTX_DATA_BEGIN (C, Base *, base, selectable_bases) {
 | 
						|
    if ((base->object->parent == ob->parent) && ((base->flag & BASE_SELECTED) == 0)) {
 | 
						|
      ED_object_base_select(base, BA_SELECT);
 | 
						|
      changed = true;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  CTX_DATA_END;
 | 
						|
  return changed;
 | 
						|
}
 | 
						|
static bool select_grouped_lighttype(bContext *C, Object *ob)
 | 
						|
{
 | 
						|
  Light *la = ob->data;
 | 
						|
 | 
						|
  bool changed = false;
 | 
						|
 | 
						|
  CTX_DATA_BEGIN (C, Base *, base, selectable_bases) {
 | 
						|
    if (base->object->type == OB_LAMP) {
 | 
						|
      Light *la_test = base->object->data;
 | 
						|
      if ((la->type == la_test->type) && ((base->flag & BASE_SELECTED) == 0)) {
 | 
						|
        ED_object_base_select(base, BA_SELECT);
 | 
						|
        changed = true;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  CTX_DATA_END;
 | 
						|
  return changed;
 | 
						|
}
 | 
						|
static bool select_grouped_type(bContext *C, Object *ob)
 | 
						|
{
 | 
						|
  bool changed = false;
 | 
						|
 | 
						|
  CTX_DATA_BEGIN (C, Base *, base, selectable_bases) {
 | 
						|
    if ((base->object->type == ob->type) && ((base->flag & BASE_SELECTED) == 0)) {
 | 
						|
      ED_object_base_select(base, BA_SELECT);
 | 
						|
      changed = true;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  CTX_DATA_END;
 | 
						|
  return changed;
 | 
						|
}
 | 
						|
 | 
						|
static bool select_grouped_index_object(bContext *C, Object *ob)
 | 
						|
{
 | 
						|
  bool changed = false;
 | 
						|
 | 
						|
  CTX_DATA_BEGIN (C, Base *, base, selectable_bases) {
 | 
						|
    if ((base->object->index == ob->index) && ((base->flag & BASE_SELECTED) == 0)) {
 | 
						|
      ED_object_base_select(base, BA_SELECT);
 | 
						|
      changed = true;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  CTX_DATA_END;
 | 
						|
  return changed;
 | 
						|
}
 | 
						|
 | 
						|
static bool select_grouped_color(bContext *C, Object *ob)
 | 
						|
{
 | 
						|
  bool changed = false;
 | 
						|
 | 
						|
  CTX_DATA_BEGIN (C, Base *, base, selectable_bases) {
 | 
						|
    if (((base->flag & BASE_SELECTED) == 0) &&
 | 
						|
        (compare_v3v3(base->object->color, ob->color, 0.005f))) {
 | 
						|
      ED_object_base_select(base, BA_SELECT);
 | 
						|
      changed = true;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  CTX_DATA_END;
 | 
						|
  return changed;
 | 
						|
}
 | 
						|
 | 
						|
static bool select_grouped_keyingset(bContext *C, Object *UNUSED(ob), ReportList *reports)
 | 
						|
{
 | 
						|
  KeyingSet *ks = ANIM_scene_get_active_keyingset(CTX_data_scene(C));
 | 
						|
  bool changed = false;
 | 
						|
 | 
						|
  /* firstly, validate KeyingSet */
 | 
						|
  if (ks == NULL) {
 | 
						|
    BKE_report(reports, RPT_ERROR, "No active Keying Set to use");
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  else if (ANIM_validate_keyingset(C, NULL, ks) != 0) {
 | 
						|
    if (ks->paths.first == NULL) {
 | 
						|
      if ((ks->flag & KEYINGSET_ABSOLUTE) == 0) {
 | 
						|
        BKE_report(reports,
 | 
						|
                   RPT_ERROR,
 | 
						|
                   "Use another Keying Set, as the active one depends on the currently "
 | 
						|
                   "selected objects or cannot find any targets due to unsuitable context");
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        BKE_report(reports, RPT_ERROR, "Keying Set does not contain any paths");
 | 
						|
      }
 | 
						|
    }
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  /* select each object that Keying Set refers to */
 | 
						|
  /* TODO: perhaps to be more in line with the rest of these, we should only take objects
 | 
						|
   * if the passed in object is included in this too */
 | 
						|
  CTX_DATA_BEGIN (C, Base *, base, selectable_bases) {
 | 
						|
    /* only check for this object if it isn't selected already, to limit time wasted */
 | 
						|
    if ((base->flag & BASE_SELECTED) == 0) {
 | 
						|
      KS_Path *ksp;
 | 
						|
 | 
						|
      /* this is the slow way... we could end up with > 500 items here,
 | 
						|
       * with none matching, but end up doing this on 1000 objects...
 | 
						|
       */
 | 
						|
      for (ksp = ks->paths.first; ksp; ksp = ksp->next) {
 | 
						|
        /* if id matches, select then stop looping (match found) */
 | 
						|
        if (ksp->id == (ID *)base->object) {
 | 
						|
          ED_object_base_select(base, BA_SELECT);
 | 
						|
          changed = true;
 | 
						|
          break;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  CTX_DATA_END;
 | 
						|
 | 
						|
  return changed;
 | 
						|
}
 | 
						|
 | 
						|
static int object_select_grouped_exec(bContext *C, wmOperator *op)
 | 
						|
{
 | 
						|
  Scene *scene = CTX_data_scene(C);
 | 
						|
  ViewLayer *view_layer = CTX_data_view_layer(C);
 | 
						|
  View3D *v3d = CTX_wm_view3d(C);
 | 
						|
  Object *ob;
 | 
						|
  const int type = RNA_enum_get(op->ptr, "type");
 | 
						|
  bool changed = false, extend;
 | 
						|
 | 
						|
  extend = RNA_boolean_get(op->ptr, "extend");
 | 
						|
 | 
						|
  if (extend == 0) {
 | 
						|
    changed = ED_object_base_deselect_all(view_layer, v3d, SEL_DESELECT);
 | 
						|
  }
 | 
						|
 | 
						|
  ob = OBACT(view_layer);
 | 
						|
  if (ob == NULL) {
 | 
						|
    BKE_report(op->reports, RPT_ERROR, "No active object");
 | 
						|
    return OPERATOR_CANCELLED;
 | 
						|
  }
 | 
						|
 | 
						|
  switch (type) {
 | 
						|
    case OBJECT_GRPSEL_CHILDREN_RECURSIVE:
 | 
						|
      changed |= select_grouped_children(C, ob, true);
 | 
						|
      break;
 | 
						|
    case OBJECT_GRPSEL_CHILDREN:
 | 
						|
      changed |= select_grouped_children(C, ob, false);
 | 
						|
      break;
 | 
						|
    case OBJECT_GRPSEL_PARENT:
 | 
						|
      changed |= select_grouped_parent(C);
 | 
						|
      break;
 | 
						|
    case OBJECT_GRPSEL_SIBLINGS:
 | 
						|
      changed |= select_grouped_siblings(C, ob);
 | 
						|
      break;
 | 
						|
    case OBJECT_GRPSEL_TYPE:
 | 
						|
      changed |= select_grouped_type(C, ob);
 | 
						|
      break;
 | 
						|
    case OBJECT_GRPSEL_COLLECTION:
 | 
						|
      changed |= select_grouped_collection(C, ob);
 | 
						|
      break;
 | 
						|
    case OBJECT_GRPSEL_HOOK:
 | 
						|
      changed |= select_grouped_object_hooks(C, ob);
 | 
						|
      break;
 | 
						|
    case OBJECT_GRPSEL_PASS:
 | 
						|
      changed |= select_grouped_index_object(C, ob);
 | 
						|
      break;
 | 
						|
    case OBJECT_GRPSEL_COLOR:
 | 
						|
      changed |= select_grouped_color(C, ob);
 | 
						|
      break;
 | 
						|
    case OBJECT_GRPSEL_KEYINGSET:
 | 
						|
      changed |= select_grouped_keyingset(C, ob, op->reports);
 | 
						|
      break;
 | 
						|
    case OBJECT_GRPSEL_LIGHT_TYPE:
 | 
						|
      if (ob->type != OB_LAMP) {
 | 
						|
        BKE_report(op->reports, RPT_ERROR, "Active object must be a light");
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      changed |= select_grouped_lighttype(C, ob);
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      break;
 | 
						|
  }
 | 
						|
 | 
						|
  if (changed) {
 | 
						|
    DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
 | 
						|
    WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
 | 
						|
    return OPERATOR_FINISHED;
 | 
						|
  }
 | 
						|
 | 
						|
  return OPERATOR_CANCELLED;
 | 
						|
}
 | 
						|
 | 
						|
void OBJECT_OT_select_grouped(wmOperatorType *ot)
 | 
						|
{
 | 
						|
  /* identifiers */
 | 
						|
  ot->name = "Select Grouped";
 | 
						|
  ot->description = "Select all visible objects grouped by various properties";
 | 
						|
  ot->idname = "OBJECT_OT_select_grouped";
 | 
						|
 | 
						|
  /* api callbacks */
 | 
						|
  ot->invoke = WM_menu_invoke;
 | 
						|
  ot->exec = object_select_grouped_exec;
 | 
						|
  ot->poll = objects_selectable_poll;
 | 
						|
 | 
						|
  /* flags */
 | 
						|
  ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 | 
						|
 | 
						|
  /* properties */
 | 
						|
  RNA_def_boolean(ot->srna,
 | 
						|
                  "extend",
 | 
						|
                  false,
 | 
						|
                  "Extend",
 | 
						|
                  "Extend selection instead of deselecting everything first");
 | 
						|
  ot->prop = RNA_def_enum(ot->srna, "type", prop_select_grouped_types, 0, "Type", "");
 | 
						|
}
 | 
						|
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/** \name (De)select All
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static int object_select_all_exec(bContext *C, wmOperator *op)
 | 
						|
{
 | 
						|
  ViewLayer *view_layer = CTX_data_view_layer(C);
 | 
						|
  View3D *v3d = CTX_wm_view3d(C);
 | 
						|
  int action = RNA_enum_get(op->ptr, "action");
 | 
						|
  bool any_visible = false;
 | 
						|
 | 
						|
  bool changed = ED_object_base_deselect_all_ex(view_layer, v3d, action, &any_visible);
 | 
						|
 | 
						|
  if (changed) {
 | 
						|
    Scene *scene = CTX_data_scene(C);
 | 
						|
    DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
 | 
						|
    WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
 | 
						|
 | 
						|
    return OPERATOR_FINISHED;
 | 
						|
  }
 | 
						|
  else if (any_visible == false) {
 | 
						|
    /* TODO(campbell): Looks like we could remove this,
 | 
						|
     * if not comment should say why its needed. */
 | 
						|
    return OPERATOR_PASS_THROUGH;
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    return OPERATOR_CANCELLED;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void OBJECT_OT_select_all(wmOperatorType *ot)
 | 
						|
{
 | 
						|
 | 
						|
  /* identifiers */
 | 
						|
  ot->name = "(De)select All";
 | 
						|
  ot->description = "Change selection of all visible objects in scene";
 | 
						|
  ot->idname = "OBJECT_OT_select_all";
 | 
						|
 | 
						|
  /* api callbacks */
 | 
						|
  ot->exec = object_select_all_exec;
 | 
						|
  ot->poll = objects_selectable_poll;
 | 
						|
 | 
						|
  /* flags */
 | 
						|
  ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 | 
						|
 | 
						|
  WM_operator_properties_select_all(ot);
 | 
						|
}
 | 
						|
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/** \name Select In The Same Collection
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static int object_select_same_collection_exec(bContext *C, wmOperator *op)
 | 
						|
{
 | 
						|
  Main *bmain = CTX_data_main(C);
 | 
						|
  Collection *collection;
 | 
						|
  char collection_name[MAX_ID_NAME];
 | 
						|
 | 
						|
  /* passthrough if no objects are visible */
 | 
						|
  if (CTX_DATA_COUNT(C, visible_bases) == 0) {
 | 
						|
    return OPERATOR_PASS_THROUGH;
 | 
						|
  }
 | 
						|
 | 
						|
  RNA_string_get(op->ptr, "collection", collection_name);
 | 
						|
 | 
						|
  collection = (Collection *)BKE_libblock_find_name(bmain, ID_GR, collection_name);
 | 
						|
 | 
						|
  if (!collection) {
 | 
						|
    return OPERATOR_PASS_THROUGH;
 | 
						|
  }
 | 
						|
 | 
						|
  CTX_DATA_BEGIN (C, Base *, base, visible_bases) {
 | 
						|
    if (((base->flag & BASE_SELECTED) == 0) && ((base->flag & BASE_SELECTABLE) != 0)) {
 | 
						|
      if (BKE_collection_has_object(collection, base->object)) {
 | 
						|
        ED_object_base_select(base, BA_SELECT);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  CTX_DATA_END;
 | 
						|
 | 
						|
  Scene *scene = CTX_data_scene(C);
 | 
						|
  DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
 | 
						|
  WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
 | 
						|
 | 
						|
  return OPERATOR_FINISHED;
 | 
						|
}
 | 
						|
 | 
						|
void OBJECT_OT_select_same_collection(wmOperatorType *ot)
 | 
						|
{
 | 
						|
 | 
						|
  /* identifiers */
 | 
						|
  ot->name = "Select Same Collection";
 | 
						|
  ot->description = "Select object in the same collection";
 | 
						|
  ot->idname = "OBJECT_OT_select_same_collection";
 | 
						|
 | 
						|
  /* api callbacks */
 | 
						|
  ot->exec = object_select_same_collection_exec;
 | 
						|
  ot->poll = objects_selectable_poll;
 | 
						|
 | 
						|
  /* flags */
 | 
						|
  ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 | 
						|
 | 
						|
  RNA_def_string(
 | 
						|
      ot->srna, "collection", NULL, MAX_ID_NAME, "Collection", "Name of the collection to select");
 | 
						|
}
 | 
						|
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/** \name Select Mirror
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static int object_select_mirror_exec(bContext *C, wmOperator *op)
 | 
						|
{
 | 
						|
  Main *bmain = CTX_data_main(C);
 | 
						|
  Scene *scene = CTX_data_scene(C);
 | 
						|
  ViewLayer *view_layer = CTX_data_view_layer(C);
 | 
						|
  bool extend;
 | 
						|
 | 
						|
  extend = RNA_boolean_get(op->ptr, "extend");
 | 
						|
 | 
						|
  CTX_DATA_BEGIN (C, Base *, primbase, selected_bases) {
 | 
						|
    char name_flip[MAXBONENAME];
 | 
						|
 | 
						|
    BLI_string_flip_side_name(name_flip, primbase->object->id.name + 2, true, sizeof(name_flip));
 | 
						|
 | 
						|
    if (!STREQ(name_flip, primbase->object->id.name + 2)) {
 | 
						|
      Object *ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, name_flip);
 | 
						|
      if (ob) {
 | 
						|
        Base *secbase = BKE_view_layer_base_find(view_layer, ob);
 | 
						|
 | 
						|
        if (secbase) {
 | 
						|
          ED_object_base_select(secbase, BA_SELECT);
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    if (extend == false) {
 | 
						|
      ED_object_base_select(primbase, BA_DESELECT);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  CTX_DATA_END;
 | 
						|
 | 
						|
  /* undo? */
 | 
						|
  DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
 | 
						|
  WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
 | 
						|
 | 
						|
  return OPERATOR_FINISHED;
 | 
						|
}
 | 
						|
 | 
						|
void OBJECT_OT_select_mirror(wmOperatorType *ot)
 | 
						|
{
 | 
						|
 | 
						|
  /* identifiers */
 | 
						|
  ot->name = "Select Mirror";
 | 
						|
  ot->description = "Select the Mirror objects of the selected object eg. L.sword -> R.sword";
 | 
						|
  ot->idname = "OBJECT_OT_select_mirror";
 | 
						|
 | 
						|
  /* api callbacks */
 | 
						|
  ot->exec = object_select_mirror_exec;
 | 
						|
  ot->poll = objects_selectable_poll;
 | 
						|
 | 
						|
  /* flags */
 | 
						|
  ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 | 
						|
 | 
						|
  RNA_def_boolean(
 | 
						|
      ot->srna, "extend", 0, "Extend", "Extend selection instead of deselecting everything first");
 | 
						|
}
 | 
						|
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/** \name Select More/Less
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static bool object_select_more_less(bContext *C, const bool select)
 | 
						|
{
 | 
						|
  ViewLayer *view_layer = CTX_data_view_layer(C);
 | 
						|
 | 
						|
  for (Base *base = view_layer->object_bases.first; base; base = base->next) {
 | 
						|
    Object *ob = base->object;
 | 
						|
    ob->flag &= ~OB_DONE;
 | 
						|
    ob->id.tag &= ~LIB_TAG_DOIT;
 | 
						|
    /* parent may be in another scene */
 | 
						|
    if (ob->parent) {
 | 
						|
      ob->parent->flag &= ~OB_DONE;
 | 
						|
      ob->parent->id.tag &= ~LIB_TAG_DOIT;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  ListBase ctx_base_list;
 | 
						|
  CollectionPointerLink *ctx_base;
 | 
						|
  CTX_data_selectable_bases(C, &ctx_base_list);
 | 
						|
 | 
						|
  CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
 | 
						|
    ob->flag |= OB_DONE;
 | 
						|
  }
 | 
						|
  CTX_DATA_END;
 | 
						|
 | 
						|
  for (ctx_base = ctx_base_list.first; ctx_base; ctx_base = ctx_base->next) {
 | 
						|
    Object *ob = ((Base *)ctx_base->ptr.data)->object;
 | 
						|
    if (ob->parent) {
 | 
						|
      if ((ob->flag & OB_DONE) != (ob->parent->flag & OB_DONE)) {
 | 
						|
        ob->id.tag |= LIB_TAG_DOIT;
 | 
						|
        ob->parent->id.tag |= LIB_TAG_DOIT;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  bool changed = false;
 | 
						|
  const short select_mode = select ? BA_SELECT : BA_DESELECT;
 | 
						|
  const short select_flag = select ? BASE_SELECTED : 0;
 | 
						|
 | 
						|
  for (ctx_base = ctx_base_list.first; ctx_base; ctx_base = ctx_base->next) {
 | 
						|
    Base *base = ctx_base->ptr.data;
 | 
						|
    Object *ob = base->object;
 | 
						|
    if ((ob->id.tag & LIB_TAG_DOIT) && ((base->flag & BASE_SELECTED) != select_flag)) {
 | 
						|
      ED_object_base_select(base, select_mode);
 | 
						|
      changed = true;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  BLI_freelistN(&ctx_base_list);
 | 
						|
 | 
						|
  return changed;
 | 
						|
}
 | 
						|
 | 
						|
static int object_select_more_exec(bContext *C, wmOperator *UNUSED(op))
 | 
						|
{
 | 
						|
  bool changed = object_select_more_less(C, true);
 | 
						|
 | 
						|
  if (changed) {
 | 
						|
    Scene *scene = CTX_data_scene(C);
 | 
						|
    DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
 | 
						|
    WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
 | 
						|
    return OPERATOR_FINISHED;
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    return OPERATOR_CANCELLED;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void OBJECT_OT_select_more(wmOperatorType *ot)
 | 
						|
{
 | 
						|
  /* identifiers */
 | 
						|
  ot->name = "Select More";
 | 
						|
  ot->idname = "OBJECT_OT_select_more";
 | 
						|
  ot->description = "Select connected parent/child objects";
 | 
						|
 | 
						|
  /* api callbacks */
 | 
						|
  ot->exec = object_select_more_exec;
 | 
						|
  ot->poll = ED_operator_objectmode;
 | 
						|
 | 
						|
  /* flags */
 | 
						|
  ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 | 
						|
}
 | 
						|
 | 
						|
static int object_select_less_exec(bContext *C, wmOperator *UNUSED(op))
 | 
						|
{
 | 
						|
  bool changed = object_select_more_less(C, false);
 | 
						|
 | 
						|
  if (changed) {
 | 
						|
    Scene *scene = CTX_data_scene(C);
 | 
						|
    DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
 | 
						|
    WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
 | 
						|
    return OPERATOR_FINISHED;
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    return OPERATOR_CANCELLED;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void OBJECT_OT_select_less(wmOperatorType *ot)
 | 
						|
{
 | 
						|
  /* identifiers */
 | 
						|
  ot->name = "Select Less";
 | 
						|
  ot->idname = "OBJECT_OT_select_less";
 | 
						|
  ot->description = "Deselect objects at the boundaries of parent/child relationships";
 | 
						|
 | 
						|
  /* api callbacks */
 | 
						|
  ot->exec = object_select_less_exec;
 | 
						|
  ot->poll = ED_operator_objectmode;
 | 
						|
 | 
						|
  /* flags */
 | 
						|
  ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 | 
						|
}
 | 
						|
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/** \name Select Random
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static int object_select_random_exec(bContext *C, wmOperator *op)
 | 
						|
{
 | 
						|
  const float randfac = RNA_float_get(op->ptr, "percent") / 100.0f;
 | 
						|
  const int seed = WM_operator_properties_select_random_seed_increment_get(op);
 | 
						|
  const bool select = (RNA_enum_get(op->ptr, "action") == SEL_SELECT);
 | 
						|
 | 
						|
  RNG *rng = BLI_rng_new_srandom(seed);
 | 
						|
 | 
						|
  CTX_DATA_BEGIN (C, Base *, base, selectable_bases) {
 | 
						|
    if (BLI_rng_get_float(rng) < randfac) {
 | 
						|
      ED_object_base_select(base, select);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  CTX_DATA_END;
 | 
						|
 | 
						|
  BLI_rng_free(rng);
 | 
						|
 | 
						|
  Scene *scene = CTX_data_scene(C);
 | 
						|
  DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
 | 
						|
  WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
 | 
						|
 | 
						|
  return OPERATOR_FINISHED;
 | 
						|
}
 | 
						|
 | 
						|
void OBJECT_OT_select_random(wmOperatorType *ot)
 | 
						|
{
 | 
						|
  /* identifiers */
 | 
						|
  ot->name = "Select Random";
 | 
						|
  ot->description = "Set select on random visible objects";
 | 
						|
  ot->idname = "OBJECT_OT_select_random";
 | 
						|
 | 
						|
  /* api callbacks */
 | 
						|
  /*ot->invoke = object_select_random_invoke XXX - need a number popup ;*/
 | 
						|
  ot->exec = object_select_random_exec;
 | 
						|
  ot->poll = objects_selectable_poll;
 | 
						|
 | 
						|
  /* flags */
 | 
						|
  ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 | 
						|
 | 
						|
  /* properties */
 | 
						|
  WM_operator_properties_select_random(ot);
 | 
						|
}
 | 
						|
 | 
						|
/** \} */
 |