Merge branch 'master' into blender2.8
authorCampbell Barton <ideasman42@gmail.com>
Thu, 7 Sep 2017 17:18:39 +0000 (03:18 +1000)
committerCampbell Barton <ideasman42@gmail.com>
Thu, 7 Sep 2017 17:18:39 +0000 (03:18 +1000)
1  2 
source/blender/editors/interface/interface_handlers.c
source/blender/editors/interface/interface_ops.c
source/blender/python/intern/bpy_rna.c

index 75b947d455e506222b58141be185f14ad8d9af84,031011ddaee03f8987271a58504955432a9a867c..cc3faf82db0a00a35d13d7d123ade7892f469127
@@@ -186,6 -186,7 +186,6 @@@ typedef struct uiSelectContextStore 
        uiSelectContextElem *elems;
        int elems_len;
        bool do_free;
 -      bool is_enabled;
        /* When set, simply copy values (don't apply difference).
         * Rules are:
         * - dragging numbers uses delta.
@@@ -201,9 -202,7 +201,9 @@@ static void ui_selectcontext_apply
          bContext *C, uiBut *but, struct uiSelectContextStore *selctx_data,
          const double value, const double value_orig);
  
 +#if 0
  #define IS_ALLSELECT_EVENT(event) ((event)->alt != 0)
 +#endif
  
  /** just show a tinted color so users know its activated */
  #define UI_BUT_IS_SELECT_CONTEXT UI_BUT_NODE_ACTIVE
@@@ -1180,11 -1179,14 +1180,11 @@@ static void ui_multibut_states_apply(bC
                                ui_but_execute_begin(C, ar, but, &active_back);
  
  #ifdef USE_ALLSELECT
 -                              if (data->select_others.is_enabled) {
 -                                      /* init once! */
 -                                      if (mbut_state->select_others.elems_len == 0) {
 -                                              ui_selectcontext_begin(C, but, &mbut_state->select_others);
 -                                      }
 -                                      if (mbut_state->select_others.elems_len == 0) {
 -                                              mbut_state->select_others.elems_len = -1;
 -                                      }
 +                              if (mbut_state->select_others.elems_len == 0) {
 +                                      ui_selectcontext_begin(C, but, &mbut_state->select_others);
 +                              }
 +                              if (mbut_state->select_others.elems_len == 0) {
 +                                      mbut_state->select_others.elems_len = -1;
                                }
  
                                /* needed so we apply the right deltas */
@@@ -1438,87 -1440,82 +1438,82 @@@ static bool ui_selectcontext_begin
                const bool is_array = RNA_property_array_check(prop);
                const int rna_type = RNA_property_type(prop);
  
-               if (!UI_context_copy_to_selected_list(C, &ptr, prop, &lb, &use_path_from_id, &path)) {
-                       goto finally;
-               }
-               selctx_data->elems_len = BLI_listbase_count(&lb);
-               if (selctx_data->elems_len == 0) {
-                       goto finally;
-               }
-               selctx_data->elems = MEM_mallocN(sizeof(uiSelectContextElem) * selctx_data->elems_len, __func__);
-               for (i = 0, link = lb.first; i < selctx_data->elems_len; i++, link = link->next) {
-                       uiSelectContextElem *other = &selctx_data->elems[i];
-                       /* TODO,. de-duplicate copy_to_selected_button */
-                       if (link->ptr.data != ptr.data) {
-                               if (use_path_from_id) {
-                                       /* Path relative to ID. */
-                                       lprop = NULL;
-                                       RNA_id_pointer_create(link->ptr.id.data, &idptr);
-                                       RNA_path_resolve_property(&idptr, path, &lptr, &lprop);
-                               }
-                               else if (path) {
-                                       /* Path relative to elements from list. */
-                                       lprop = NULL;
-                                       RNA_path_resolve_property(&link->ptr, path, &lptr, &lprop);
-                               }
-                               else {
-                                       lptr = link->ptr;
-                                       lprop = prop;
-                               }
+               if (UI_context_copy_to_selected_list(C, &ptr, prop, &lb, &use_path_from_id, &path) &&
+                   !BLI_listbase_is_empty(&lb))
+               {
+                       selctx_data->elems_len = BLI_listbase_count(&lb);
+                       selctx_data->elems = MEM_mallocN(sizeof(uiSelectContextElem) * selctx_data->elems_len, __func__);
+                       for (i = 0, link = lb.first; i < selctx_data->elems_len; i++, link = link->next) {
+                               uiSelectContextElem *other = &selctx_data->elems[i];
+                               /* TODO,. de-duplicate copy_to_selected_button */
+                               if (link->ptr.data != ptr.data) {
+                                       if (use_path_from_id) {
+                                               /* Path relative to ID. */
+                                               lprop = NULL;
+                                               RNA_id_pointer_create(link->ptr.id.data, &idptr);
+                                               RNA_path_resolve_property(&idptr, path, &lptr, &lprop);
+                                       }
+                                       else if (path) {
+                                               /* Path relative to elements from list. */
+                                               lprop = NULL;
+                                               RNA_path_resolve_property(&link->ptr, path, &lptr, &lprop);
+                                       }
+                                       else {
+                                               lptr = link->ptr;
+                                               lprop = prop;
+                                       }
  
-                               /* lptr might not be the same as link->ptr! */
-                               if ((lptr.data != ptr.data) &&
-                                   (lprop == prop) &&
-                                   RNA_property_editable(&lptr, lprop))
-                               {
-                                       other->ptr = lptr;
-                                       if (is_array) {
-                                               if (rna_type == PROP_FLOAT) {
-                                                       other->val_f = RNA_property_float_get_index(&lptr, lprop, index);
-                                               }
-                                               else if (rna_type == PROP_INT) {
-                                                       other->val_i = RNA_property_int_get_index(&lptr, lprop, index);
-                                               }
-                                               /* ignored for now */
+                                       /* lptr might not be the same as link->ptr! */
+                                       if ((lptr.data != ptr.data) &&
+                                           (lprop == prop) &&
+                                           RNA_property_editable(&lptr, lprop))
+                                       {
+                                               other->ptr = lptr;
+                                               if (is_array) {
+                                                       if (rna_type == PROP_FLOAT) {
+                                                               other->val_f = RNA_property_float_get_index(&lptr, lprop, index);
+                                                       }
+                                                       else if (rna_type == PROP_INT) {
+                                                               other->val_i = RNA_property_int_get_index(&lptr, lprop, index);
+                                                       }
+                                                       /* ignored for now */
  #if 0
-                                               else if (rna_type == PROP_BOOLEAN) {
-                                                       other->val_b = RNA_property_boolean_get_index(&lptr, lprop, index);
-                                               }
+                                                       else if (rna_type == PROP_BOOLEAN) {
+                                                               other->val_b = RNA_property_boolean_get_index(&lptr, lprop, index);
+                                                       }
  #endif
-                                       }
-                                       else {
-                                               if (rna_type == PROP_FLOAT) {
-                                                       other->val_f = RNA_property_float_get(&lptr, lprop);
                                                }
-                                               else if (rna_type == PROP_INT) {
-                                                       other->val_i = RNA_property_int_get(&lptr, lprop);
-                                               }
-                                               /* ignored for now */
+                                               else {
+                                                       if (rna_type == PROP_FLOAT) {
+                                                               other->val_f = RNA_property_float_get(&lptr, lprop);
+                                                       }
+                                                       else if (rna_type == PROP_INT) {
+                                                               other->val_i = RNA_property_int_get(&lptr, lprop);
+                                                       }
+                                                       /* ignored for now */
  #if 0
-                                               else if (rna_type == PROP_BOOLEAN) {
-                                                       other->val_b = RNA_property_boolean_get(&lptr, lprop);
-                                               }
-                                               else if (rna_type == PROP_ENUM) {
-                                                       other->val_i = RNA_property_enum_get(&lptr, lprop);
-                                               }
+                                                       else if (rna_type == PROP_BOOLEAN) {
+                                                               other->val_b = RNA_property_boolean_get(&lptr, lprop);
+                                                       }
+                                                       else if (rna_type == PROP_ENUM) {
+                                                               other->val_i = RNA_property_enum_get(&lptr, lprop);
+                                                       }
  #endif
-                                       }
+                                               }
  
-                                       continue;
+                                               continue;
+                                       }
                                }
+                               selctx_data->elems_len -= 1;
+                               i -= 1;
                        }
  
-                       selctx_data->elems_len -= 1;
-                       i -= 1;
+                       success = (selctx_data->elems_len != 0);
                }
        }
  
-       success = (selctx_data->elems_len != 0);
- finally:
        if (selctx_data->elems_len == 0) {
                MEM_SAFE_FREE(selctx_data->elems);
        }
@@@ -2072,7 -2069,12 +2067,7 @@@ static void ui_apply_but(bContext *C, u
                else
  #  endif
                if (data->select_others.elems_len == 0) {
 -                      wmWindow *win = CTX_wm_window(C);
 -                      /* may have been enabled before activating */
 -                      if (data->select_others.is_enabled || IS_ALLSELECT_EVENT(win->eventstate)) {
 -                              ui_selectcontext_begin(C, but, &data->select_others);
 -                              data->select_others.is_enabled = true;
 -                      }
 +                      ui_selectcontext_begin(C, but, &data->select_others);
                }
                if (data->select_others.elems_len == 0) {
                        /* dont check again */
                        break;
                case UI_BTYPE_ROW:
                case UI_BTYPE_LISTROW:
 +              case UI_BTYPE_TAB:
                        ui_apply_but_ROW(C, block, but, data);
                        break;
                case UI_BTYPE_SCROLL:
@@@ -3075,7 -3076,11 +3070,7 @@@ static void ui_textedit_begin(bContext 
  
  #ifdef USE_ALLSELECT
        if (is_num_but) {
 -              if (IS_ALLSELECT_EVENT(win->eventstate)) {
 -                      data->select_others.is_enabled = true;
 -                      data->select_others.is_copy = true;
 -
 -              }
 +              data->select_others.is_copy = true;
        }
  #endif
  
@@@ -3162,9 -3167,6 +3157,9 @@@ static void ui_textedit_end(bContext *C
  
                        ui_searchbox_free(C, data->searchbox);
                        data->searchbox = NULL;
 +                      if (but->free_search_arg) {
 +                              MEM_SAFE_FREE(but->search_arg);
 +                      }
                }
                
                but->editstr = NULL;
@@@ -3680,6 -3682,15 +3675,6 @@@ static void ui_block_open_begin(bContex
                        data->menu->popup = but->block->handle->popup;
        }
  
 -#ifdef USE_ALLSELECT
 -      {
 -              wmWindow *win = CTX_wm_window(C);
 -              if (IS_ALLSELECT_EVENT(win->eventstate)) {
 -                      data->select_others.is_enabled = true;
 -              }
 -      }
 -#endif
 -
        /* this makes adjacent blocks auto open from now on */
        //if (but->block->auto_open == 0) but->block->auto_open = 1;
  }
@@@ -3861,18 -3872,6 +3856,18 @@@ static int ui_do_but_KEYEVT
        return WM_UI_HANDLER_CONTINUE;
  }
  
 +static int ui_do_but_TAB(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
 +{
 +      if (data->state == BUTTON_STATE_HIGHLIGHT) {
 +              if (ELEM(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val == KM_RELEASE) {
 +                      button_activate_state(C, but, BUTTON_STATE_EXIT);
 +                      return WM_UI_HANDLER_CONTINUE;
 +              }
 +      }
 +
 +      return WM_UI_HANDLER_CONTINUE;
 +}
 +
  static bool ui_but_is_mouse_over_icon_extra(const ARegion *region, uiBut *but, const int mouse_xy[2])
  {
        int x = mouse_xy[0], y = mouse_xy[1];
@@@ -5217,8 -5216,7 +5212,8 @@@ static int ui_do_but_COLOR
                                if (!event->ctrl) {
                                        float color[3];
                                        Scene *scene = CTX_data_scene(C);
 -                                      Paint *paint = BKE_paint_get_active(scene);
 +                                      SceneLayer *sl = CTX_data_scene_layer(C);
 +                                      Paint *paint = BKE_paint_get_active(scene, sl);
                                        Brush *brush = BKE_paint_brush(paint);
  
                                        if (brush->flag & BRUSH_USE_GRADIENT) {
@@@ -6126,7 -6124,6 +6121,7 @@@ static int ui_do_but_CURVE
        int mx, my, a;
        bool changed = false;
        Scene *scene = CTX_data_scene(C);
 +      SceneLayer *sl = CTX_data_scene_layer(C);
  
        mx = event->x;
        my = event->y;
                                }
                                else {
                                        curvemapping_changed(cumap, true);  /* remove doubles */
 -                                      BKE_paint_invalidate_cursor_overlay(scene, cumap);
 +                                      BKE_paint_invalidate_cursor_overlay(scene, sl, cumap);
                                }
                        }
  
@@@ -7135,9 -7132,6 +7130,9 @@@ static int ui_do_button(bContext *C, ui
                case UI_BTYPE_HOTKEY_EVENT:
                        retval = ui_do_but_HOTKEYEVT(C, but, data, event);
                        break;
 +              case UI_BTYPE_TAB:
 +                      retval = ui_do_but_TAB(C, but, data, event);
 +                      break;
                case UI_BTYPE_BUT_TOGGLE:
                case UI_BTYPE_TOGGLE:
                case UI_BTYPE_ICON_TOGGLE:
index 95ea2b87cba64c65ae34fc634a3a556a1d4ed951,d0c110d1db5f435ed91a5c220639d1932c374afe..40ca2d41372a375d2d1b45ff4fe66f6a477b3d3f
  #include "BLT_lang.h"
  
  #include "BKE_context.h"
 +#include "BKE_idprop.h"
 +#include "BKE_layer.h"
  #include "BKE_screen.h"
  #include "BKE_global.h"
  #include "BKE_node.h"
  #include "BKE_text.h" /* for UI_OT_reports_to_text */
  #include "BKE_report.h"
  
 +#include "DEG_depsgraph.h"
 +
  #include "RNA_access.h"
  #include "RNA_define.h"
 +#include "RNA_types.h"
  
  #include "UI_interface.h"
  
@@@ -333,125 -328,6 +333,125 @@@ static void UI_OT_unset_property_button
        ot->flag = OPTYPE_UNDO;
  }
  
 +/* Use/Unuse Property Button Operator ------------------------ */
 +
 +static int use_property_button_exec(bContext *C, wmOperator *UNUSED(op))
 +{
 +      PointerRNA ptr, scene_props_ptr;
 +      PropertyRNA *prop;
 +      IDProperty *props;
 +
 +      uiBut *but = UI_context_active_but_get(C);
 +
 +      prop = but->rnaprop;
 +      ptr = but->rnapoin;
 +      props = (IDProperty *)ptr.data;
 +      /* XXX Using existing data struct to pass another RNAPointer */
 +      scene_props_ptr = but->rnasearchpoin;
 +
 +      const char *identifier = RNA_property_identifier(prop);
 +      if (IDP_GetPropertyFromGroup(props, identifier)) {
 +              return OPERATOR_CANCELLED;
 +      }
 +
 +      int array_len = RNA_property_array_length(&scene_props_ptr, prop);
 +      bool is_array = array_len != 0;
 +
 +      switch (RNA_property_type(prop)) {
 +              case PROP_FLOAT:
 +              {
 +                      if (is_array) {
 +                              float values[RNA_MAX_ARRAY_LENGTH];
 +                              RNA_property_float_get_array(&scene_props_ptr, prop, values);
 +                              BKE_collection_engine_property_add_float_array(props, identifier, values, array_len);
 +                      }
 +                      else {
 +                              float value = RNA_property_float_get(&scene_props_ptr, prop);
 +                              BKE_collection_engine_property_add_float(props, identifier, value);
 +                      }
 +                      break;
 +              }
 +              case PROP_ENUM:
 +              {
 +                      int value = RNA_enum_get(&scene_props_ptr, identifier);
 +                      BKE_collection_engine_property_add_int(props, identifier, value);
 +                      break;
 +              }
 +              case PROP_INT:
 +              {
 +                      int value = RNA_int_get(&scene_props_ptr, identifier);
 +                      BKE_collection_engine_property_add_int(props, identifier, value);
 +                      break;
 +              }
 +              case PROP_BOOLEAN:
 +              {
 +                      int value = RNA_boolean_get(&scene_props_ptr, identifier);
 +                      BKE_collection_engine_property_add_bool(props, identifier, value);
 +                      break;
 +              }
 +              case PROP_STRING:
 +              case PROP_POINTER:
 +              case PROP_COLLECTION:
 +              default:
 +                      break;
 +      }
 +
 +      /* TODO(sergey): Use proper flag for tagging here. */
 +      DEG_id_tag_update((ID *)CTX_data_scene(C), 0);
 +
 +      return OPERATOR_FINISHED;
 +}
 +
 +static void UI_OT_use_property_button(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name = "Use property";
 +      ot->idname = "UI_OT_use_property_button";
 +      ot->description = "Create a property";
 +
 +      /* callbacks */
 +      ot->poll = ED_operator_regionactive;
 +      ot->exec = use_property_button_exec;
 +
 +      /* flags */
 +      ot->flag = OPTYPE_UNDO;
 +}
 +
 +static int unuse_property_button_exec(bContext *C, wmOperator *UNUSED(op))
 +{
 +      PointerRNA ptr;
 +      PropertyRNA *prop;
 +      int index;
 +
 +      /* try to unset the nominated property */
 +      UI_context_active_but_prop_get(C, &ptr, &prop, &index);
 +      const char *identifier = RNA_property_identifier(prop);
 +
 +      IDProperty *props = (IDProperty *)ptr.data;
 +      IDProperty *prop_to_remove = IDP_GetPropertyFromGroup(props, identifier);
 +      IDP_FreeFromGroup(props, prop_to_remove);
 +
 +      /* TODO(sergey): Use proper flag for tagging here. */
 +      DEG_id_tag_update((ID *)CTX_data_scene(C), 0);
 +
 +      return OPERATOR_FINISHED;
 +}
 +
 +static void UI_OT_unuse_property_button(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name = "Unuse property";
 +      ot->idname = "UI_OT_unuse_property_button";
 +      ot->description = "Remove a property";
 +
 +      /* callbacks */
 +      ot->poll = ED_operator_regionactive;
 +      ot->exec = unuse_property_button_exec;
 +
 +      /* flags */
 +      ot->flag = OPTYPE_UNDO;
 +}
 +
  /* Copy To Selected Operator ------------------------ */
  
  bool UI_context_copy_to_selected_list(
@@@ -621,51 -497,51 +621,51 @@@ static bool copy_to_selected_button(bCo
                char *path = NULL;
                bool use_path_from_id;
                CollectionPointerLink *link;
-               ListBase lb;
-               if (!UI_context_copy_to_selected_list(C, &ptr, prop, &lb, &use_path_from_id, &path))
-                       return success;
+               ListBase lb = {NULL};
  
-               for (link = lb.first; link; link = link->next) {
-                       if (link->ptr.data != ptr.data) {
-                               if (use_path_from_id) {
-                                       /* Path relative to ID. */
-                                       lprop = NULL;
-                                       RNA_id_pointer_create(link->ptr.id.data, &idptr);
-                                       RNA_path_resolve_property(&idptr, path, &lptr, &lprop);
-                               }
-                               else if (path) {
-                                       /* Path relative to elements from list. */
-                                       lprop = NULL;
-                                       RNA_path_resolve_property(&link->ptr, path, &lptr, &lprop);
-                               }
-                               else {
-                                       lptr = link->ptr;
-                                       lprop = prop;
-                               }
+               if (UI_context_copy_to_selected_list(C, &ptr, prop, &lb, &use_path_from_id, &path) &&
+                   !BLI_listbase_is_empty(&lb))
+               {
+                       for (link = lb.first; link; link = link->next) {
+                               if (link->ptr.data != ptr.data) {
+                                       if (use_path_from_id) {
+                                               /* Path relative to ID. */
+                                               lprop = NULL;
+                                               RNA_id_pointer_create(link->ptr.id.data, &idptr);
+                                               RNA_path_resolve_property(&idptr, path, &lptr, &lprop);
+                                       }
+                                       else if (path) {
+                                               /* Path relative to elements from list. */
+                                               lprop = NULL;
+                                               RNA_path_resolve_property(&link->ptr, path, &lptr, &lprop);
+                                       }
+                                       else {
+                                               lptr = link->ptr;
+                                               lprop = prop;
+                                       }
  
-                               if (lptr.data == ptr.data) {
-                                       /* lptr might not be the same as link->ptr! */
-                                       continue;
-                               }
+                                       if (lptr.data == ptr.data) {
+                                               /* lptr might not be the same as link->ptr! */
+                                               continue;
+                                       }
  
-                               if (lprop == prop) {
-                                       if (RNA_property_editable(&lptr, lprop)) {
-                                               if (poll) {
-                                                       success = true;
-                                                       break;
-                                               }
-                                               else {
-                                                       if (RNA_property_copy(&lptr, &ptr, prop, (all) ? -1 : index)) {
-                                                               RNA_property_update(C, &lptr, prop);
+                                       if (lprop == prop) {
+                                               if (RNA_property_editable(&lptr, lprop)) {
+                                                       if (poll) {
                                                                success = true;
+                                                               break;
+                                                       }
+                                                       else {
+                                                               if (RNA_property_copy(&lptr, &ptr, prop, (all) ? -1 : index)) {
+                                                                       RNA_property_update(C, &lptr, prop);
+                                                                       success = true;
+                                                               }
                                                        }
                                                }
                                        }
                                }
                        }
                }
                MEM_SAFE_FREE(path);
                BLI_freelistN(&lb);
        }
@@@ -1241,8 -1117,6 +1241,8 @@@ void ED_operatortypes_ui(void
        WM_operatortype_append(UI_OT_copy_python_command_button);
        WM_operatortype_append(UI_OT_reset_default_button);
        WM_operatortype_append(UI_OT_unset_property_button);
 +      WM_operatortype_append(UI_OT_use_property_button);
 +      WM_operatortype_append(UI_OT_unuse_property_button);
        WM_operatortype_append(UI_OT_copy_to_selected_button);
        WM_operatortype_append(UI_OT_reports_to_textblock);  /* XXX: temp? */
        WM_operatortype_append(UI_OT_drop_color);
index 2ffb2ba5291b9cc4c816b0ef1579a0dc090033d1,c323366454e8063dc7c087540f020afcddebf3b0..1368b610b53e1021cd26d8d74d524b7da76d4305
@@@ -310,7 -310,7 +310,7 @@@ static bool rna_id_write_error(PointerR
        ID *id = ptr->id.data;
        if (id) {
                const short idcode = GS(id->name);
 -              if (!ELEM(idcode, ID_WM, ID_SCR)) { /* may need more added here */
 +              if (!ELEM(idcode, ID_WM, ID_SCR, ID_WS)) { /* may need more added here */
                        const char *idtype = BKE_idcode_to_name(idcode);
                        const char *pyname;
                        if (key && PyUnicode_Check(key)) pyname = _PyUnicode_AsString(key);
@@@ -1842,28 -1842,19 +1842,28 @@@ static int pyrna_py_to_prop
                                 * class mixing if this causes problems in the future it should be removed.
                                 */
                                if ((ptr_type == &RNA_AnyType) &&
 -                                  (BPy_StructRNA_Check(value)) &&
 -                                  (RNA_struct_is_a(((BPy_StructRNA *)value)->ptr.type, &RNA_Operator)))
 +                                  (BPy_StructRNA_Check(value)))
                                {
 -                                      value = PyObject_GetAttrString(value, "properties");
 -                                      value_new = value;
 +                                      const StructRNA *base_type =
 +                                              RNA_struct_base_child_of(((const BPy_StructRNA *)value)->ptr.type, NULL);
 +                                      if (ELEM(base_type, &RNA_Operator, &RNA_Manipulator)) {
 +                                              value = PyObject_GetAttr(value, bpy_intern_str_properties);
 +                                              value_new = value;
 +                                      }
                                }
  
 -
 -                              /* if property is an OperatorProperties pointer and value is a map,
 +                              /* if property is an OperatorProperties/ManipulatorProperties pointer and value is a map,
                                 * forward back to pyrna_pydict_to_props */
 -                              if (RNA_struct_is_a(ptr_type, &RNA_OperatorProperties) && PyDict_Check(value)) {
 -                                      PointerRNA opptr = RNA_property_pointer_get(ptr, prop);
 -                                      return pyrna_pydict_to_props(&opptr, value, false, error_prefix);
 +                              if (PyDict_Check(value)) {
 +                                      const StructRNA *base_type = RNA_struct_base_child_of(ptr_type, NULL);
 +                                      if (base_type == &RNA_OperatorProperties) {
 +                                              PointerRNA opptr = RNA_property_pointer_get(ptr, prop);
 +                                              return pyrna_pydict_to_props(&opptr, value, false, error_prefix);
 +                                      }
 +                                      else if (base_type == &RNA_ManipulatorProperties) {
 +                                              PointerRNA opptr = RNA_property_pointer_get(ptr, prop);
 +                                              return pyrna_pydict_to_props(&opptr, value, false, error_prefix);
 +                                      }
                                }
  
                                /* another exception, allow to pass a collection as an RNA property */
@@@ -3716,6 -3707,66 +3716,66 @@@ static PyObject *pyrna_struct_type_reca
        return pyrna_struct_CreatePyObject(&r_ptr);
  }
  
+ /**
+  * \note Return value is borrowed, caller must incref.
+  */
+ static PyObject *pyrna_struct_bl_rna_find_subclass_recursive(PyObject *cls, const char *id)
+ {
+       PyObject *ret_test = NULL;
+       PyObject *subclasses = ((PyTypeObject *)cls)->tp_subclasses;
+       if (subclasses) {
+               /* Unfortunately we can't use the dict key because Python class names
+                * don't match the bl_idname used internally. */
+               BLI_assert(PyDict_CheckExact(subclasses));
+               PyObject *key = NULL;
+               Py_ssize_t pos = 0;
+               PyObject *value = NULL;
+               while (PyDict_Next(subclasses, &pos, &key, &value)) {
+                       BLI_assert(PyWeakref_CheckRef(value));
+                       PyObject *subcls = PyWeakref_GET_OBJECT(value);
+                       if (subcls != Py_None) {
+                               BPy_StructRNA *py_srna = (BPy_StructRNA *)PyDict_GetItem(
+                                       ((PyTypeObject *)subcls)->tp_dict, bpy_intern_str_bl_rna);
+                               if (py_srna) {
+                                       StructRNA *srna = py_srna->ptr.data;
+                                       if (STREQ(id, RNA_struct_identifier(srna))) {
+                                               ret_test = subcls;
+                                               break;
+                                       }
+                               }
+                               ret_test = pyrna_struct_bl_rna_find_subclass_recursive(subcls, id);
+                               if (ret_test) {
+                                       break;
+                               }
+                       }
+               }
+       }
+       return ret_test;
+ }
+ PyDoc_STRVAR(pyrna_struct_bl_rna_get_subclass_doc,
+ ".. classmethod:: bl_rna_get_subclass(id, default=None)\n"
+ "\n"
+ "   :arg id: The RNA type identifier.\n"
+ "   :type vector: string\n"
+ "   :return: The class or default when not found.\n"
+ "   :rtype: type\n"
+ );
+ static PyObject *pyrna_struct_bl_rna_get_subclass(PyObject *cls, PyObject *args)
+ {
+       char *id;
+       PyObject *ret_default = Py_None;
+       if (!PyArg_ParseTuple(args, "s|O:bl_rna_get_subclass", &id, &ret_default)) {
+               return NULL;
+       }
+       PyObject *ret = pyrna_struct_bl_rna_find_subclass_recursive(cls, id);
+       if (ret == NULL) {
+               ret = ret_default;
+       }
+       return Py_INCREF_RET(ret);
+ }
  static void pyrna_dir_members_py__add_keys(PyObject *list, PyObject *dict)
  {
        PyObject *list_tmp;
@@@ -5014,6 -5065,7 +5074,7 @@@ static struct PyMethodDef pyrna_struct_
        {"path_resolve", (PyCFunction)pyrna_struct_path_resolve, METH_VARARGS, pyrna_struct_path_resolve_doc},
        {"path_from_id", (PyCFunction)pyrna_struct_path_from_id, METH_VARARGS, pyrna_struct_path_from_id_doc},
        {"type_recast", (PyCFunction)pyrna_struct_type_recast, METH_NOARGS, pyrna_struct_type_recast_doc},
+       {"bl_rna_get_subclass", (PyCFunction) pyrna_struct_bl_rna_get_subclass, METH_VARARGS | METH_CLASS, pyrna_struct_bl_rna_get_subclass_doc},
        {"__dir__", (PyCFunction)pyrna_struct_dir, METH_NOARGS, NULL},
  
        /* experimental */
@@@ -6660,30 -6712,7 +6721,30 @@@ PyObject *pyrna_struct_CreatePyObject(P
        if (ptr->data == NULL && ptr->type == NULL) { /* Operator RNA has NULL data */
                Py_RETURN_NONE;
        }
 -      else {
 +
 +      /* New in 2.8x, since not many types support instancing
 +       * we may want to use a flag to avoid looping over all classes. - campbell */
 +      void **instance = ptr->data ? RNA_struct_instance(ptr) : NULL;
 +      if (instance && *instance) {
 +              pyrna = *instance;
 +
 +              /* Refine may have changed types after the first instance was created. */
 +              if (ptr->type == pyrna->ptr.type) {
 +                      Py_INCREF(pyrna);
 +                      return (PyObject *)pyrna;
 +              }
 +              else {
 +                      /* Existing users will need to use 'type_recast' method. */
 +                      Py_DECREF(pyrna);
 +                      *instance = NULL;
 +                      /* Continue as if no instance was made */
 +#if 0         /* no need to assign, will be written to next... */
 +                      pyrna = NULL;
 +#endif
 +              }
 +      }
 +
 +      {
                PyTypeObject *tp = (PyTypeObject *)pyrna_struct_Subtype(ptr);
  
                if (tp) {
                return NULL;
        }
  
 +      /* Blender's instance owns a reference (to avoid Python freeing it). */
 +      if (instance) {
 +              *instance = pyrna;
 +              Py_INCREF(pyrna);
 +      }
 +
        pyrna->ptr = *ptr;
  #ifdef PYRNA_FREE_SUPPORT
        pyrna->freeptr = false;
@@@ -7474,6 -7497,7 +7535,6 @@@ static int bpy_class_call(bContext *C, 
        PyObject *args;
        PyObject *ret = NULL, *py_srna = NULL, *py_class_instance = NULL, *parmitem;
        PyTypeObject *py_class;
 -      void **py_class_instance_store = NULL;
        PropertyRNA *parm;
        ParameterIterator iter;
        PointerRNA funcptr;
        PyGILState_STATE gilstate;
  
  #ifdef USE_PEDANTIC_WRITE
 -      const bool is_operator = RNA_struct_is_a(ptr->type, &RNA_Operator);
 +      const bool is_readonly_init = !(RNA_struct_is_a(ptr->type, &RNA_Operator) ||
 +                                      RNA_struct_is_a(ptr->type, &RNA_Manipulator));
        // const char *func_id = RNA_function_identifier(func);  /* UNUSED */
        /* testing, for correctness, not operator and not draw function */
        const bool is_readonly = !(RNA_function_flag(func) & FUNC_ALLOW_WRITE);
                                        py_class_instance = *instance;
                                        Py_INCREF(py_class_instance);
                                }
 -                              else {
 -                                      /* store the instance here once its created */
 -                                      py_class_instance_store = instance;
 -                              }
                        }
                }
                /* end exception */
                        if (py_class->tp_init) {
  #ifdef USE_PEDANTIC_WRITE
                                const int prev_write = rna_disallow_writes;
 -                              rna_disallow_writes = is_operator ? false : true;  /* only operators can write on __init__ */
 +                              rna_disallow_writes = is_readonly_init ? false : true;  /* only operators can write on __init__ */
  #endif
  
                                /* true in most cases even when the class its self doesn't define an __init__ function. */
                        if (py_class_instance == NULL) {
                                err = -1; /* so the error is not overridden below */
                        }
 -                      else if (py_class_instance_store) {
 -                              *py_class_instance_store = py_class_instance;
 -                              Py_INCREF(py_class_instance);
 -                      }
                }
        }