Compare commits
47 Commits
tmp-workbe
...
soc-2020-c
Author | SHA1 | Date | |
---|---|---|---|
![]() |
11aa61274d | ||
![]() |
1b8bc74b1f | ||
![]() |
705acb423b | ||
![]() |
114987ee42 | ||
![]() |
f8ebfd0e88 | ||
![]() |
0dfe6a6a37 | ||
![]() |
fd2d7e7a48 | ||
81aeb7ab08 | |||
![]() |
2772910b32 | ||
![]() |
79d256a06d | ||
![]() |
278b753088 | ||
![]() |
0137777137 | ||
a7e8e509a7 | |||
8da4917156 | |||
![]() |
4279181f10 | ||
![]() |
b1269effa9 | ||
![]() |
ca64458b61 | ||
04ef339f74 | |||
![]() |
1e68909800 | ||
![]() |
0de94b2b62 | ||
![]() |
d225ddec4a | ||
![]() |
8d0a3b5c1c | ||
![]() |
9ca8bf13d5 | ||
![]() |
2ae9183f4b | ||
a1dcb2aa70 | |||
9f60b06c8b | |||
![]() |
48b5c90975 | ||
![]() |
1cd405850c | ||
![]() |
cd610269dc | ||
![]() |
9fe9eb5240 | ||
![]() |
3c9ba5e994 | ||
![]() |
b83e559956 | ||
![]() |
fe0e9900d5 | ||
![]() |
3a519e00c5 | ||
![]() |
2eba146a88 | ||
9c530388ca | |||
![]() |
bf3c22ec55 | ||
![]() |
7f7a7efc62 | ||
52b2901156 | |||
![]() |
8344c0acc5 | ||
![]() |
3229939eae | ||
![]() |
d1921a46ca | ||
![]() |
b997a7cb51 | ||
![]() |
8a661f5dd1 | ||
![]() |
717dfa198c | ||
![]() |
935b518ec7 | ||
![]() |
4e04c9b874 |
Submodule release/datafiles/locale updated: 2b3c19f5f6...2a85baf731
@@ -105,6 +105,7 @@ const UserDef U_default = {
|
||||
|
||||
.autoexec_paths = {NULL},
|
||||
.user_menus = {NULL},
|
||||
.user_menus_group = {NULL},
|
||||
|
||||
.keyconfigstr = "blender",
|
||||
.undosteps = 32,
|
||||
@@ -234,5 +235,9 @@ const UserDef U_default = {
|
||||
.runtime =
|
||||
{
|
||||
.is_dirty = 0,
|
||||
|
||||
.um_space_select = 1,
|
||||
.um_context_select = 0,
|
||||
.um_item_select = NULL,
|
||||
},
|
||||
};
|
||||
|
Submodule release/scripts/addons updated: 49c39f59fb...5d33d1a1c2
Submodule release/scripts/addons_contrib updated: a52733b58d...f2f4a8b3bf
294
release/scripts/modules/rna_user_menus_ui.py
Normal file
294
release/scripts/modules/rna_user_menus_ui.py
Normal file
@@ -0,0 +1,294 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
__all__ = (
|
||||
"draw_user_menus",
|
||||
)
|
||||
|
||||
import bpy
|
||||
from bpy.app.translations import pgettext_iface as iface_
|
||||
from bpy.app.translations import contexts as i18n_contexts
|
||||
|
||||
def _indented_layout(layout, level):
|
||||
indentpx = 16
|
||||
if level == 0:
|
||||
level = 0.0001 # Tweak so that a percentage of 0 won't split by half
|
||||
indent = level * indentpx / bpy.context.region.width
|
||||
|
||||
split = layout.split(factor=indent)
|
||||
col = split.column()
|
||||
col = split.column()
|
||||
return col
|
||||
|
||||
def get_keymap(context, idname, ensure):
|
||||
prefs = context.preferences
|
||||
um = prefs.user_menus
|
||||
|
||||
if not idname:
|
||||
idname = um.active_group.idname
|
||||
|
||||
for km in context.window_manager.keyconfigs.user.keymaps:
|
||||
for kmi in km.keymap_items:
|
||||
if kmi.idname == "wm.call_user_menu":
|
||||
if kmi.properties.name == idname:
|
||||
return kmi
|
||||
if ensure:
|
||||
km = context.window_manager.keyconfigs.user.keymaps['Window']
|
||||
kmi = km.keymap_items.new("wm.call_user_menu",'NONE', 'ANY', shift=False, ctrl=False, alt=False)
|
||||
kmi.properties.idname = idname
|
||||
kmi.active = True
|
||||
return kmi
|
||||
|
||||
def draw_button(context, box, item, index):
|
||||
prefs = context.preferences
|
||||
um = prefs.user_menus
|
||||
|
||||
name = item.name
|
||||
if name == "":
|
||||
name = " "
|
||||
item.is_selected = item == um.active_item
|
||||
col = box.column(align=True)
|
||||
row = col.row(align=True)
|
||||
if item.type == "SEPARATOR":
|
||||
name = "___________"
|
||||
#icons = bpy.types.UILayout.bl_rna.functions["prop"].parameters["icon"].enum_items.keys()
|
||||
#selected_icon = icons[item.icon]
|
||||
row.prop(item, "is_selected", icon=item.icon, text=name, toggle=1)
|
||||
if item.type == "SUBMENU":
|
||||
sm = item.get_submenu()
|
||||
if um.active_group.type == "PIE" and index >= 0:
|
||||
row.operator("preferences.pie_menuitem_add", text="", icon='ADD').index = index
|
||||
row.operator("preferences.menuitem_remove", text="", icon='REMOVE')
|
||||
row.operator("preferences.menuitem_up", text="", icon='TRIA_UP')
|
||||
row.operator("preferences.menuitem_down", text="", icon='TRIA_DOWN')
|
||||
sub_box = col.box()
|
||||
sub_box = sub_box.column(align=True)
|
||||
draw_item(context, sub_box, sm.items_list)
|
||||
|
||||
def draw_item(context, box, items):
|
||||
for umi in items:
|
||||
draw_button(context, box, umi, -1)
|
||||
|
||||
def draw_item_box(context, row):
|
||||
prefs = context.preferences
|
||||
um = prefs.user_menus
|
||||
|
||||
box_line = row.box()
|
||||
box_col = box_line.column(align=True)
|
||||
|
||||
has_item = um.has_item()
|
||||
if not has_item:
|
||||
box_col.label(text="none")
|
||||
else:
|
||||
draw_item(context, box_col, um.get_current_menu().menu_items)
|
||||
|
||||
row = row.split(factor=0.9, align=True)
|
||||
col = row.column(align=True)
|
||||
|
||||
col.operator("preferences.menuitem_add", text="", icon='ADD').index = -1
|
||||
col.operator("preferences.menuitem_remove", text="", icon='REMOVE')
|
||||
col.operator("preferences.menuitem_up", text="", icon='TRIA_UP')
|
||||
col.operator("preferences.menuitem_down", text="", icon='TRIA_DOWN')
|
||||
row.separator()
|
||||
|
||||
def draw_pie_item(context, col, items, label, index):
|
||||
row = col.row()
|
||||
row.label(text=label)
|
||||
draw_button(context, row, items, index)
|
||||
|
||||
def draw_pie(context, row):
|
||||
prefs = context.preferences
|
||||
um = prefs.user_menus
|
||||
|
||||
cm = um.get_current_menu()
|
||||
if not cm:
|
||||
return
|
||||
col = row.column()
|
||||
draw_pie_item(context, col, cm.menu_items[0], "Left : ", 0)
|
||||
draw_pie_item(context, col, cm.menu_items[1], "Right : ", 1)
|
||||
draw_pie_item(context, col, cm.menu_items[2], "Down : ", 2)
|
||||
draw_pie_item(context, col, cm.menu_items[3], "Up : ", 3)
|
||||
draw_pie_item(context, col, cm.menu_items[4], "Upper left : ", 4)
|
||||
draw_pie_item(context, col, cm.menu_items[5], "Upper right : ", 5)
|
||||
draw_pie_item(context, col, cm.menu_items[6], "Lower left : ", 6)
|
||||
draw_pie_item(context, col, cm.menu_items[7], "Lower right : ", 7)
|
||||
row.separator()
|
||||
|
||||
|
||||
def draw_item_editor(context, row):
|
||||
prefs = context.preferences
|
||||
um = prefs.user_menus
|
||||
|
||||
col = row.column()
|
||||
|
||||
has_item = um.has_item()
|
||||
current = um.active_item
|
||||
if not has_item:
|
||||
col.label(text="No item in this list.")
|
||||
col.label(text="Add one or choose another list to get started")
|
||||
elif current:
|
||||
col.prop(current, "type")
|
||||
if (current.type != "SEPARATOR"):
|
||||
rowsub = col.row(align=True)
|
||||
rowsub.prop(current, "icon", icon=current.icon, text="")
|
||||
col.prop(current, "name")
|
||||
if (current.type == "OPERATOR"):
|
||||
umi_op = current.get_operator()
|
||||
col.prop(umi_op, "operator")
|
||||
box = col.box()
|
||||
box.template_user_menu_item_properties(umi_op)
|
||||
if (current.type == "MENU"):
|
||||
umi_pm = current.get_menu()
|
||||
col.prop(umi_pm, "id_name", text="ID name")
|
||||
if (current.type == "PROPERTY"):
|
||||
umi_prop = current.get_property()
|
||||
col.prop(umi_prop, "id_name")
|
||||
col.prop(umi_prop, "context")
|
||||
else:
|
||||
col.label(text="No item selected.")
|
||||
|
||||
def draw_user_menu_preference_expanded(context, layout, kmi, map_type):
|
||||
prefs = context.preferences
|
||||
um = prefs.user_menus
|
||||
|
||||
box = layout.box()
|
||||
sub = box.row()
|
||||
|
||||
if kmi:
|
||||
if map_type not in {'TEXTINPUT', 'TIMER'}:
|
||||
sub = box.column()
|
||||
subrow = sub.row(align=True)
|
||||
|
||||
if map_type == 'KEYBOARD':
|
||||
subrow.prop(kmi, "type", text="", event=True)
|
||||
subrow.prop(kmi, "value", text="")
|
||||
subrow_repeat = subrow.row(align=True)
|
||||
subrow_repeat.active = kmi.value in {'ANY', 'PRESS'}
|
||||
subrow_repeat.prop(kmi, "repeat", text="Repeat")
|
||||
elif map_type in {'MOUSE', 'NDOF'}:
|
||||
subrow.prop(kmi, "type", text="")
|
||||
subrow.prop(kmi, "value", text="")
|
||||
|
||||
subrow = sub.row()
|
||||
subrow.scale_x = 0.75
|
||||
subrow.prop(kmi, "any", toggle=True)
|
||||
subrow.prop(kmi, "shift", toggle=True)
|
||||
subrow.prop(kmi, "ctrl", toggle=True)
|
||||
subrow.prop(kmi, "alt", toggle=True)
|
||||
subrow.prop(kmi, "oskey", text="Cmd", toggle=True)
|
||||
subrow.prop(kmi, "key_modifier", text="", event=True)
|
||||
else:
|
||||
sub.label(text="No key set")
|
||||
|
||||
def draw_user_menu_preference(context, layout):
|
||||
prefs = context.preferences
|
||||
um = prefs.user_menus
|
||||
umg = um.active_group
|
||||
kmi = get_keymap(context, None, False)
|
||||
map_type = None
|
||||
|
||||
box = layout.box()
|
||||
row = box.row()
|
||||
|
||||
row.prop(um, "expanded", text="", emboss=False)
|
||||
|
||||
qf = um.menus[0]
|
||||
if umg != qf:
|
||||
row.prop(umg, "name")
|
||||
pie_text = "List"
|
||||
if umg.type == "PIE":
|
||||
pie_text = "Pie"
|
||||
row.prop(umg, "type", text=pie_text, expand=True)
|
||||
if kmi:
|
||||
row.prop(kmi, "map_type", text="")
|
||||
map_type = kmi.map_type
|
||||
if map_type == 'KEYBOARD':
|
||||
row.prop(kmi, "type", text="", full_event=True)
|
||||
elif map_type == 'MOUSE':
|
||||
row.prop(kmi, "type", text="", full_event=True)
|
||||
elif map_type == 'NDOF':
|
||||
row.prop(kmi, "type", text="", full_event=True)
|
||||
elif map_type == 'TWEAK':
|
||||
subrow = row.row()
|
||||
subrow.prop(kmi, "type", text="")
|
||||
subrow.prop(kmi, "value", text="")
|
||||
elif map_type == 'TIMER':
|
||||
row.prop(kmi, "type", text="")
|
||||
else:
|
||||
row.label()
|
||||
|
||||
if um.expanded:
|
||||
draw_user_menu_preference_expanded(context=context, layout=box, kmi=kmi, map_type=map_type)
|
||||
|
||||
|
||||
def menu_id(context, umg):
|
||||
prefs = context.preferences
|
||||
um = prefs.user_menus
|
||||
|
||||
i = 0
|
||||
for item in um.menus:
|
||||
if item == umg:
|
||||
return i
|
||||
i = i + 1
|
||||
return -1
|
||||
|
||||
|
||||
def draw_user_menus(context, layout):
|
||||
prefs = context.preferences
|
||||
um = prefs.user_menus
|
||||
|
||||
if not um.active_group:
|
||||
um.active_group = um.menus[0]
|
||||
|
||||
split = layout.split(factor=0.4)
|
||||
|
||||
row = split.row()
|
||||
|
||||
rowsub = row.row(align=True)
|
||||
rowsub.menu("USERPREF_MT_menu_select", text=um.active_group.name)
|
||||
rowsub.operator("preferences.usermenus_add", text="", icon='ADD')
|
||||
rowsub.operator("preferences.usermenus_remove", text="", icon='REMOVE')
|
||||
|
||||
rowsub = split.row(align=True)
|
||||
rowsub.prop(um, "space_selected", text="")
|
||||
|
||||
rowsub = split.row(align=True)
|
||||
rowsub.prop(um, "context_selected", text="")
|
||||
|
||||
#rowsub = split.row(align=True)
|
||||
#rowsub.operator("preferences.keyconfig_import", text="", icon='IMPORT')
|
||||
#rowsub.operator("preferences.keyconfig_export", text="", icon='EXPORT')
|
||||
|
||||
row = layout.row()
|
||||
row.separator()
|
||||
|
||||
draw_user_menu_preference(context=context, layout=row)
|
||||
|
||||
row = layout.row()
|
||||
row.separator()
|
||||
|
||||
if um.active_group.type == "PIE":
|
||||
draw_pie(context=context, row=row)
|
||||
else:
|
||||
draw_item_box(context=context, row=row)
|
||||
draw_item_editor(context=context, row=row)
|
||||
|
||||
row.separator()
|
||||
|
@@ -157,6 +157,8 @@ def op_menu(menu, kmi_args):
|
||||
def op_menu_pie(menu, kmi_args):
|
||||
return ("wm.call_menu_pie", kmi_args, {"properties": [("name", menu)]})
|
||||
|
||||
def op_user_menu(menu, kmi_args):
|
||||
return ("wm.call_user_menu", kmi_args, {"properties": [("name", menu)]})
|
||||
|
||||
def op_panel(menu, kmi_args, kmi_data=()):
|
||||
return ("wm.call_panel", kmi_args, {"properties": [("name", menu), *kmi_data]})
|
||||
@@ -407,7 +409,7 @@ def km_window(params):
|
||||
("wm.quit_blender", {"type": 'Q', "value": 'PRESS', "ctrl": True}, None),
|
||||
|
||||
# Quick menu and toolbar
|
||||
op_menu("SCREEN_MT_user_menu", {"type": 'Q', "value": 'PRESS'}),
|
||||
op_user_menu("QUICK_FAVORITES", {"type": 'Q', "value": 'PRESS'}),
|
||||
|
||||
# Fast editor switching
|
||||
*(
|
||||
|
@@ -67,6 +67,8 @@ def op_menu(menu, kmi_args):
|
||||
def op_menu_pie(menu, kmi_args):
|
||||
return ("wm.call_menu_pie", kmi_args, {"properties": [("name", menu)]})
|
||||
|
||||
def op_user_menu(menu, kmi_args):
|
||||
return ("wm.call_user_menu", kmi_args, {"properties": [("name", menu)]})
|
||||
|
||||
def op_panel(menu, kmi_args, kmi_data=()):
|
||||
return ("wm.call_panel", kmi_args, {"properties": [("name", menu), *kmi_data]})
|
||||
@@ -210,7 +212,7 @@ def km_window(params):
|
||||
("wm.quit_blender", {"type": 'Q', "value": 'PRESS', "ctrl": True}, None),
|
||||
|
||||
# Quick menu and toolbar
|
||||
op_menu("SCREEN_MT_user_menu", {"type": 'TAB', "value": 'PRESS', "shift": True}),
|
||||
op_user_menu("QUICK_FAVORITES", {"type": 'Q', "value": 'PRESS'}),
|
||||
|
||||
# NDOF settings
|
||||
op_panel("USERPREF_PT_ndof_settings", {"type": 'NDOF_BUTTON_MENU', "value": 'PRESS'}),
|
||||
|
@@ -602,7 +602,6 @@ class AddPresetOperator(AddPresetBase, Operator):
|
||||
prefix, suffix = operator.split("_OT_", 1)
|
||||
return os.path.join("operator", "%s.%s" % (prefix.lower(), suffix))
|
||||
|
||||
|
||||
class WM_MT_operator_presets(Menu):
|
||||
bl_label = "Operator Presets"
|
||||
|
||||
|
@@ -51,6 +51,8 @@ def module_filesystem_remove(path_base, module_name):
|
||||
|
||||
# This duplicates shutil.copytree from Python 3.8, with the new dirs_exist_ok
|
||||
# argument that we need. Once we upgrade to 3.8 we can remove this.
|
||||
|
||||
|
||||
def _preferences_copytree(entries, src, dst):
|
||||
import os
|
||||
import shutil
|
||||
@@ -85,11 +87,13 @@ def _preferences_copytree(entries, src, dst):
|
||||
raise Error(errors)
|
||||
return dst
|
||||
|
||||
|
||||
def preferences_copytree(src, dst):
|
||||
import os
|
||||
with os.scandir(src) as entries:
|
||||
return _preferences_copytree(entries=entries, src=src, dst=dst)
|
||||
|
||||
|
||||
class PREFERENCES_OT_keyconfig_activate(Operator):
|
||||
bl_idname = "preferences.keyconfig_activate"
|
||||
bl_label = "Activate Keyconfig"
|
||||
@@ -241,7 +245,8 @@ class PREFERENCES_OT_keyconfig_import(Operator):
|
||||
|
||||
config_name = basename(self.filepath)
|
||||
|
||||
path = bpy.utils.user_resource('SCRIPTS', os.path.join("presets", "keyconfig"), create=True)
|
||||
path = bpy.utils.user_resource(
|
||||
'SCRIPTS', os.path.join("presets", "keyconfig"), create=True)
|
||||
path = os.path.join(path, config_name)
|
||||
|
||||
try:
|
||||
@@ -460,7 +465,8 @@ class PREFERENCES_OT_addon_enable(Operator):
|
||||
err_str = traceback.format_exc()
|
||||
print(err_str)
|
||||
|
||||
mod = addon_utils.enable(self.module, default_set=True, handle_error=err_cb)
|
||||
mod = addon_utils.enable(
|
||||
self.module, default_set=True, handle_error=err_cb)
|
||||
|
||||
if mod:
|
||||
info = addon_utils.module_bl_info(mod)
|
||||
@@ -544,7 +550,8 @@ class PREFERENCES_OT_theme_install(Operator):
|
||||
|
||||
xmlfile = self.filepath
|
||||
|
||||
path_themes = bpy.utils.user_resource('SCRIPTS', "presets/interface_theme", create=True)
|
||||
path_themes = bpy.utils.user_resource(
|
||||
'SCRIPTS', "presets/interface_theme", create=True)
|
||||
|
||||
if not path_themes:
|
||||
self.report({'ERROR'}, "Failed to get themes path")
|
||||
@@ -554,7 +561,8 @@ class PREFERENCES_OT_theme_install(Operator):
|
||||
|
||||
if not self.overwrite:
|
||||
if os.path.exists(path_dest):
|
||||
self.report({'WARNING'}, "File already installed to %r\n" % path_dest)
|
||||
self.report(
|
||||
{'WARNING'}, "File already installed to %r\n" % path_dest)
|
||||
return {'CANCELLED'}
|
||||
|
||||
try:
|
||||
@@ -638,7 +646,8 @@ class PREFERENCES_OT_addon_install(Operator):
|
||||
|
||||
if self.target == 'DEFAULT':
|
||||
# don't use bpy.utils.script_paths("addons") because we may not be able to write to it.
|
||||
path_addons = bpy.utils.user_resource('SCRIPTS', "addons", create=True)
|
||||
path_addons = bpy.utils.user_resource(
|
||||
'SCRIPTS', "addons", create=True)
|
||||
else:
|
||||
path_addons = context.preferences.filepaths.script_directory
|
||||
if path_addons:
|
||||
@@ -661,7 +670,8 @@ class PREFERENCES_OT_addon_install(Operator):
|
||||
pyfile_dir = os.path.dirname(pyfile)
|
||||
for addon_path in addon_utils.paths():
|
||||
if os.path.samefile(pyfile_dir, addon_path):
|
||||
self.report({'ERROR'}, "Source file is in the add-on search path: %r" % addon_path)
|
||||
self.report(
|
||||
{'ERROR'}, "Source file is in the add-on search path: %r" % addon_path)
|
||||
return {'CANCELLED'}
|
||||
del addon_path
|
||||
del pyfile_dir
|
||||
@@ -684,7 +694,8 @@ class PREFERENCES_OT_addon_install(Operator):
|
||||
for f in file_to_extract.namelist():
|
||||
path_dest = os.path.join(path_addons, os.path.basename(f))
|
||||
if os.path.exists(path_dest):
|
||||
self.report({'WARNING'}, "File already installed to %r\n" % path_dest)
|
||||
self.report(
|
||||
{'WARNING'}, "File already installed to %r\n" % path_dest)
|
||||
return {'CANCELLED'}
|
||||
|
||||
try: # extract the file to "addons"
|
||||
@@ -699,7 +710,8 @@ class PREFERENCES_OT_addon_install(Operator):
|
||||
if self.overwrite:
|
||||
module_filesystem_remove(path_addons, os.path.basename(pyfile))
|
||||
elif os.path.exists(path_dest):
|
||||
self.report({'WARNING'}, "File already installed to %r\n" % path_dest)
|
||||
self.report(
|
||||
{'WARNING'}, "File already installed to %r\n" % path_dest)
|
||||
return {'CANCELLED'}
|
||||
|
||||
# if not compressed file just copy into the addon path
|
||||
@@ -709,7 +721,8 @@ class PREFERENCES_OT_addon_install(Operator):
|
||||
traceback.print_exc()
|
||||
return {'CANCELLED'}
|
||||
|
||||
addons_new = {mod.__name__ for mod in addon_utils.modules()} - addons_old
|
||||
addons_new = {mod.__name__ for mod in addon_utils.modules()} - \
|
||||
addons_old
|
||||
addons_new.discard("modules")
|
||||
|
||||
# disable any addons we may have enabled previously and removed.
|
||||
@@ -779,7 +792,8 @@ class PREFERENCES_OT_addon_remove(Operator):
|
||||
|
||||
path, isdir = PREFERENCES_OT_addon_remove.path_from_addon(self.module)
|
||||
if path is None:
|
||||
self.report({'WARNING'}, "Add-on path %r could not be found" % path)
|
||||
self.report(
|
||||
{'WARNING'}, "Add-on path %r could not be found" % path)
|
||||
return {'CANCELLED'}
|
||||
|
||||
# in case its enabled
|
||||
@@ -925,9 +939,11 @@ class PREFERENCES_OT_app_template_install(Operator):
|
||||
module_filesystem_remove(path_app_templates, f)
|
||||
else:
|
||||
for f in file_to_extract.namelist():
|
||||
path_dest = os.path.join(path_app_templates, os.path.basename(f))
|
||||
path_dest = os.path.join(
|
||||
path_app_templates, os.path.basename(f))
|
||||
if os.path.exists(path_dest):
|
||||
self.report({'WARNING'}, "File already installed to %r\n" % path_dest)
|
||||
self.report(
|
||||
{'WARNING'}, "File already installed to %r\n" % path_dest)
|
||||
return {'CANCELLED'}
|
||||
|
||||
try: # extract the file to "bl_app_templates_user"
|
||||
@@ -941,7 +957,8 @@ class PREFERENCES_OT_app_template_install(Operator):
|
||||
self.report({'WARNING'}, "Expected a zip-file %r\n" % filepath)
|
||||
return {'CANCELLED'}
|
||||
|
||||
app_templates_new = set(os.listdir(path_app_templates)) - app_templates_old
|
||||
app_templates_new = set(os.listdir(
|
||||
path_app_templates)) - app_templates_old
|
||||
|
||||
# in case a new module path was created to install this addon.
|
||||
bpy.utils.refresh_script_paths()
|
||||
@@ -1001,14 +1018,17 @@ class PREFERENCES_OT_studiolight_install(Operator):
|
||||
prefs = context.preferences
|
||||
|
||||
path_studiolights = os.path.join("studiolights", self.type.lower())
|
||||
path_studiolights = bpy.utils.user_resource('DATAFILES', path_studiolights, create=True)
|
||||
path_studiolights = bpy.utils.user_resource(
|
||||
'DATAFILES', path_studiolights, create=True)
|
||||
if not path_studiolights:
|
||||
self.report({'ERROR'}, "Failed to create Studio Light path")
|
||||
return {'CANCELLED'}
|
||||
|
||||
for e in self.files:
|
||||
shutil.copy(os.path.join(self.directory, e.name), path_studiolights)
|
||||
prefs.studio_lights.load(os.path.join(path_studiolights, e.name), self.type)
|
||||
shutil.copy(os.path.join(self.directory, e.name),
|
||||
path_studiolights)
|
||||
prefs.studio_lights.load(os.path.join(
|
||||
path_studiolights, e.name), self.type)
|
||||
|
||||
# print message
|
||||
msg = (
|
||||
@@ -1047,7 +1067,8 @@ class PREFERENCES_OT_studiolight_new(Operator):
|
||||
wm = context.window_manager
|
||||
filename = bpy.path.ensure_ext(self.filename, ".sl")
|
||||
|
||||
path_studiolights = bpy.utils.user_resource('DATAFILES', os.path.join("studiolights", "studio"), create=True)
|
||||
path_studiolights = bpy.utils.user_resource(
|
||||
'DATAFILES', os.path.join("studiolights", "studio"), create=True)
|
||||
if not path_studiolights:
|
||||
self.report({'ERROR'}, "Failed to get Studio Light path")
|
||||
return {'CANCELLED'}
|
||||
@@ -1060,7 +1081,8 @@ class PREFERENCES_OT_studiolight_new(Operator):
|
||||
else:
|
||||
for studio_light in prefs.studio_lights:
|
||||
if studio_light.name == filename:
|
||||
bpy.ops.preferences.studiolight_uninstall(index=studio_light.index)
|
||||
bpy.ops.preferences.studiolight_uninstall(
|
||||
index=studio_light.index)
|
||||
|
||||
prefs.studio_lights.new(path=filepath_final)
|
||||
|
||||
@@ -1076,7 +1098,8 @@ class PREFERENCES_OT_studiolight_new(Operator):
|
||||
def draw(self, _context):
|
||||
layout = self.layout
|
||||
if self.ask_overide:
|
||||
layout.label(text="Warning, file already exists. Overwrite existing file?")
|
||||
layout.label(
|
||||
text="Warning, file already exists. Overwrite existing file?")
|
||||
else:
|
||||
layout.prop(self, "filename")
|
||||
|
||||
@@ -1141,6 +1164,184 @@ class PREFERENCES_OT_studiolight_show(Operator):
|
||||
bpy.ops.screen.userpref_show('INVOKE_DEFAULT')
|
||||
return {'FINISHED'}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# User Menus Operators
|
||||
|
||||
class PREFERENCES_OT_usermenus_select(Operator):
|
||||
bl_idname = "preferences.usermenus_select"
|
||||
bl_label = "select user menu to edit"
|
||||
|
||||
index: IntProperty()
|
||||
|
||||
def execute(self, _context):
|
||||
prefs = _context.preferences
|
||||
um = prefs.user_menus
|
||||
|
||||
i = 0
|
||||
for umg in um.menus:
|
||||
if i == self.index:
|
||||
um.set_group(new_group=umg)
|
||||
return {'FINISHED'}
|
||||
i = i + 1
|
||||
return {'CANCELLED'}
|
||||
|
||||
class PREFERENCES_OT_usermenus_add(Operator):
|
||||
bl_idname = "preferences.usermenus_add"
|
||||
bl_label = "add an user menus group"
|
||||
|
||||
def execute(self, _context):
|
||||
prefs = _context.preferences
|
||||
um = prefs.user_menus
|
||||
um.add_group()
|
||||
return {'FINISHED'}
|
||||
|
||||
class PREFERENCES_OT_usermenus_remove(Operator):
|
||||
bl_idname = "preferences.usermenus_remove"
|
||||
bl_label = "remove an user menus group"
|
||||
|
||||
@classmethod
|
||||
def poll(self, context):
|
||||
prefs = context.preferences
|
||||
um = prefs.user_menus
|
||||
|
||||
if um.active_group.idname == "QUICK_FAVORITES":
|
||||
return False
|
||||
return True
|
||||
|
||||
def execute(self, _context):
|
||||
prefs = _context.preferences
|
||||
um = prefs.user_menus
|
||||
um.remove_group()
|
||||
return {'FINISHED'}
|
||||
|
||||
class PREFERENCES_OT_menuitem_add(Operator):
|
||||
"""Add user menu item"""
|
||||
bl_idname = "preferences.menuitem_add"
|
||||
bl_label = "Add User Menu Item"
|
||||
|
||||
index: IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
prefs = context.preferences
|
||||
um = prefs.user_menus
|
||||
|
||||
um.item_add(index=self.index)
|
||||
context.preferences.is_dirty = True
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class PREFERENCES_OT_menuitem_remove(Operator):
|
||||
"""Remove user menu item"""
|
||||
bl_idname = "preferences.menuitem_remove"
|
||||
bl_label = "Remove User Menu Item"
|
||||
|
||||
@classmethod
|
||||
def poll(self, context):
|
||||
prefs = context.preferences
|
||||
um = prefs.user_menus
|
||||
can_remove = um.active_item
|
||||
um_type = um.active_group.type
|
||||
|
||||
if um_type == "PIE" and can_remove:
|
||||
if not can_remove.parent:
|
||||
return False
|
||||
return can_remove
|
||||
|
||||
def execute(self, context):
|
||||
prefs = context.preferences
|
||||
um = prefs.user_menus
|
||||
um.item_remove()
|
||||
context.preferences.is_dirty = True
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class PREFERENCES_OT_menuitem_up(Operator):
|
||||
"""move up an user menu item"""
|
||||
bl_idname = "preferences.menuitem_up"
|
||||
bl_label = "Move Up An User Menu Item"
|
||||
|
||||
@classmethod
|
||||
def poll(self, context):
|
||||
prefs = context.preferences
|
||||
um = prefs.user_menus
|
||||
current = um.active_item
|
||||
um_type = um.active_group.type
|
||||
if not current:
|
||||
return False
|
||||
|
||||
if um_type == "PIE":
|
||||
if not current.parent:
|
||||
return False
|
||||
prev_item = current.prev
|
||||
if prev_item:
|
||||
return True
|
||||
if current.parent.item.parent:
|
||||
return True
|
||||
return False
|
||||
else:
|
||||
prev_item = current.prev
|
||||
if prev_item or current.parent:
|
||||
return True
|
||||
return False
|
||||
|
||||
def execute(self, context):
|
||||
prefs = context.preferences
|
||||
um = prefs.user_menus
|
||||
um.item_move(up=True)
|
||||
context.preferences.is_dirty = True
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class PREFERENCES_OT_menuitem_down(Operator):
|
||||
"""move up an user menu item"""
|
||||
bl_idname = "preferences.menuitem_down"
|
||||
bl_label = "Move Up An User Menu Item"
|
||||
|
||||
@classmethod
|
||||
def poll(self, context):
|
||||
prefs = context.preferences
|
||||
um = prefs.user_menus
|
||||
um_type = um.active_group.type
|
||||
current = um.active_item
|
||||
if not current:
|
||||
return False
|
||||
|
||||
if um_type == "PIE":
|
||||
if not current.parent:
|
||||
return False
|
||||
next_item = current.next
|
||||
if next_item:
|
||||
return True
|
||||
if current.parent.item.parent:
|
||||
return True
|
||||
return False
|
||||
else:
|
||||
next_item = current.next
|
||||
if next_item or current.parent:
|
||||
return True
|
||||
return False
|
||||
|
||||
def execute(self, context):
|
||||
prefs = context.preferences
|
||||
um = prefs.user_menus
|
||||
um.item_move(up=False)
|
||||
context.preferences.is_dirty = True
|
||||
return {'FINISHED'}
|
||||
|
||||
class PREFERENCES_OT_pie_menuitem_add(Operator):
|
||||
"""Add user menu item"""
|
||||
bl_idname = "preferences.pie_menuitem_add"
|
||||
bl_label = "Add User Menu Item"
|
||||
|
||||
index: IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
prefs = context.preferences
|
||||
um = prefs.user_menus
|
||||
|
||||
um.pie_item_add(index=self.index)
|
||||
context.preferences.is_dirty = True
|
||||
return {'FINISHED'}
|
||||
|
||||
classes = (
|
||||
PREFERENCES_OT_addon_disable,
|
||||
@@ -1167,4 +1368,12 @@ classes = (
|
||||
PREFERENCES_OT_studiolight_uninstall,
|
||||
PREFERENCES_OT_studiolight_copy_settings,
|
||||
PREFERENCES_OT_studiolight_show,
|
||||
PREFERENCES_OT_usermenus_select,
|
||||
PREFERENCES_OT_usermenus_add,
|
||||
PREFERENCES_OT_usermenus_remove,
|
||||
PREFERENCES_OT_menuitem_add,
|
||||
PREFERENCES_OT_menuitem_remove,
|
||||
PREFERENCES_OT_menuitem_up,
|
||||
PREFERENCES_OT_menuitem_down,
|
||||
PREFERENCES_OT_pie_menuitem_add,
|
||||
)
|
||||
|
@@ -2633,6 +2633,36 @@ class WM_OT_drop_blend_file(Operator):
|
||||
col.operator("wm.link", text="Link...", icon='LINK_BLEND').filepath = self.filepath
|
||||
col.operator("wm.append", text="Append...", icon='APPEND_BLEND').filepath = self.filepath
|
||||
|
||||
class WM_OT_call_user_menu(Operator):
|
||||
bl_idname = "wm.call_user_menu"
|
||||
bl_label = "display user menu"
|
||||
|
||||
name: StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
return {'FINISHED'}
|
||||
|
||||
def draw_menu(self, menu, context):
|
||||
prefs = context.preferences
|
||||
um = prefs.user_menus
|
||||
umg = um.get_group(idname=self.name)
|
||||
|
||||
layout = menu.layout
|
||||
if umg.type == "PIE":
|
||||
layout = layout.menu_pie()
|
||||
um.draw_menu(context=context, layout=layout, menu=umg)
|
||||
|
||||
def invoke(self, context, event):
|
||||
prefs = context.preferences
|
||||
um = prefs.user_menus
|
||||
wm = context.window_manager
|
||||
umg = um.get_group(idname=self.name)
|
||||
|
||||
if umg.type == "PIE":
|
||||
wm.popup_menu_pie(draw_func=self.draw_menu, title=umg.name, event=event)
|
||||
else:
|
||||
wm.popup_menu(self.draw_menu, title=umg.name)
|
||||
return {'FINISHED'}
|
||||
|
||||
classes = (
|
||||
WM_OT_context_collection_boolean_set,
|
||||
@@ -2677,4 +2707,5 @@ classes = (
|
||||
WM_OT_batch_rename,
|
||||
WM_MT_splash,
|
||||
WM_MT_splash_about,
|
||||
WM_OT_call_user_menu,
|
||||
)
|
||||
|
@@ -1684,8 +1684,37 @@ class USERPREF_PT_keymap(KeymapPanel, Panel):
|
||||
# Keymap Settings
|
||||
draw_keymaps(context, layout)
|
||||
|
||||
# print("runtime", time.time() - start)
|
||||
# -----------------------------------------------------------------------------
|
||||
# Custom Menu Editor Panels
|
||||
|
||||
class UserMenusPanel:
|
||||
bl_space_type = 'PREFERENCES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "user_menus"
|
||||
|
||||
class USERPREF_MT_menu_select(Menu):
|
||||
bl_label = "Menus group select"
|
||||
|
||||
def draw(self, context):
|
||||
prefs = context.preferences
|
||||
um = prefs.user_menus
|
||||
|
||||
layout = self.layout
|
||||
index = 0
|
||||
for umg in um.menus:
|
||||
layout.operator("preferences.usermenus_select", text=umg.name).index = index
|
||||
index = index + 1
|
||||
|
||||
|
||||
class USERPREF_PT_user_menus(UserMenusPanel, Panel):
|
||||
bl_label = "user_menus"
|
||||
bl_options = {'HIDE_HEADER'}
|
||||
|
||||
def draw(self, context):
|
||||
from rna_user_menus_ui import draw_user_menus
|
||||
|
||||
layout = self.layout
|
||||
draw_user_menus(context, layout)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Add-On Panels
|
||||
@@ -2278,6 +2307,8 @@ classes = (
|
||||
USERPREF_PT_navigation_fly_walk_gravity,
|
||||
|
||||
USERPREF_PT_keymap,
|
||||
USERPREF_MT_menu_select,
|
||||
USERPREF_PT_user_menus,
|
||||
USERPREF_PT_addons,
|
||||
|
||||
USERPREF_PT_studiolight_lights,
|
||||
|
@@ -27,7 +27,16 @@ extern "C" {
|
||||
struct ListBase;
|
||||
struct bUserMenu;
|
||||
struct bUserMenuItem;
|
||||
struct wmWindowManager;
|
||||
struct bUserMenusGroup;
|
||||
|
||||
void BKE_blender_user_menu_free_list(struct ListBase *lb);
|
||||
void BKE_blender_user_menus_group_idname_update(struct bUserMenusGroup *umg);
|
||||
void BKE_blender_user_menus_group_idname_update_keymap(struct wmWindowManager *wm,
|
||||
const char *old,
|
||||
const char *new);
|
||||
struct bUserMenusGroup *BKE_blender_user_menus_group_new(const char *name);
|
||||
struct bUserMenusGroup *BKE_blender_user_menus_group_find(struct ListBase *lb, const char *idname);
|
||||
struct bUserMenu *BKE_blender_user_menu_find(struct ListBase *lb,
|
||||
char space_type,
|
||||
const char *context);
|
||||
@@ -38,6 +47,7 @@ struct bUserMenu *BKE_blender_user_menu_ensure(struct ListBase *lb,
|
||||
struct bUserMenuItem *BKE_blender_user_menu_item_add(struct ListBase *lb, int type);
|
||||
void BKE_blender_user_menu_item_free(struct bUserMenuItem *umi);
|
||||
void BKE_blender_user_menu_item_free_list(struct ListBase *lb);
|
||||
struct bUserMenusGroup *BKU_blender_user_menu_default(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@@ -255,6 +255,7 @@ struct ViewLayer *CTX_data_view_layer(const bContext *C);
|
||||
struct RenderEngineType *CTX_data_engine_type(const bContext *C);
|
||||
struct ToolSettings *CTX_data_tool_settings(const bContext *C);
|
||||
|
||||
const char **CTX_data_list_mode_string(void);
|
||||
const char *CTX_data_mode_string(const bContext *C);
|
||||
enum eContextObjectMode CTX_data_mode_enum_ex(const struct Object *obedit,
|
||||
const struct Object *ob,
|
||||
|
@@ -238,10 +238,14 @@ static void userdef_free_keyconfig_prefs(UserDef *userdef)
|
||||
|
||||
static void userdef_free_user_menus(UserDef *userdef)
|
||||
{
|
||||
for (bUserMenu *um = userdef->user_menus.first, *um_next; um; um = um_next) {
|
||||
um_next = um->next;
|
||||
BKE_blender_user_menu_item_free_list(&um->items);
|
||||
MEM_freeN(um);
|
||||
for (bUserMenusGroup *umg = userdef->user_menus_group.first, *umg_next; umg; umg = umg_next) {
|
||||
umg_next = umg->next;
|
||||
for (bUserMenu *um = umg->menus.first, *um_next; um; um = um_next) {
|
||||
um_next = um->next;
|
||||
BKE_blender_user_menu_item_free_list(&um->items);
|
||||
MEM_freeN(um);
|
||||
}
|
||||
MEM_freeN(umg);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -26,12 +26,97 @@
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_utils.h"
|
||||
|
||||
#include "DNA_userdef_types.h"
|
||||
#include "DNA_windowmanager_types.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
|
||||
#include "BKE_blender_user_menu.h"
|
||||
#include "BKE_idprop.h"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Menu group
|
||||
* \{ */
|
||||
|
||||
void BKE_blender_user_menu_free_list(ListBase *lb)
|
||||
{
|
||||
for (bUserMenu *um = lb->first, *um_next; um; um = um_next) {
|
||||
um_next = um->next;
|
||||
BKE_blender_user_menu_item_free_list(&um->items);
|
||||
MEM_freeN(um);
|
||||
}
|
||||
BLI_listbase_clear(lb);
|
||||
}
|
||||
|
||||
bUserMenusGroup *BKE_blender_user_menus_group_find(ListBase *lb, const char *idname)
|
||||
{
|
||||
LISTBASE_FOREACH (bUserMenusGroup *, umg, lb) {
|
||||
if ((STREQ(idname, umg->idname))) {
|
||||
return umg;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void BKE_blender_user_menus_group_idname_update(bUserMenusGroup *umg)
|
||||
{
|
||||
char name[64];
|
||||
|
||||
STRNCPY(name, umg->name);
|
||||
for (int i = 0; name[i]; i++) {
|
||||
if (name[i] == ' ')
|
||||
name[i] = '_';
|
||||
if (name[i] >= 'a' && name[i] <= 'z')
|
||||
name[i] += 'A' - 'a';
|
||||
}
|
||||
STRNCPY(umg->idname, name);
|
||||
BLI_uniquename(&U.user_menus_group,
|
||||
umg,
|
||||
umg->idname,
|
||||
'_',
|
||||
offsetof(bUserMenusGroup, idname),
|
||||
sizeof(umg->idname));
|
||||
}
|
||||
|
||||
void BKE_blender_user_menus_group_idname_update_keymap(wmWindowManager *wm,
|
||||
const char *old,
|
||||
const char *new)
|
||||
{
|
||||
wmKeyConfig *kc;
|
||||
wmKeyMap *km;
|
||||
|
||||
for (kc = wm->keyconfigs.first; kc; kc = kc->next) {
|
||||
for (km = kc->keymaps.first; km; km = km->next) {
|
||||
wmKeyMapItem *kmi;
|
||||
for (kmi = km->items.first; kmi; kmi = kmi->next) {
|
||||
if (STREQ(kmi->idname, "WM_OT_call_user_menu")) {
|
||||
IDProperty *idp = IDP_GetPropertyFromGroup(kmi->properties, "name");
|
||||
char *index = IDP_String(idp);
|
||||
if (STREQ(index, old)) {
|
||||
IDP_AssignString(idp, new, 64);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bUserMenusGroup *BKE_blender_user_menus_group_new(const char *name)
|
||||
{
|
||||
bUserMenusGroup *umg = MEM_mallocN(sizeof(*umg), __func__);
|
||||
STRNCPY(umg->name, name);
|
||||
umg->type = 0;
|
||||
umg->prev = NULL;
|
||||
umg->next = NULL;
|
||||
BLI_listbase_clear(&umg->menus);
|
||||
BKE_blender_user_menus_group_idname_update(umg);
|
||||
return umg;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Menu Type
|
||||
* \{ */
|
||||
@@ -50,8 +135,10 @@ bUserMenu *BKE_blender_user_menu_ensure(ListBase *lb, char space_type, const cha
|
||||
{
|
||||
bUserMenu *um = BKE_blender_user_menu_find(lb, space_type, context);
|
||||
if (um == NULL) {
|
||||
|
||||
um = MEM_callocN(sizeof(bUserMenu), __func__);
|
||||
um->space_type = space_type;
|
||||
BLI_listbase_clear(&um->items);
|
||||
STRNCPY(um->context, context);
|
||||
BLI_addhead(lb, um);
|
||||
}
|
||||
@@ -80,6 +167,9 @@ bUserMenuItem *BKE_blender_user_menu_item_add(ListBase *lb, int type)
|
||||
else if (type == USER_MENU_TYPE_PROP) {
|
||||
size = sizeof(bUserMenuItem_Prop);
|
||||
}
|
||||
else if (type == USER_MENU_TYPE_SUBMENU) {
|
||||
size = sizeof(bUserMenuItem_SubMenu);
|
||||
}
|
||||
else {
|
||||
size = sizeof(bUserMenuItem);
|
||||
BLI_assert(0);
|
||||
@@ -87,7 +177,9 @@ bUserMenuItem *BKE_blender_user_menu_item_add(ListBase *lb, int type)
|
||||
|
||||
bUserMenuItem *umi = MEM_callocN(size, __func__);
|
||||
umi->type = type;
|
||||
BLI_addtail(lb, umi);
|
||||
umi->icon = 0;
|
||||
if (lb)
|
||||
BLI_addtail(lb, umi);
|
||||
return umi;
|
||||
}
|
||||
|
||||
@@ -99,6 +191,10 @@ void BKE_blender_user_menu_item_free(bUserMenuItem *umi)
|
||||
IDP_FreeProperty(umi_op->prop);
|
||||
}
|
||||
}
|
||||
if (umi->type == USER_MENU_TYPE_SUBMENU) {
|
||||
bUserMenuItem_SubMenu *umi_sm = (bUserMenuItem_SubMenu *)umi;
|
||||
BKE_blender_user_menu_item_free_list(&umi_sm->items);
|
||||
}
|
||||
MEM_freeN(umi);
|
||||
}
|
||||
|
||||
@@ -110,3 +206,19 @@ void BKE_blender_user_menu_item_free_list(ListBase *lb)
|
||||
}
|
||||
BLI_listbase_clear(lb);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Default Menu
|
||||
* \{ */
|
||||
|
||||
bUserMenusGroup *BKU_blender_user_menu_default(void)
|
||||
{
|
||||
bUserMenusGroup *umg = MEM_mallocN(sizeof(*umg), __func__);
|
||||
STRNCPY(umg->name, "Quick Favorites");
|
||||
STRNCPY(umg->idname, "QUICK_FAVORITES");
|
||||
umg->type = 0;
|
||||
umg->prev = NULL;
|
||||
umg->next = NULL;
|
||||
BLI_listbase_clear(&umg->menus);
|
||||
return umg;
|
||||
}
|
@@ -57,6 +57,7 @@
|
||||
#include "BKE_screen.h"
|
||||
#include "BKE_studiolight.h"
|
||||
#include "BKE_workspace.h"
|
||||
#include "BKE_blender_user_menu.h"
|
||||
|
||||
#include "BLO_readfile.h"
|
||||
#include "BLO_writefile.h"
|
||||
@@ -614,6 +615,13 @@ UserDef *BKE_blendfile_userdef_from_defaults(void)
|
||||
BLI_addtail(&userdef->themes, btheme);
|
||||
}
|
||||
|
||||
/* default user menus. */
|
||||
{
|
||||
bUserMenusGroup *umg = BKU_blender_user_menu_default();
|
||||
BLI_addtail(&userdef->user_menus_group, umg);
|
||||
userdef->runtime.umg_select = umg;
|
||||
}
|
||||
|
||||
#ifdef WITH_PYTHON_SECURITY
|
||||
/* use alternative setting for security nuts
|
||||
* otherwise we'd need to patch the binary blob - startup.blend.c */
|
||||
|
@@ -1151,6 +1151,11 @@ const char *CTX_data_mode_string(const bContext *C)
|
||||
return data_mode_strings[CTX_data_mode_enum(C)];
|
||||
}
|
||||
|
||||
const char **CTX_data_list_mode_string(void)
|
||||
{
|
||||
return data_mode_strings;
|
||||
}
|
||||
|
||||
void CTX_data_scene_set(bContext *C, Scene *scene)
|
||||
{
|
||||
C->data.scene = scene;
|
||||
|
@@ -114,6 +114,7 @@
|
||||
#include "BKE_action.h"
|
||||
#include "BKE_anim_data.h"
|
||||
#include "BKE_armature.h"
|
||||
#include "BKE_blender_user_menu.h"
|
||||
#include "BKE_brush.h"
|
||||
#include "BKE_collection.h"
|
||||
#include "BKE_colortools.h"
|
||||
@@ -159,6 +160,8 @@
|
||||
#include "BKE_volume.h"
|
||||
#include "BKE_workspace.h"
|
||||
|
||||
#include "BKE_blender_user_menu.h"
|
||||
|
||||
#include "DRW_engine.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
@@ -8897,6 +8900,26 @@ static void direct_link_keymapitem(BlendDataReader *reader, wmKeyMapItem *kmi)
|
||||
kmi->flag &= ~KMI_UPDATE;
|
||||
}
|
||||
|
||||
static void read_usermenuitems(BlendDataReader *reader,
|
||||
ListBase *lb,
|
||||
bUserMenuItem_SubMenu *parent)
|
||||
{
|
||||
LISTBASE_FOREACH (bUserMenuItem *, umi, lb) {
|
||||
umi->parent = parent;
|
||||
if (umi->type == USER_MENU_TYPE_OPERATOR) {
|
||||
bUserMenuItem_Op *umi_op = (bUserMenuItem_Op *)umi;
|
||||
BLO_read_data_address(reader, &umi_op->prop);
|
||||
IDP_BlendDataRead(reader, &umi_op->prop);
|
||||
}
|
||||
if (umi->type == USER_MENU_TYPE_SUBMENU) {
|
||||
bUserMenuItem_SubMenu *umi_sm = (bUserMenuItem_SubMenu *)umi;
|
||||
BLI_listbase_clear(&umi_sm->items);
|
||||
BLO_read_list(reader, &umi_sm->items);
|
||||
read_usermenuitems(reader, &umi_sm->items, umi_sm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead)
|
||||
{
|
||||
UserDef *user;
|
||||
@@ -8918,6 +8941,7 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead)
|
||||
BLO_read_list(reader, &user->user_menus);
|
||||
BLO_read_list(reader, &user->addons);
|
||||
BLO_read_list(reader, &user->autoexec_paths);
|
||||
BLO_read_list(reader, &user->user_menus_group);
|
||||
|
||||
LISTBASE_FOREACH (wmKeyMap *, keymap, &user->user_keymaps) {
|
||||
keymap->modal_items = NULL;
|
||||
@@ -8951,13 +8975,7 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead)
|
||||
|
||||
LISTBASE_FOREACH (bUserMenu *, um, &user->user_menus) {
|
||||
BLO_read_list(reader, &um->items);
|
||||
LISTBASE_FOREACH (bUserMenuItem *, umi, &um->items) {
|
||||
if (umi->type == USER_MENU_TYPE_OPERATOR) {
|
||||
bUserMenuItem_Op *umi_op = (bUserMenuItem_Op *)umi;
|
||||
BLO_read_data_address(reader, &umi_op->prop);
|
||||
IDP_BlendDataRead(reader, &umi_op->prop);
|
||||
}
|
||||
}
|
||||
read_usermenuitems(reader, &um->items, NULL);
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (bAddon *, addon, &user->addons) {
|
||||
@@ -8965,6 +8983,23 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead)
|
||||
IDP_BlendDataRead(reader, &addon->prop);
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (bUserMenusGroup *, umg, &user->user_menus_group) {
|
||||
BLO_read_list(reader, &umg->menus);
|
||||
BKE_blender_user_menus_group_idname_update(umg);
|
||||
LISTBASE_FOREACH (bUserMenu *, um, &umg->menus) {
|
||||
BLO_read_list(reader, &um->items);
|
||||
read_usermenuitems(reader, &um->items, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (user->user_menus.first != NULL && user->user_menus_group.first == NULL) {
|
||||
bUserMenusGroup *umg = BKU_blender_user_menu_default();
|
||||
BLI_addtail(&user->user_menus_group, umg);
|
||||
user->runtime.umg_select = umg;
|
||||
umg->menus = user->user_menus;
|
||||
BLI_listbase_clear(&user->user_menus);
|
||||
}
|
||||
|
||||
// XXX
|
||||
user->uifonts.first = user->uifonts.last = NULL;
|
||||
|
||||
@@ -8976,6 +9011,10 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead)
|
||||
/* Clear runtime data. */
|
||||
user->runtime.is_dirty = false;
|
||||
user->edit_studio_light = 0;
|
||||
user->runtime.um_space_select = 1;
|
||||
user->runtime.um_context_select = 1;
|
||||
user->runtime.um_item_select = NULL;
|
||||
user->runtime.umg_select = user->user_menus_group.first;
|
||||
|
||||
/* free fd->datamap again */
|
||||
oldnewmap_clear(fd->datamap);
|
||||
|
@@ -980,7 +980,37 @@ static void write_keymapitem(BlendWriter *writer, const wmKeyMapItem *kmi)
|
||||
}
|
||||
}
|
||||
|
||||
static void write_usermenuitems(BlendWriter *writer, const ListBase *lb)
|
||||
{
|
||||
LISTBASE_FOREACH (const bUserMenuItem *, umi, lb) {
|
||||
if (umi->type == USER_MENU_TYPE_OPERATOR) {
|
||||
const bUserMenuItem_Op *umi_op = (const bUserMenuItem_Op *)umi;
|
||||
BLO_write_struct(writer, bUserMenuItem_Op, umi_op);
|
||||
if (umi_op->prop) {
|
||||
IDP_BlendWrite(writer, umi_op->prop);
|
||||
}
|
||||
}
|
||||
else if (umi->type == USER_MENU_TYPE_MENU) {
|
||||
const bUserMenuItem_Menu *umi_mt = (const bUserMenuItem_Menu *)umi;
|
||||
BLO_write_struct(writer, bUserMenuItem_Menu, umi_mt);
|
||||
}
|
||||
else if (umi->type == USER_MENU_TYPE_PROP) {
|
||||
const bUserMenuItem_Prop *umi_pr = (const bUserMenuItem_Prop *)umi;
|
||||
BLO_write_struct(writer, bUserMenuItem_Prop, umi_pr);
|
||||
}
|
||||
else if (umi->type == USER_MENU_TYPE_SUBMENU) {
|
||||
const bUserMenuItem_SubMenu *umi_sm = (const bUserMenuItem_SubMenu *)umi;
|
||||
BLO_write_struct(writer, bUserMenuItem_SubMenu, umi_sm);
|
||||
write_usermenuitems(writer, &umi_sm->items);
|
||||
}
|
||||
else {
|
||||
BLO_write_struct(writer, bUserMenuItem, umi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void write_userdef(BlendWriter *writer, const UserDef *userdef)
|
||||
|
||||
{
|
||||
writestruct(writer->wd, USER, UserDef, 1, userdef);
|
||||
|
||||
@@ -1013,30 +1043,6 @@ static void write_userdef(BlendWriter *writer, const UserDef *userdef)
|
||||
}
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (const bUserMenu *, um, &userdef->user_menus) {
|
||||
BLO_write_struct(writer, bUserMenu, um);
|
||||
LISTBASE_FOREACH (const bUserMenuItem *, umi, &um->items) {
|
||||
if (umi->type == USER_MENU_TYPE_OPERATOR) {
|
||||
const bUserMenuItem_Op *umi_op = (const bUserMenuItem_Op *)umi;
|
||||
BLO_write_struct(writer, bUserMenuItem_Op, umi_op);
|
||||
if (umi_op->prop) {
|
||||
IDP_BlendWrite(writer, umi_op->prop);
|
||||
}
|
||||
}
|
||||
else if (umi->type == USER_MENU_TYPE_MENU) {
|
||||
const bUserMenuItem_Menu *umi_mt = (const bUserMenuItem_Menu *)umi;
|
||||
BLO_write_struct(writer, bUserMenuItem_Menu, umi_mt);
|
||||
}
|
||||
else if (umi->type == USER_MENU_TYPE_PROP) {
|
||||
const bUserMenuItem_Prop *umi_pr = (const bUserMenuItem_Prop *)umi;
|
||||
BLO_write_struct(writer, bUserMenuItem_Prop, umi_pr);
|
||||
}
|
||||
else {
|
||||
BLO_write_struct(writer, bUserMenuItem, umi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (const bAddon *, bext, &userdef->addons) {
|
||||
BLO_write_struct(writer, bAddon, bext);
|
||||
if (bext->prop) {
|
||||
@@ -1048,6 +1054,14 @@ static void write_userdef(BlendWriter *writer, const UserDef *userdef)
|
||||
BLO_write_struct(writer, bPathCompare, path_cmp);
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (const bUserMenusGroup *, umg, &userdef->user_menus_group) {
|
||||
BLO_write_struct(writer, bUserMenusGroup, umg);
|
||||
LISTBASE_FOREACH (const bUserMenu *, um, &umg->menus) {
|
||||
BLO_write_struct(writer, bUserMenu, um);
|
||||
write_usermenuitems(writer, &um->items);
|
||||
}
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (const uiStyle *, style, &userdef->uistyles) {
|
||||
BLO_write_struct(writer, uiStyle, style);
|
||||
}
|
||||
|
@@ -374,8 +374,12 @@ bool ED_operator_camera(struct bContext *C);
|
||||
|
||||
/* screen_user_menu.c */
|
||||
|
||||
bUserMenu **ED_screen_user_menus_find(const struct bContext *C, uint *r_len);
|
||||
struct bUserMenu *ED_screen_user_menu_ensure(struct bContext *C);
|
||||
struct bUserMenusGroup *ED_screen_user_menus_group_find(int id);
|
||||
struct bUserMenu **ED_screen_user_menus_find_menu(const struct bContext *C,
|
||||
uint *r_len,
|
||||
struct bUserMenusGroup *umg);
|
||||
struct bUserMenu **ED_screen_user_menus_find(const struct bContext *C, uint *r_len, int id);
|
||||
struct bUserMenu *ED_screen_user_menu_ensure(struct bContext *C, int id);
|
||||
|
||||
struct bUserMenuItem_Op *ED_screen_user_menu_item_find_operator(struct ListBase *lb,
|
||||
const struct wmOperatorType *ot,
|
||||
@@ -396,7 +400,8 @@ void ED_screen_user_menu_item_add_operator(struct ListBase *lb,
|
||||
void ED_screen_user_menu_item_add_menu(struct ListBase *lb,
|
||||
const char *ui_name,
|
||||
const struct MenuType *mt);
|
||||
void ED_screen_user_menu_item_add_prop(ListBase *lb,
|
||||
void ED_screen_user_menu_item_add_prop(struct bContext *C,
|
||||
ListBase *lb,
|
||||
const char *ui_name,
|
||||
const char *context_data_path,
|
||||
const char *prop_id,
|
||||
@@ -404,6 +409,14 @@ void ED_screen_user_menu_item_add_prop(ListBase *lb,
|
||||
|
||||
void ED_screen_user_menu_item_remove(struct ListBase *lb, struct bUserMenuItem *umi);
|
||||
void ED_screen_user_menu_register(void);
|
||||
bool screen_user_menu_draw_items(const struct bContext *C,
|
||||
struct uiLayout *layout,
|
||||
struct ListBase *lb,
|
||||
char type);
|
||||
void screen_user_menu_draw_begin(struct bContext *C,
|
||||
struct uiLayout *layout,
|
||||
char type,
|
||||
struct bUserMenusGroup *umg);
|
||||
|
||||
/* Cache display helpers */
|
||||
|
||||
|
@@ -2113,6 +2113,7 @@ void uiTemplateCacheFile(uiLayout *layout,
|
||||
const struct bContext *C,
|
||||
struct PointerRNA *ptr,
|
||||
const char *propname);
|
||||
void uiTemplateUserMenuItemProperties(uiLayout *layout, PointerRNA *ptr);
|
||||
|
||||
/* Default UIList class name, keep in sync with its declaration in bl_ui/__init__.py */
|
||||
#define UI_UL_DEFAULT_CLASS_NAME "UI_UL_list"
|
||||
|
@@ -437,7 +437,8 @@ static void ui_but_user_menu_add(bContext *C, uiBut *but, bUserMenu *um)
|
||||
}
|
||||
const char *prop_id = RNA_property_identifier(but->rnaprop);
|
||||
/* Note, ignore 'drawstr', use property idname always. */
|
||||
ED_screen_user_menu_item_add_prop(&um->items, "", member_id_data_path, prop_id, but->rnaindex);
|
||||
ED_screen_user_menu_item_add_prop(
|
||||
C, &um->items, "", member_id_data_path, prop_id, but->rnaindex);
|
||||
if (data_path) {
|
||||
MEM_freeN((void *)data_path);
|
||||
}
|
||||
@@ -453,7 +454,7 @@ static void ui_but_user_menu_add(bContext *C, uiBut *but, bUserMenu *um)
|
||||
static void popup_user_menu_add_or_replace_func(bContext *C, void *arg1, void *UNUSED(arg2))
|
||||
{
|
||||
uiBut *but = arg1;
|
||||
bUserMenu *um = ED_screen_user_menu_ensure(C);
|
||||
bUserMenu *um = ED_screen_user_menu_ensure(C, 0);
|
||||
U.runtime.is_dirty = true;
|
||||
ui_but_user_menu_add(C, but, um);
|
||||
}
|
||||
@@ -983,7 +984,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but)
|
||||
bool item_found = false;
|
||||
|
||||
uint um_array_len;
|
||||
bUserMenu **um_array = ED_screen_user_menus_find(C, &um_array_len);
|
||||
bUserMenu **um_array = ED_screen_user_menus_find(C, &um_array_len, 0);
|
||||
for (int um_index = 0; um_index < um_array_len; um_index++) {
|
||||
bUserMenu *um = um_array[um_index];
|
||||
if (um == NULL) {
|
||||
|
@@ -7390,3 +7390,80 @@ void uiTemplateFileSelectPath(uiLayout *layout, bContext *C, FileSelectParams *p
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Keymap Template
|
||||
* \{ */
|
||||
|
||||
static void template_user_menu_item_properties(uiLayout *layout,
|
||||
const char *title,
|
||||
PointerRNA *ptr)
|
||||
{
|
||||
uiLayout *flow, *box, *row;
|
||||
|
||||
uiItemS(layout);
|
||||
|
||||
if (title) {
|
||||
uiItemL(layout, title, ICON_NONE);
|
||||
}
|
||||
|
||||
flow = uiLayoutColumnFlow(layout, 2, false);
|
||||
|
||||
int i = 0;
|
||||
RNA_STRUCT_BEGIN_SKIP_RNA_TYPE (ptr, prop) {
|
||||
const bool is_set = RNA_property_is_set(ptr, prop);
|
||||
uiBut *but;
|
||||
|
||||
// recurse for nested properties
|
||||
if (RNA_property_type(prop) == PROP_POINTER) {
|
||||
PointerRNA propptr = RNA_property_pointer_get(ptr, prop);
|
||||
|
||||
if (propptr.data && RNA_struct_is_a(propptr.type, &RNA_OperatorProperties)) {
|
||||
const char *name = RNA_property_ui_name(prop);
|
||||
template_user_menu_item_properties(layout, name, &propptr);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
box = uiLayoutBox(flow);
|
||||
uiLayoutSetActive(box, is_set);
|
||||
row = uiLayoutRow(box, false);
|
||||
|
||||
// property value
|
||||
uiItemFullR(row, ptr, prop, -1, 0, 0, NULL, ICON_NONE);
|
||||
|
||||
if (is_set) {
|
||||
// unset operator
|
||||
uiBlock *block = uiLayoutGetBlock(row);
|
||||
UI_block_emboss_set(block, UI_EMBOSS_NONE);
|
||||
but = uiDefIconButO(block,
|
||||
UI_BTYPE_BUT,
|
||||
"UI_OT_unset_property_button",
|
||||
WM_OP_EXEC_DEFAULT,
|
||||
ICON_X,
|
||||
0,
|
||||
0,
|
||||
UI_UNIT_X,
|
||||
UI_UNIT_Y,
|
||||
NULL);
|
||||
but->rnapoin = *ptr;
|
||||
but->rnaprop = prop;
|
||||
UI_block_emboss_set(block, UI_EMBOSS);
|
||||
}
|
||||
}
|
||||
RNA_STRUCT_END;
|
||||
}
|
||||
|
||||
void uiTemplateUserMenuItemProperties(uiLayout *layout, PointerRNA *ptr)
|
||||
{
|
||||
PointerRNA propptr = RNA_pointer_get(ptr, "prop");
|
||||
|
||||
if (propptr.data) {
|
||||
uiBut *but = uiLayoutGetBlock(layout)->buttons.last;
|
||||
|
||||
WM_operator_properties_sanitize(&propptr, false);
|
||||
template_user_menu_item_properties(layout, NULL, &propptr);
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
@@ -67,10 +67,23 @@ static const char *screen_menu_context_string(const bContext *C, const SpaceLink
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Menu Type
|
||||
/** \name Menu Group
|
||||
* \{ */
|
||||
|
||||
bUserMenu **ED_screen_user_menus_find(const bContext *C, uint *r_len)
|
||||
bUserMenusGroup *ED_screen_user_menus_group_find(int id)
|
||||
{
|
||||
int index = 0;
|
||||
LISTBASE_FOREACH (bUserMenusGroup *, umg, &U.user_menus_group) {
|
||||
if (index == id)
|
||||
return umg;
|
||||
index++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
bUserMenu **ED_screen_user_menus_find_menu(const bContext *C, uint *r_len, bUserMenusGroup *umg)
|
||||
{
|
||||
SpaceLink *sl = CTX_wm_space_data(C);
|
||||
|
||||
@@ -83,23 +96,33 @@ bUserMenu **ED_screen_user_menus_find(const bContext *C, uint *r_len)
|
||||
const char *context = screen_menu_context_string(C, sl);
|
||||
uint array_len = 3;
|
||||
bUserMenu **um_array = MEM_calloc_arrayN(array_len, sizeof(*um_array), __func__);
|
||||
um_array[0] = BKE_blender_user_menu_find(&U.user_menus, sl->spacetype, context);
|
||||
um_array[0] = BKE_blender_user_menu_find(&umg->menus, sl->spacetype, context);
|
||||
um_array[1] = (sl->spacetype != SPACE_TOPBAR) ?
|
||||
BKE_blender_user_menu_find(&U.user_menus, SPACE_TOPBAR, context_mode) :
|
||||
BKE_blender_user_menu_find(&umg->menus, SPACE_TOPBAR, context_mode) :
|
||||
NULL;
|
||||
um_array[2] = (sl->spacetype == SPACE_VIEW3D) ?
|
||||
BKE_blender_user_menu_find(&U.user_menus, SPACE_PROPERTIES, context_mode) :
|
||||
BKE_blender_user_menu_find(&umg->menus, SPACE_PROPERTIES, context_mode) :
|
||||
NULL;
|
||||
|
||||
*r_len = array_len;
|
||||
return um_array;
|
||||
}
|
||||
|
||||
bUserMenu *ED_screen_user_menu_ensure(bContext *C)
|
||||
bUserMenu **ED_screen_user_menus_find(const bContext *C, uint *r_len, int id)
|
||||
{
|
||||
bUserMenusGroup *umg = ED_screen_user_menus_group_find(id);
|
||||
if (!umg)
|
||||
return NULL;
|
||||
return ED_screen_user_menus_find_menu(C, r_len, umg);
|
||||
}
|
||||
|
||||
bUserMenu *ED_screen_user_menu_ensure(bContext *C, int id)
|
||||
{
|
||||
bUserMenusGroup *umg = ED_screen_user_menus_group_find(id);
|
||||
if (!umg)
|
||||
return NULL;
|
||||
SpaceLink *sl = CTX_wm_space_data(C);
|
||||
const char *context = screen_menu_context_string(C, sl);
|
||||
return BKE_blender_user_menu_ensure(&U.user_menus, sl->spacetype, context);
|
||||
return BKE_blender_user_menu_ensure(&umg->menus, sl->spacetype, context);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@@ -169,6 +192,9 @@ void ED_screen_user_menu_item_add_operator(ListBase *lb,
|
||||
STRNCPY(umi_op->item.ui_name, ui_name);
|
||||
}
|
||||
STRNCPY(umi_op->op_idname, ot->idname);
|
||||
umi_op->ptr = NULL;
|
||||
umi_op->prop = NULL;
|
||||
WM_operator_properties_alloc(&(umi_op->ptr), &(umi_op->prop), ot->idname);
|
||||
umi_op->prop = prop ? IDP_CopyProperty(prop) : NULL;
|
||||
}
|
||||
|
||||
@@ -182,7 +208,8 @@ void ED_screen_user_menu_item_add_menu(ListBase *lb, const char *ui_name, const
|
||||
STRNCPY(umi_mt->mt_idname, mt->idname);
|
||||
}
|
||||
|
||||
void ED_screen_user_menu_item_add_prop(ListBase *lb,
|
||||
void ED_screen_user_menu_item_add_prop(bContext *C,
|
||||
ListBase *lb,
|
||||
const char *ui_name,
|
||||
const char *context_data_path,
|
||||
const char *prop_id,
|
||||
@@ -194,6 +221,39 @@ void ED_screen_user_menu_item_add_prop(ListBase *lb,
|
||||
STRNCPY(umi_pr->context_data_path, context_data_path);
|
||||
STRNCPY(umi_pr->prop_id, prop_id);
|
||||
umi_pr->prop_index = prop_index;
|
||||
|
||||
/* Copy current context name into ui_name if null */
|
||||
if (ui_name && ui_name[0] != '\0')
|
||||
return;
|
||||
char *data_path = strchr(umi_pr->context_data_path, '.');
|
||||
if (data_path) {
|
||||
*data_path = '\0';
|
||||
}
|
||||
|
||||
PointerRNA ptr = CTX_data_pointer_get(C, umi_pr->context_data_path);
|
||||
if (ptr.type == NULL) {
|
||||
PointerRNA ctx_ptr;
|
||||
RNA_pointer_create(NULL, &RNA_Context, (void *)C, &ctx_ptr);
|
||||
if (!RNA_path_resolve_full(&ctx_ptr, umi_pr->context_data_path, &ptr, NULL, NULL)) {
|
||||
ptr.type = NULL;
|
||||
}
|
||||
}
|
||||
if (data_path) {
|
||||
*data_path = '.';
|
||||
data_path += 1;
|
||||
}
|
||||
|
||||
if (ptr.type != NULL) {
|
||||
PropertyRNA *prop = NULL;
|
||||
PointerRNA prop_ptr = ptr;
|
||||
if ((data_path == NULL) || RNA_path_resolve_full(&ptr, data_path, &prop_ptr, NULL, NULL)) {
|
||||
prop = RNA_struct_find_property(&prop_ptr, umi_pr->prop_id);
|
||||
}
|
||||
if (prop) {
|
||||
const char *name = RNA_property_ui_name(prop);
|
||||
BLI_strncpy(umi_pr->item.ui_name, name, FILE_MAX);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ED_screen_user_menu_item_remove(ListBase *lb, bUserMenuItem *umi)
|
||||
@@ -208,97 +268,146 @@ void ED_screen_user_menu_item_remove(ListBase *lb, bUserMenuItem *umi)
|
||||
/** \name Menu Definition
|
||||
* \{ */
|
||||
|
||||
static void screen_user_menu_draw(const bContext *C, Menu *menu)
|
||||
static void screen_user_menu_draw_submenu(bContext *C, uiLayout *layout, void *arg)
|
||||
{
|
||||
ListBase *lb = (ListBase *)arg;
|
||||
|
||||
screen_user_menu_draw_items(C, layout, lb, false);
|
||||
}
|
||||
|
||||
bool screen_user_menu_draw_items(const bContext *C, uiLayout *layout, ListBase *lb, char type)
|
||||
{
|
||||
/* Enable when we have the ability to edit menus. */
|
||||
const bool show_missing = false;
|
||||
char label[512];
|
||||
const bool show_missing = type;
|
||||
bool is_empty = true;
|
||||
|
||||
int i = 0;
|
||||
|
||||
LISTBASE_FOREACH (bUserMenuItem *, umi, lb) {
|
||||
if (type == 1 && i > 7)
|
||||
return is_empty;
|
||||
const char *ui_name = umi->ui_name[0] ? umi->ui_name : NULL;
|
||||
if (umi->type == USER_MENU_TYPE_OPERATOR) {
|
||||
bUserMenuItem_Op *umi_op = (bUserMenuItem_Op *)umi;
|
||||
wmOperatorType *ot = WM_operatortype_find(umi_op->op_idname, false);
|
||||
if (ot != NULL) {
|
||||
IDProperty *prop = umi_op->prop ? IDP_CopyProperty(umi_op->prop) : NULL;
|
||||
uiItemFullO_ptr(layout, ot, ui_name, umi->icon, prop, umi_op->opcontext, 0, NULL);
|
||||
is_empty = false;
|
||||
}
|
||||
else {
|
||||
if (show_missing) {
|
||||
SNPRINTF(label, "Missing: %s", umi_op->op_idname);
|
||||
uiItemL(layout, label, ICON_NONE);
|
||||
}
|
||||
i--;
|
||||
}
|
||||
}
|
||||
else if (umi->type == USER_MENU_TYPE_MENU) {
|
||||
bUserMenuItem_Menu *umi_mt = (bUserMenuItem_Menu *)umi;
|
||||
MenuType *mt = WM_menutype_find(umi_mt->mt_idname, false);
|
||||
if (mt != NULL) {
|
||||
uiItemM_ptr(layout, mt, ui_name, umi->icon);
|
||||
is_empty = false;
|
||||
}
|
||||
else {
|
||||
if (show_missing) {
|
||||
SNPRINTF(label, "Missing: %s", umi_mt->mt_idname);
|
||||
uiItemL(layout, label, ICON_NONE);
|
||||
}
|
||||
i--;
|
||||
}
|
||||
}
|
||||
else if (umi->type == USER_MENU_TYPE_SUBMENU) {
|
||||
bUserMenuItem_SubMenu *umi_mt = (bUserMenuItem_SubMenu *)umi;
|
||||
|
||||
uiItemMenuF(
|
||||
layout, ui_name, umi->icon, &screen_user_menu_draw_submenu, (void *)&umi_mt->items);
|
||||
is_empty = false;
|
||||
}
|
||||
else if (umi->type == USER_MENU_TYPE_PROP) {
|
||||
bUserMenuItem_Prop *umi_pr = (bUserMenuItem_Prop *)umi;
|
||||
|
||||
char *data_path = strchr(umi_pr->context_data_path, '.');
|
||||
if (data_path) {
|
||||
*data_path = '\0';
|
||||
}
|
||||
PointerRNA ptr = CTX_data_pointer_get(C, umi_pr->context_data_path);
|
||||
if (ptr.type == NULL) {
|
||||
PointerRNA ctx_ptr;
|
||||
RNA_pointer_create(NULL, &RNA_Context, (void *)C, &ctx_ptr);
|
||||
if (!RNA_path_resolve_full(&ctx_ptr, umi_pr->context_data_path, &ptr, NULL, NULL)) {
|
||||
ptr.type = NULL;
|
||||
}
|
||||
}
|
||||
if (data_path) {
|
||||
*data_path = '.';
|
||||
data_path += 1;
|
||||
}
|
||||
|
||||
bool ok = false;
|
||||
if (ptr.type != NULL) {
|
||||
PropertyRNA *prop = NULL;
|
||||
PointerRNA prop_ptr = ptr;
|
||||
if ((data_path == NULL) || RNA_path_resolve_full(&ptr, data_path, &prop_ptr, NULL, NULL)) {
|
||||
prop = RNA_struct_find_property(&prop_ptr, umi_pr->prop_id);
|
||||
if (prop) {
|
||||
ok = true;
|
||||
uiItemFullR(layout, &prop_ptr, prop, umi_pr->prop_index, 0, 0, ui_name, umi->icon);
|
||||
is_empty = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ok) {
|
||||
if (show_missing) {
|
||||
SNPRINTF(label, "Missing: %s.%s", umi_pr->context_data_path, umi_pr->prop_id);
|
||||
uiItemL(layout, label, ICON_NONE);
|
||||
}
|
||||
i--;
|
||||
}
|
||||
}
|
||||
else if (umi->type == USER_MENU_TYPE_SEP) {
|
||||
uiItemS(layout);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return is_empty;
|
||||
}
|
||||
|
||||
void screen_user_menu_draw_begin(bContext *C, uiLayout *layout, char type, bUserMenusGroup *umg)
|
||||
{
|
||||
uint um_array_len;
|
||||
bUserMenu **um_array = ED_screen_user_menus_find(C, &um_array_len);
|
||||
bUserMenu **um_array = ED_screen_user_menus_find_menu(C, &um_array_len, umg);
|
||||
bool is_empty = true;
|
||||
for (int um_index = 0; um_index < um_array_len; um_index++) {
|
||||
bUserMenu *um = um_array[um_index];
|
||||
if (um == NULL) {
|
||||
continue;
|
||||
}
|
||||
LISTBASE_FOREACH (bUserMenuItem *, umi, &um->items) {
|
||||
const char *ui_name = umi->ui_name[0] ? umi->ui_name : NULL;
|
||||
if (umi->type == USER_MENU_TYPE_OPERATOR) {
|
||||
bUserMenuItem_Op *umi_op = (bUserMenuItem_Op *)umi;
|
||||
wmOperatorType *ot = WM_operatortype_find(umi_op->op_idname, false);
|
||||
if (ot != NULL) {
|
||||
IDProperty *prop = umi_op->prop ? IDP_CopyProperty(umi_op->prop) : NULL;
|
||||
uiItemFullO_ptr(menu->layout, ot, ui_name, ICON_NONE, prop, umi_op->opcontext, 0, NULL);
|
||||
is_empty = false;
|
||||
}
|
||||
else {
|
||||
if (show_missing) {
|
||||
SNPRINTF(label, "Missing: %s", umi_op->op_idname);
|
||||
uiItemL(menu->layout, label, ICON_NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (umi->type == USER_MENU_TYPE_MENU) {
|
||||
bUserMenuItem_Menu *umi_mt = (bUserMenuItem_Menu *)umi;
|
||||
MenuType *mt = WM_menutype_find(umi_mt->mt_idname, false);
|
||||
if (mt != NULL) {
|
||||
uiItemM_ptr(menu->layout, mt, ui_name, ICON_NONE);
|
||||
is_empty = false;
|
||||
}
|
||||
else {
|
||||
if (show_missing) {
|
||||
SNPRINTF(label, "Missing: %s", umi_mt->mt_idname);
|
||||
uiItemL(menu->layout, label, ICON_NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (umi->type == USER_MENU_TYPE_PROP) {
|
||||
bUserMenuItem_Prop *umi_pr = (bUserMenuItem_Prop *)umi;
|
||||
is_empty = screen_user_menu_draw_items(C, layout, &um->items, true) && is_empty;
|
||||
}
|
||||
if (um_array) {
|
||||
MEM_freeN(um_array);
|
||||
}
|
||||
|
||||
char *data_path = strchr(umi_pr->context_data_path, '.');
|
||||
if (data_path) {
|
||||
*data_path = '\0';
|
||||
}
|
||||
PointerRNA ptr = CTX_data_pointer_get(C, umi_pr->context_data_path);
|
||||
if (ptr.type == NULL) {
|
||||
PointerRNA ctx_ptr;
|
||||
RNA_pointer_create(NULL, &RNA_Context, (void *)C, &ctx_ptr);
|
||||
if (!RNA_path_resolve_full(&ctx_ptr, umi_pr->context_data_path, &ptr, NULL, NULL)) {
|
||||
ptr.type = NULL;
|
||||
}
|
||||
}
|
||||
if (data_path) {
|
||||
*data_path = '.';
|
||||
data_path += 1;
|
||||
}
|
||||
if (is_empty && type == 0) {
|
||||
uiItemL(layout, TIP_("No menu items found"), ICON_NONE);
|
||||
uiItemL(layout, TIP_("Right click on buttons to add them to this menu"), ICON_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
bool ok = false;
|
||||
if (ptr.type != NULL) {
|
||||
PropertyRNA *prop = NULL;
|
||||
PointerRNA prop_ptr = ptr;
|
||||
if ((data_path == NULL) ||
|
||||
RNA_path_resolve_full(&ptr, data_path, &prop_ptr, NULL, NULL)) {
|
||||
prop = RNA_struct_find_property(&prop_ptr, umi_pr->prop_id);
|
||||
if (prop) {
|
||||
ok = true;
|
||||
uiItemFullR(
|
||||
menu->layout, &prop_ptr, prop, umi_pr->prop_index, 0, 0, ui_name, ICON_NONE);
|
||||
is_empty = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ok) {
|
||||
if (show_missing) {
|
||||
SNPRINTF(label, "Missing: %s.%s", umi_pr->context_data_path, umi_pr->prop_id);
|
||||
uiItemL(menu->layout, label, ICON_NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (umi->type == USER_MENU_TYPE_SEP) {
|
||||
uiItemS(menu->layout);
|
||||
}
|
||||
static void screen_user_menu_draw(const bContext *C, Menu *menu)
|
||||
{
|
||||
uint um_array_len;
|
||||
bUserMenu **um_array = ED_screen_user_menus_find(C, &um_array_len, 0);
|
||||
bool is_empty = true;
|
||||
for (int um_index = 0; um_index < um_array_len; um_index++) {
|
||||
bUserMenu *um = um_array[um_index];
|
||||
if (um == NULL) {
|
||||
continue;
|
||||
}
|
||||
is_empty = is_empty || screen_user_menu_draw_items(C, menu->layout, &um->items, true);
|
||||
}
|
||||
if (um_array) {
|
||||
MEM_freeN(um_array);
|
||||
|
@@ -136,6 +136,7 @@ static void userpref_main_region_layout(const bContext *C, ARegion *region)
|
||||
i = 0;
|
||||
}
|
||||
const char *id = items[i].identifier;
|
||||
|
||||
BLI_assert(strlen(id) < sizeof(id_lower));
|
||||
STRNCPY(id_lower, id);
|
||||
BLI_str_tolower_ascii(id_lower, strlen(id_lower));
|
||||
|
@@ -515,27 +515,42 @@ typedef struct bPathCompare {
|
||||
char _pad0[7];
|
||||
} bPathCompare;
|
||||
|
||||
typedef struct bUserMenusGroup {
|
||||
struct bUserMenusGroup *next, *prev;
|
||||
/* bUserMenu */
|
||||
char idname[64];
|
||||
char name[64];
|
||||
char type;
|
||||
char _pad0[7];
|
||||
struct ListBase menus;
|
||||
} bUserMenusGroup;
|
||||
|
||||
typedef struct bUserMenu {
|
||||
struct bUserMenu *next, *prev;
|
||||
char space_type;
|
||||
char _pad0[7];
|
||||
char context[64];
|
||||
/* bUserMenuItem */
|
||||
ListBase items;
|
||||
struct ListBase items;
|
||||
} bUserMenu;
|
||||
|
||||
/** May be part of #bUserMenu or other list. */
|
||||
typedef struct bUserMenuItem {
|
||||
struct bUserMenuItem *next, *prev;
|
||||
struct bUserMenuItem_SubMenu *parent;
|
||||
int icon;
|
||||
char ui_name[64];
|
||||
char type;
|
||||
char _pad0[7];
|
||||
// editor
|
||||
char is_selected;
|
||||
char _pad0[2];
|
||||
} bUserMenuItem;
|
||||
|
||||
typedef struct bUserMenuItem_Op {
|
||||
bUserMenuItem item;
|
||||
char op_idname[64];
|
||||
struct IDProperty *prop;
|
||||
struct PointerRNA *ptr;
|
||||
char opcontext;
|
||||
char _pad0[7];
|
||||
} bUserMenuItem_Op;
|
||||
@@ -545,6 +560,12 @@ typedef struct bUserMenuItem_Menu {
|
||||
char mt_idname[64];
|
||||
} bUserMenuItem_Menu;
|
||||
|
||||
typedef struct bUserMenuItem_SubMenu {
|
||||
bUserMenuItem item;
|
||||
struct ListBase items;
|
||||
char name[64];
|
||||
} bUserMenuItem_SubMenu;
|
||||
|
||||
typedef struct bUserMenuItem_Prop {
|
||||
bUserMenuItem item;
|
||||
char context_data_path[256];
|
||||
@@ -558,6 +579,7 @@ enum {
|
||||
USER_MENU_TYPE_OPERATOR = 2,
|
||||
USER_MENU_TYPE_MENU = 3,
|
||||
USER_MENU_TYPE_PROP = 4,
|
||||
USER_MENU_TYPE_SUBMENU = 5,
|
||||
};
|
||||
|
||||
typedef struct SolidLight {
|
||||
@@ -582,7 +604,14 @@ typedef struct WalkNavigation {
|
||||
|
||||
typedef struct UserDef_Runtime {
|
||||
char is_dirty;
|
||||
char _pad0[7];
|
||||
|
||||
/* User menu editor runtime datas */
|
||||
char um_space_select;
|
||||
char um_context_select;
|
||||
char um_expanded;
|
||||
char _pad0[4];
|
||||
struct bUserMenuItem *um_item_select;
|
||||
struct bUserMenusGroup *umg_select;
|
||||
} UserDef_Runtime;
|
||||
|
||||
/**
|
||||
@@ -722,8 +751,10 @@ typedef struct UserDef {
|
||||
struct ListBase user_keyconfig_prefs;
|
||||
struct ListBase addons;
|
||||
struct ListBase autoexec_paths;
|
||||
/** #bUserMenu. */
|
||||
/** #bUserMenu : deprecated, keep for compatibility. */
|
||||
struct ListBase user_menus;
|
||||
/** #bUserMenuGroups. */
|
||||
struct ListBase user_menus_group;
|
||||
|
||||
char keyconfigstr[64];
|
||||
|
||||
@@ -913,16 +944,17 @@ typedef enum eUserPref_Section {
|
||||
USER_SECTION_ADDONS = 6,
|
||||
USER_SECTION_LIGHT = 7,
|
||||
USER_SECTION_KEYMAP = 8,
|
||||
USER_SECTION_USER_MENUS = 9,
|
||||
#ifdef WITH_USERDEF_WORKSPACES
|
||||
USER_SECTION_WORKSPACE_CONFIG = 9,
|
||||
USER_SECTION_WORKSPACE_ADDONS = 10,
|
||||
USER_SECTION_WORKSPACE_KEYMAPS = 11,
|
||||
USER_SECTION_WORKSPACE_CONFIG = 10,
|
||||
USER_SECTION_WORKSPACE_ADDONS = 11,
|
||||
USER_SECTION_WORKSPACE_KEYMAPS = 12,
|
||||
#endif
|
||||
USER_SECTION_VIEWPORT = 12,
|
||||
USER_SECTION_ANIMATION = 13,
|
||||
USER_SECTION_NAVIGATION = 14,
|
||||
USER_SECTION_FILE_PATHS = 15,
|
||||
USER_SECTION_EXPERIMENTAL = 16,
|
||||
USER_SECTION_VIEWPORT = 13,
|
||||
USER_SECTION_ANIMATION = 14,
|
||||
USER_SECTION_NAVIGATION = 15,
|
||||
USER_SECTION_FILE_PATHS = 16,
|
||||
USER_SECTION_EXPERIMENTAL = 17,
|
||||
} eUserPref_Section;
|
||||
|
||||
/** #UserDef_SpaceData.flag (State of the user preferences UI). */
|
||||
|
@@ -474,6 +474,7 @@ extern StructRNA RNA_PreferencesEdit;
|
||||
extern StructRNA RNA_PreferencesFilePaths;
|
||||
extern StructRNA RNA_PreferencesInput;
|
||||
extern StructRNA RNA_PreferencesKeymap;
|
||||
extern StructRNA RNA_PreferencesUserMenus;
|
||||
extern StructRNA RNA_PreferencesSystem;
|
||||
extern StructRNA RNA_PreferencesView;
|
||||
extern StructRNA RNA_Property;
|
||||
|
@@ -1869,7 +1869,7 @@ void RNA_def_property_enum_items(PropertyRNA *prop, const EnumPropertyItem *item
|
||||
{
|
||||
StructRNA *srna = DefRNA.laststruct;
|
||||
int i, defaultfound = 0;
|
||||
|
||||
|
||||
switch (prop->type) {
|
||||
case PROP_ENUM: {
|
||||
EnumPropertyRNA *eprop = (EnumPropertyRNA *)prop;
|
||||
|
@@ -1639,6 +1639,12 @@ void RNA_api_ui_layout(StructRNA *srna)
|
||||
RNA_def_property_ui_text(parm, "Item", "");
|
||||
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
|
||||
api_ui_item_common_text(func);
|
||||
|
||||
/* User menu template */
|
||||
func = RNA_def_function(
|
||||
srna, "template_user_menu_item_properties", "uiTemplateUserMenuItemProperties");
|
||||
parm = RNA_def_pointer(func, "item", "um_item_op", "", "");
|
||||
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -88,6 +88,7 @@ const EnumPropertyItem rna_enum_preference_section_items[] = {
|
||||
{USER_SECTION_INPUT, "INPUT", 0, "Input", ""},
|
||||
{USER_SECTION_NAVIGATION, "NAVIGATION", 0, "Navigation", ""},
|
||||
{USER_SECTION_KEYMAP, "KEYMAP", 0, "Keymap", ""},
|
||||
{USER_SECTION_USER_MENUS, "USER_MENUS", 0, "User Menus", ""},
|
||||
{0, "", 0, NULL, NULL},
|
||||
{USER_SECTION_SYSTEM, "SYSTEM", 0, "System", ""},
|
||||
{USER_SECTION_SAVE_LOAD, "SAVE_LOAD", 0, "Save & Load", ""},
|
||||
@@ -177,6 +178,7 @@ static const EnumPropertyItem rna_enum_userdef_viewport_aa_items[] = {
|
||||
# include "DNA_screen_types.h"
|
||||
|
||||
# include "BKE_blender.h"
|
||||
# include "BKE_blender_user_menu.h"
|
||||
# include "BKE_global.h"
|
||||
# include "BKE_idprop.h"
|
||||
# include "BKE_image.h"
|
||||
@@ -186,6 +188,8 @@ static const EnumPropertyItem rna_enum_userdef_viewport_aa_items[] = {
|
||||
# include "BKE_pbvh.h"
|
||||
# include "BKE_screen.h"
|
||||
|
||||
# include "ED_screen.h"
|
||||
|
||||
# include "DEG_depsgraph.h"
|
||||
|
||||
# include "GPU_extensions.h"
|
||||
@@ -531,6 +535,11 @@ static PointerRNA rna_UserDef_keymap_get(PointerRNA *ptr)
|
||||
return rna_pointer_inherit_refine(ptr, &RNA_PreferencesKeymap, ptr->data);
|
||||
}
|
||||
|
||||
static PointerRNA rna_UserDef_user_menus_get(PointerRNA *ptr)
|
||||
{
|
||||
return rna_pointer_inherit_refine(ptr, &RNA_PreferencesUserMenus, ptr->data);
|
||||
}
|
||||
|
||||
static PointerRNA rna_UserDef_filepaths_get(PointerRNA *ptr)
|
||||
{
|
||||
return rna_pointer_inherit_refine(ptr, &RNA_PreferencesFilePaths, ptr->data);
|
||||
@@ -1068,6 +1077,549 @@ static void rna_UserDef_studiolight_light_ambient_get(PointerRNA *ptr, float *va
|
||||
copy_v3_v3(values, sl->light_ambient);
|
||||
}
|
||||
|
||||
/* User Menus Functions */
|
||||
|
||||
static bUserMenu *rna_UserDef_usermenus_get_current(UserDef *userdef, bool ensure)
|
||||
{
|
||||
const char **contexts_list = CTX_data_list_mode_string();
|
||||
# if 0 /* UNUSED */
|
||||
ListBase *umg_list = &userdef->user_menus_group;
|
||||
# endif
|
||||
|
||||
bUserMenusGroup *umg = userdef->runtime.umg_select;
|
||||
bUserMenu *bum = NULL;
|
||||
|
||||
if (!umg)
|
||||
return NULL;
|
||||
if (userdef->runtime.um_space_select > 0) {
|
||||
if (umg->type) {
|
||||
bum = BKE_blender_user_menu_find(&umg->menus,
|
||||
userdef->runtime.um_space_select,
|
||||
contexts_list[userdef->runtime.um_context_select]);
|
||||
if (!bum) {
|
||||
bum = BKE_blender_user_menu_ensure(&umg->menus,
|
||||
userdef->runtime.um_space_select,
|
||||
contexts_list[userdef->runtime.um_context_select]);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
BKE_blender_user_menu_item_add(&bum->items, USER_MENU_TYPE_SEP);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (ensure) {
|
||||
bum = BKE_blender_user_menu_ensure(&umg->menus,
|
||||
userdef->runtime.um_space_select,
|
||||
contexts_list[userdef->runtime.um_context_select]);
|
||||
}
|
||||
else {
|
||||
bum = BKE_blender_user_menu_find(&umg->menus,
|
||||
userdef->runtime.um_space_select,
|
||||
contexts_list[userdef->runtime.um_context_select]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return bum;
|
||||
}
|
||||
|
||||
/*static bUserMenuItem *rna_UserDef_usermenus_get_current_item(UserDef *userdef)
|
||||
{
|
||||
int id = userdef->runtime.um_item_select - 1;
|
||||
if (id < 0) return NULL;
|
||||
|
||||
bUserMenu *bum = rna_UserDef_usermenus_get_current(userdef, false);
|
||||
if (bum) {
|
||||
|
||||
ListBase *lb = &bum->items;
|
||||
int i = 0;
|
||||
for (bUserMenuItem *umi = lb->first; umi; umi = umi->next, i++) {
|
||||
if (i == id)
|
||||
return umi;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}*/
|
||||
|
||||
static bUserMenusGroup *rna_UserDef_usermenus_get_group(UserDef *userdef, const char *idname)
|
||||
{
|
||||
return BKE_blender_user_menus_group_find(&userdef->user_menus_group, idname);
|
||||
}
|
||||
|
||||
static void rna_UserDef_usermenus_set_group(UserDef *userdef, bUserMenusGroup *umg)
|
||||
{
|
||||
userdef->runtime.umg_select = umg;
|
||||
}
|
||||
|
||||
static void rna_UserDef_usermenus_add_group(UserDef *userdef)
|
||||
{
|
||||
bUserMenusGroup *umg = BKE_blender_user_menus_group_new("new menu");
|
||||
BLI_addtail(&userdef->user_menus_group, umg);
|
||||
userdef->runtime.umg_select = umg;
|
||||
}
|
||||
|
||||
static void rna_UserDef_usermenus_remove_group(UserDef *userdef)
|
||||
{
|
||||
bUserMenusGroup *umg = userdef->runtime.umg_select;
|
||||
|
||||
userdef->runtime.umg_select = umg->prev;
|
||||
if (!userdef->runtime.umg_select) {
|
||||
userdef->runtime.umg_select = umg->next;
|
||||
if (!userdef->runtime.umg_select) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
BLI_remlink(&userdef->user_menus_group, umg);
|
||||
BKE_blender_user_menu_free_list(&umg->menus);
|
||||
MEM_freeN(umg);
|
||||
}
|
||||
|
||||
static bool rna_UserDef_usermenus_has_item(UserDef *userdef)
|
||||
{
|
||||
bUserMenu *bum = rna_UserDef_usermenus_get_current(userdef, false);
|
||||
if (!bum)
|
||||
return false;
|
||||
|
||||
ListBase *lb = &bum->items;
|
||||
bUserMenuItem *umi = lb->first;
|
||||
if (!umi)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int rna_UserDef_usermenus_items_length(UserDef *userdef)
|
||||
{
|
||||
bUserMenu *bum = rna_UserDef_usermenus_get_current(userdef, false);
|
||||
if (!bum)
|
||||
return 0;
|
||||
|
||||
ListBase *lb = &bum->items;
|
||||
int i = 0;
|
||||
for (bUserMenuItem *umi = lb->first; umi; umi = umi->next, i++)
|
||||
;
|
||||
return i;
|
||||
}
|
||||
|
||||
static int rna_UserDef_usermenus_spacetypes_get(PointerRNA *ptr)
|
||||
{
|
||||
UserDef *userdef = (UserDef *)ptr->data;
|
||||
int id = userdef->runtime.um_space_select;
|
||||
id = (id <= 0) ? USER_SECTION_EDITING : id;
|
||||
return id;
|
||||
}
|
||||
|
||||
static void rna_UserDef_usermenus_active_item_set(PointerRNA *ptr, bool value)
|
||||
{
|
||||
bUserMenuItem *umi = (bUserMenuItem *)ptr->data;
|
||||
|
||||
if (value) {
|
||||
U.runtime.um_item_select = umi;
|
||||
}
|
||||
umi->is_selected = value;
|
||||
}
|
||||
|
||||
static const EnumPropertyItem *rna_UserDef_usermenus_spacetypes_itemf(bContext *UNUSED(C),
|
||||
PointerRNA *UNUSED(ptr),
|
||||
PropertyRNA *UNUSED(prop),
|
||||
bool *r_free)
|
||||
{
|
||||
int totitem = 0;
|
||||
EnumPropertyItem *item = NULL;
|
||||
int i;
|
||||
const ListBase *spacetypes = BKE_spacetypes_list();
|
||||
|
||||
SpaceType *st = NULL;
|
||||
for (i = 0, st = spacetypes->first; st; st = st->next, i++) {
|
||||
int id = st->spaceid;
|
||||
EnumPropertyItem new_item = {id, st->name, 0, st->name, st->name};
|
||||
RNA_enum_item_add(&item, &totitem, &new_item);
|
||||
}
|
||||
|
||||
# ifndef NDEBUG
|
||||
if (i == 0) {
|
||||
EnumPropertyItem new_item = {i, "NO_SPACETYPE", 0, "No spacetype available", ""};
|
||||
RNA_enum_item_add(&item, &totitem, &new_item);
|
||||
}
|
||||
# endif
|
||||
|
||||
RNA_enum_item_end(&item, &totitem);
|
||||
*r_free = true;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
static const EnumPropertyItem *rna_UserDef_usermenus_contexts_itemf(bContext *UNUSED(C),
|
||||
PointerRNA *UNUSED(ptr),
|
||||
PropertyRNA *UNUSED(prop),
|
||||
bool *r_free)
|
||||
{
|
||||
static const char *contexts_list[] = {
|
||||
"mesh edit", "curve edit", "surface edit", "text edit",
|
||||
"armature edit", "mball edit", "lattice edit", "pose mode",
|
||||
"sculpt mode", "weight paint", "vertex paint", "image paint",
|
||||
"particle mode", "object mode", "greasepencil paint", "greasepencil edit",
|
||||
"greasepencil sculpt", "greasepencil weight", "greasepencil vertex", NULL,
|
||||
};
|
||||
|
||||
int totitem = 0;
|
||||
EnumPropertyItem *item = NULL;
|
||||
int i;
|
||||
|
||||
for (i = 0; contexts_list[i]; i++) {
|
||||
EnumPropertyItem new_item = {i, contexts_list[i], 0, contexts_list[i], contexts_list[i]};
|
||||
RNA_enum_item_add(&item, &totitem, &new_item);
|
||||
}
|
||||
|
||||
# ifndef NDEBUG
|
||||
if (i == 0) {
|
||||
EnumPropertyItem new_item = {i, "NO_CONTEXT", 0, "No context available", ""};
|
||||
RNA_enum_item_add(&item, &totitem, &new_item);
|
||||
}
|
||||
# endif
|
||||
|
||||
RNA_enum_item_end(&item, &totitem);
|
||||
*r_free = true;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
static const EnumPropertyItem *rna_UserDef_icons_itemf(bContext *UNUSED(C),
|
||||
PointerRNA *UNUSED(ptr),
|
||||
PropertyRNA *UNUSED(prop),
|
||||
bool *r_free)
|
||||
{
|
||||
int totitem = 0;
|
||||
const EnumPropertyItem *list = rna_enum_icon_items;
|
||||
EnumPropertyItem *item = NULL;
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; list[i].identifier; i++) {
|
||||
EnumPropertyItem new_item = {
|
||||
list[i].value, list[i].identifier, list[i].value, list[i].name, list[i].description};
|
||||
RNA_enum_item_add(&item, &totitem, &new_item);
|
||||
}
|
||||
|
||||
# ifndef NDEBUG
|
||||
if (i == 0) {
|
||||
EnumPropertyItem new_item = {i, "NONE", 0, "No icons", ""};
|
||||
RNA_enum_item_add(&item, &totitem, &new_item);
|
||||
}
|
||||
# endif
|
||||
|
||||
RNA_enum_item_end(&item, &totitem);
|
||||
*r_free = true;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
static void rna_UserDef_usermenus_item_add(UserDef *userdef, int index)
|
||||
{
|
||||
bUserMenu *um = rna_UserDef_usermenus_get_current(userdef, true);
|
||||
if (!um)
|
||||
return;
|
||||
|
||||
ListBase *lb = &um->items;
|
||||
bUserMenuItem_Op *umi = (bUserMenuItem_Op *)BKE_blender_user_menu_item_add(
|
||||
NULL, USER_MENU_TYPE_OPERATOR);
|
||||
|
||||
if (index < 0)
|
||||
BLI_addtail(lb, umi);
|
||||
else {
|
||||
int i = 0;
|
||||
for (bUserMenuItem *insert = lb->first; insert; insert = insert->next, i++) {
|
||||
if (i == index) {
|
||||
BLI_insertlinkbefore(lb, insert, umi);
|
||||
return;
|
||||
}
|
||||
}
|
||||
BLI_addtail(lb, umi);
|
||||
}
|
||||
|
||||
// basic operator setup
|
||||
wmOperatorType *ot = WM_operatortype_find("OBJECT_OT_add", true);
|
||||
STRNCPY(umi->item.ui_name, "new item");
|
||||
STRNCPY(umi->op_idname, ot->idname);
|
||||
userdef->runtime.um_item_select = &umi->item;
|
||||
}
|
||||
|
||||
static void rna_UserDef_usermenus_item_remove(UserDef *userdef)
|
||||
{
|
||||
bUserMenu *bum = rna_UserDef_usermenus_get_current(userdef, true);
|
||||
bUserMenuItem *umi = userdef->runtime.um_item_select;
|
||||
if (!bum || !umi)
|
||||
return;
|
||||
|
||||
userdef->runtime.um_item_select = umi->next;
|
||||
if (!userdef->runtime.um_item_select)
|
||||
userdef->runtime.um_item_select = umi->prev;
|
||||
ListBase *lb = (umi->parent) ? &umi->parent->items : &bum->items;
|
||||
ED_screen_user_menu_item_remove(lb, umi);
|
||||
}
|
||||
|
||||
static void rna_UserDef_usermenus_item_move(UserDef *userdef, bool up)
|
||||
{
|
||||
bUserMenu *bum = rna_UserDef_usermenus_get_current(userdef, true);
|
||||
bUserMenuItem *umi = userdef->runtime.um_item_select;
|
||||
if (!bum || !umi)
|
||||
return;
|
||||
|
||||
ListBase *lb = (umi->parent) ? &umi->parent->items : &bum->items;
|
||||
bUserMenuItem *umi_toward = (up) ? umi->prev : umi->next;
|
||||
BLI_remlink(lb, umi);
|
||||
|
||||
if (!umi_toward) {
|
||||
|
||||
if (!umi->parent)
|
||||
return;
|
||||
if (!umi->parent->item.parent) {
|
||||
if (up)
|
||||
BLI_insertlinkbefore(&bum->items, umi->parent, umi);
|
||||
else
|
||||
BLI_insertlinkafter(&bum->items, umi->parent, umi);
|
||||
umi->parent = NULL;
|
||||
}
|
||||
else {
|
||||
if (up)
|
||||
BLI_insertlinkbefore(&umi->parent->item.parent->items, umi->parent, umi);
|
||||
else
|
||||
BLI_insertlinkafter(&umi->parent->item.parent->items, umi->parent, umi);
|
||||
umi->parent = umi->parent->item.parent;
|
||||
}
|
||||
}
|
||||
else if (umi_toward->type == USER_MENU_TYPE_SUBMENU) {
|
||||
bUserMenuItem_SubMenu *umi_sm = (bUserMenuItem_SubMenu *)umi_toward;
|
||||
if (up)
|
||||
BLI_addtail(&umi_sm->items, umi);
|
||||
else
|
||||
BLI_addhead(&umi_sm->items, umi);
|
||||
umi->parent = umi_sm;
|
||||
}
|
||||
else {
|
||||
|
||||
if (up)
|
||||
BLI_insertlinkbefore(lb, umi->prev, umi);
|
||||
else
|
||||
BLI_insertlinkafter(lb, umi->next, umi);
|
||||
}
|
||||
}
|
||||
|
||||
static void rna_UserDef_usermenus_pie_item_add(UserDef *userdef, int index)
|
||||
{
|
||||
bUserMenu *um = rna_UserDef_usermenus_get_current(userdef, true);
|
||||
if (!um)
|
||||
return;
|
||||
|
||||
bUserMenuItem_Op *umi = NULL;
|
||||
ListBase *lb = &um->items;
|
||||
bUserMenuItem *insert = lb->first;
|
||||
int i = 0;
|
||||
for (insert = lb->first; insert; insert = insert->next, i++) {
|
||||
if (insert->type != USER_MENU_TYPE_SUBMENU)
|
||||
continue;
|
||||
bUserMenuItem_SubMenu *sm = (bUserMenuItem_SubMenu *)insert;
|
||||
if (i == index) {
|
||||
umi = (bUserMenuItem_Op *)BKE_blender_user_menu_item_add(&sm->items,
|
||||
USER_MENU_TYPE_OPERATOR);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!umi)
|
||||
return;
|
||||
|
||||
// basic operator setup
|
||||
wmOperatorType *ot = WM_operatortype_find("OBJECT_OT_add", true);
|
||||
STRNCPY(umi->item.ui_name, "new item");
|
||||
STRNCPY(umi->op_idname, ot->idname);
|
||||
umi->item.parent = (bUserMenuItem_SubMenu *)insert;
|
||||
userdef->runtime.um_item_select = &umi->item;
|
||||
}
|
||||
|
||||
static void rna_UserDef_usermenus_item_type_set(PointerRNA *ptr, int value)
|
||||
{
|
||||
bUserMenuItem *umi = (bUserMenuItem *)ptr->data;
|
||||
bUserMenu *bum = rna_UserDef_usermenus_get_current(&U, true);
|
||||
|
||||
if (!bum || !umi || umi->type == value)
|
||||
return;
|
||||
|
||||
ListBase *lb = (!umi->parent) ? &bum->items : &umi->parent->items;
|
||||
BLI_remlink(lb, umi);
|
||||
bUserMenuItem *new_umi = BKE_blender_user_menu_item_add(NULL, value);
|
||||
new_umi->parent = umi->parent;
|
||||
if (value == USER_MENU_TYPE_SUBMENU) {
|
||||
bUserMenuItem_SubMenu *umi_sm = (bUserMenuItem_SubMenu *)new_umi;
|
||||
BLI_listbase_clear(&umi_sm->items);
|
||||
}
|
||||
BLI_insertlinkafter(lb, umi->prev, new_umi);
|
||||
STRNCPY(new_umi->ui_name, umi->ui_name);
|
||||
BKE_blender_user_menu_item_free(umi);
|
||||
U.runtime.um_item_select = new_umi;
|
||||
}
|
||||
|
||||
static int rna_UserDef_usermenus_item_type_get(PointerRNA *ptr)
|
||||
{
|
||||
bUserMenuItem *data = (bUserMenuItem *)(ptr->data);
|
||||
if (data)
|
||||
return (int)(data->type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rna_UserDef_usermenus_item_name_get(PointerRNA *ptr, char *value)
|
||||
{
|
||||
bUserMenuItem *umi = (bUserMenuItem *)(ptr->data);
|
||||
|
||||
if (!*umi->ui_name) {
|
||||
const char *name = " ";
|
||||
if (umi->type == USER_MENU_TYPE_MENU) {
|
||||
bUserMenuItem_Menu *umi_mt = (bUserMenuItem_Menu *)umi;
|
||||
MenuType *mt = WM_menutype_find(umi_mt->mt_idname, true);
|
||||
name = (const char *)CTX_IFACE_(mt->translation_context, mt->label);
|
||||
BLI_strncpy(umi->ui_name, name, FILE_MAX);
|
||||
}
|
||||
}
|
||||
BLI_strncpy(value, umi->ui_name, FILE_MAX);
|
||||
}
|
||||
|
||||
static void rna_UserDef_usermenus_pie_set(PointerRNA *ptr, int value)
|
||||
{
|
||||
bUserMenusGroup *umg = (bUserMenusGroup *)ptr->data;
|
||||
|
||||
LISTBASE_FOREACH (bUserMenu *, um, &umg->menus) {
|
||||
ListBase *lb = &um->items;
|
||||
BKE_blender_user_menu_item_free_list(lb);
|
||||
if (value)
|
||||
for (int i = 0; i < 8; i++) {
|
||||
BKE_blender_user_menu_item_add(lb, USER_MENU_TYPE_SEP);
|
||||
}
|
||||
}
|
||||
umg->type = value;
|
||||
}
|
||||
|
||||
static PointerRNA rna_UserDef_usermenus_item_op_prop_get(PointerRNA *ptr)
|
||||
{
|
||||
bUserMenuItem_Op *umi_op = ptr->data;
|
||||
|
||||
if (umi_op->ptr) {
|
||||
return *(umi_op->ptr);
|
||||
}
|
||||
return PointerRNA_NULL;
|
||||
}
|
||||
|
||||
static void rna_UserDef_usermenus_group_idname_set(Main *bmain,
|
||||
Scene *UNUSED(scene),
|
||||
PointerRNA *ptr)
|
||||
{
|
||||
bUserMenusGroup *umg = (bUserMenusGroup *)ptr->data;
|
||||
# if 0
|
||||
const char *name = umg->name;
|
||||
# endif
|
||||
char old[64] = {'\0'};
|
||||
|
||||
STRNCPY(old, umg->idname);
|
||||
BKE_blender_user_menus_group_idname_update(umg);
|
||||
BKE_blender_user_menus_group_idname_update_keymap(bmain->wm.first, old, umg->idname);
|
||||
}
|
||||
|
||||
static void rna_UserDef_usermenus_item_op_get(PointerRNA *ptr, char *value)
|
||||
{
|
||||
bUserMenuItem_Op *umi_op = ptr->data;
|
||||
WM_operator_py_idname(value, umi_op->op_idname);
|
||||
}
|
||||
|
||||
static int rna_UserDef_usermenus_item_op_length(PointerRNA *ptr)
|
||||
{
|
||||
bUserMenuItem_Op *umi_op = ptr->data;
|
||||
char pyname[OP_MAX_TYPENAME];
|
||||
|
||||
WM_operator_py_idname(pyname, umi_op->op_idname);
|
||||
return strlen(pyname);
|
||||
}
|
||||
|
||||
static void rna_UserDef_usermenus_item_op_set(PointerRNA *ptr, const char *value)
|
||||
{
|
||||
bUserMenuItem_Op *umi_op = (bUserMenuItem_Op *)ptr->data;
|
||||
char idname_bl[OP_MAX_TYPENAME];
|
||||
|
||||
wmOperatorType *origin_ot = WM_operatortype_find(umi_op->op_idname, true);
|
||||
wmOperatorType *ot = WM_operatortype_find(value, false);
|
||||
if (origin_ot == ot || ot == NULL)
|
||||
return;
|
||||
|
||||
WM_operator_bl_idname(idname_bl, value);
|
||||
WM_operator_py_idname(umi_op->op_idname, value);
|
||||
|
||||
if (LIKELY(umi_op->ptr)) {
|
||||
WM_operator_properties_free(umi_op->ptr);
|
||||
MEM_freeN(umi_op->ptr);
|
||||
|
||||
umi_op->ptr = NULL;
|
||||
}
|
||||
umi_op->prop = NULL;
|
||||
|
||||
WM_operator_properties_alloc(&(umi_op->ptr), &(umi_op->prop), idname_bl);
|
||||
WM_operator_properties_sanitize(umi_op->ptr, 1);
|
||||
}
|
||||
|
||||
static void rna_UserDef_usermenu_draw(UserDef *UNUSED(userdef),
|
||||
bContext *C,
|
||||
uiLayout *layout,
|
||||
bUserMenusGroup *umg)
|
||||
{
|
||||
screen_user_menu_draw_begin(C, layout, true, umg);
|
||||
}
|
||||
|
||||
/* UserMenu.menu_items */
|
||||
|
||||
static void rna_UserDef_usermenu_items_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
|
||||
{
|
||||
bUserMenu *bum = (bUserMenu *)ptr->data;
|
||||
rna_iterator_listbase_begin(iter, &bum->items, NULL);
|
||||
}
|
||||
|
||||
static void rna_UserDef_usermenu_submenu_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
|
||||
{
|
||||
bUserMenuItem_SubMenu *umi_sm = (bUserMenuItem_SubMenu *)ptr->data;
|
||||
rna_iterator_listbase_begin(iter, &umi_sm->items, NULL);
|
||||
}
|
||||
|
||||
static bUserMenuItem_Op *rna_UserDef_usermenus_item_operator_get(bUserMenuItem *umi)
|
||||
{
|
||||
if (umi->type == USER_MENU_TYPE_OPERATOR)
|
||||
return (bUserMenuItem_Op *)umi;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bUserMenuItem_Menu *rna_UserDef_usermenus_item_menu_get(bUserMenuItem *umi)
|
||||
{
|
||||
if (umi->type == USER_MENU_TYPE_MENU)
|
||||
return (bUserMenuItem_Menu *)umi;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bUserMenuItem_Prop *rna_UserDef_usermenus_item_property_get(bUserMenuItem *umi)
|
||||
{
|
||||
if (umi->type == USER_MENU_TYPE_PROP)
|
||||
return (bUserMenuItem_Prop *)umi;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bUserMenuItem_SubMenu *rna_UserDef_usermenus_item_submenu_get(bUserMenuItem *umi)
|
||||
{
|
||||
if (umi->type == USER_MENU_TYPE_SUBMENU)
|
||||
return (bUserMenuItem_SubMenu *)umi;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* UserMenus.menus */
|
||||
|
||||
static void rna_UserDef_usermenu_menus_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
|
||||
{
|
||||
UserDef *userdef = (UserDef *)ptr->data;
|
||||
rna_iterator_listbase_begin(iter, &userdef->user_menus_group, NULL);
|
||||
}
|
||||
|
||||
int rna_show_statusbar_vram_editable(struct PointerRNA *UNUSED(ptr), const char **UNUSED(r_info))
|
||||
{
|
||||
return GPU_mem_stats_supported() ? PROP_EDITABLE : 0;
|
||||
@@ -5910,6 +6462,384 @@ static void rna_def_userdef_keymap(BlenderRNA *brna)
|
||||
RNA_def_property_ui_text(prop, "Key Config", "The name of the active key configuration");
|
||||
}
|
||||
|
||||
static void rna_def_userdef_usermenus_items_subtypes(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
|
||||
/* operator item */
|
||||
srna = RNA_def_struct(brna, "um_item_op", NULL);
|
||||
RNA_def_struct_sdna(srna, "bUserMenuItem_Op");
|
||||
RNA_def_struct_ui_text(
|
||||
srna, "User Menu operator item", "an item of the user menus that can store an operator");
|
||||
|
||||
prop = RNA_def_property(srna, "item", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_pointer_sdna(prop, NULL, "item");
|
||||
RNA_def_property_struct_type(prop, "UserMenuItem");
|
||||
RNA_def_property_ui_text(prop, "item", "");
|
||||
|
||||
prop = RNA_def_property(srna, "operator", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_string_sdna(prop, NULL, "op_idname");
|
||||
RNA_def_property_ui_text(prop, "operator", "the operator that will be executed");
|
||||
RNA_def_property_string_funcs(prop,
|
||||
"rna_UserDef_usermenus_item_op_get",
|
||||
"rna_UserDef_usermenus_item_op_length",
|
||||
"rna_UserDef_usermenus_item_op_set");
|
||||
RNA_def_struct_name_property(srna, prop);
|
||||
|
||||
prop = RNA_def_property(srna, "prop", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_pointer_sdna(prop, NULL, "prop");
|
||||
RNA_def_property_struct_type(prop, "OperatorProperties");
|
||||
RNA_def_property_ui_text(prop, "properties", "properties of the operator");
|
||||
RNA_def_property_pointer_funcs(prop, "rna_UserDef_usermenus_item_op_prop_get", NULL, NULL, NULL);
|
||||
|
||||
/* menu item */
|
||||
srna = RNA_def_struct(brna, "um_item_menu", NULL);
|
||||
RNA_def_struct_sdna(srna, "bUserMenuItem_Menu");
|
||||
RNA_def_struct_ui_text(
|
||||
srna, "User Menu menu item", "an item of the user menus that can store a submenu");
|
||||
|
||||
prop = RNA_def_property(srna, "item", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_pointer_sdna(prop, NULL, "item");
|
||||
RNA_def_property_struct_type(prop, "UserMenuItem");
|
||||
RNA_def_property_ui_text(prop, "item", "");
|
||||
|
||||
prop = RNA_def_property(srna, "id_name", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_string_sdna(prop, NULL, "mt_idname");
|
||||
RNA_def_property_ui_text(prop, "id name", "the menu id name");
|
||||
RNA_def_struct_name_property(srna, prop);
|
||||
|
||||
/* prop item */
|
||||
srna = RNA_def_struct(brna, "um_item_prop", NULL);
|
||||
RNA_def_struct_sdna(srna, "bUserMenuItem_Prop");
|
||||
RNA_def_struct_ui_text(
|
||||
srna, "User Menu property item", "an item of the user menus that can store a property");
|
||||
|
||||
prop = RNA_def_property(srna, "item", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_pointer_sdna(prop, NULL, "item");
|
||||
RNA_def_property_struct_type(prop, "UserMenuItem");
|
||||
RNA_def_property_ui_text(prop, "item", "");
|
||||
|
||||
prop = RNA_def_property(srna, "id_name", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_string_sdna(prop, NULL, "prop_id");
|
||||
RNA_def_property_ui_text(prop, "id name", "the property id name");
|
||||
RNA_def_struct_name_property(srna, prop);
|
||||
|
||||
prop = RNA_def_property(srna, "context", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_string_sdna(prop, NULL, "context_data_path");
|
||||
RNA_def_property_ui_text(prop, "context", "the context data path of the property");
|
||||
RNA_def_struct_name_property(srna, prop);
|
||||
|
||||
/* sub menu item */
|
||||
srna = RNA_def_struct(brna, "um_item_submenu", NULL);
|
||||
RNA_def_struct_sdna(srna, "bUserMenuItem_SubMenu");
|
||||
RNA_def_struct_ui_text(
|
||||
srna, "User Menu sub menu item", "an item of the user menus that can store a sub menu");
|
||||
|
||||
prop = RNA_def_property(srna, "item", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_pointer_sdna(prop, NULL, "item");
|
||||
RNA_def_property_struct_type(prop, "UserMenuItem");
|
||||
RNA_def_property_ui_text(prop, "item", "");
|
||||
|
||||
prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_string_sdna(prop, NULL, "name");
|
||||
RNA_def_property_ui_text(prop, "name", "the sub menu name");
|
||||
RNA_def_struct_name_property(srna, prop);
|
||||
|
||||
prop = RNA_def_property(srna, "items_list", PROP_COLLECTION, PROP_NONE);
|
||||
RNA_def_property_struct_type(prop, "UserMenuItem");
|
||||
RNA_def_property_ui_text(prop, "sub menu items", "list of the items of the sub menu");
|
||||
RNA_def_property_collection_funcs(prop,
|
||||
"rna_UserDef_usermenu_submenu_begin",
|
||||
"rna_iterator_listbase_next",
|
||||
NULL,
|
||||
"rna_iterator_listbase_get",
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void rna_def_userdef_usermenu(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
FunctionRNA *func;
|
||||
PropertyRNA *parm;
|
||||
|
||||
/* user menu */
|
||||
srna = RNA_def_struct(brna, "UserMenu", NULL);
|
||||
RNA_def_struct_sdna(srna, "bUserMenu");
|
||||
RNA_def_struct_ui_text(srna, "User Menu", "an user menu");
|
||||
|
||||
prop = RNA_def_property(srna, "spacetype", PROP_INT, PROP_NONE);
|
||||
RNA_def_property_int_sdna(prop, NULL, "space_type");
|
||||
RNA_def_property_ui_text(prop, "Space Type", "The Space type the user menu will be used in");
|
||||
|
||||
prop = RNA_def_property(srna, "context", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_string_sdna(prop, NULL, "context");
|
||||
RNA_def_property_ui_text(prop, "Context", "The Context the user menu will be used in");
|
||||
RNA_def_struct_name_property(srna, prop);
|
||||
|
||||
prop = RNA_def_property(srna, "menu_items", PROP_COLLECTION, PROP_NONE);
|
||||
RNA_def_property_struct_type(prop, "UserMenuItem");
|
||||
RNA_def_property_ui_text(prop, "Items", "list of the items of the menu");
|
||||
RNA_def_property_collection_funcs(prop,
|
||||
"rna_UserDef_usermenu_items_begin",
|
||||
"rna_iterator_listbase_next",
|
||||
NULL,
|
||||
"rna_iterator_listbase_get",
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
/* user menu item */
|
||||
static const EnumPropertyItem um_item_type[] = {
|
||||
{USER_MENU_TYPE_OPERATOR, "OPERATOR", 0, "Operator", "Operator"},
|
||||
{USER_MENU_TYPE_PROP, "PROPERTY", 0, "Property", "Property"},
|
||||
{USER_MENU_TYPE_SUBMENU, "SUBMENU", 0, "Menu", "Menu"},
|
||||
{USER_MENU_TYPE_MENU, "MENU", 0, "Packed Menu", "Packed Menu"},
|
||||
{USER_MENU_TYPE_SEP, "SEPARATOR", 0, "Separator", "Separator"},
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
srna = RNA_def_struct(brna, "UserMenuItem", NULL);
|
||||
RNA_def_struct_sdna(srna, "bUserMenuItem");
|
||||
RNA_def_struct_ui_text(srna, "User Menu Item", "User Menu item");
|
||||
|
||||
prop = RNA_def_property(srna, "next", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_pointer_sdna(prop, NULL, "next");
|
||||
RNA_def_property_struct_type(prop, "UserMenuItem");
|
||||
RNA_def_property_ui_text(prop, "item", "");
|
||||
|
||||
prop = RNA_def_property(srna, "prev", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_pointer_sdna(prop, NULL, "prev");
|
||||
RNA_def_property_struct_type(prop, "UserMenuItem");
|
||||
RNA_def_property_ui_text(prop, "item", "");
|
||||
|
||||
prop = RNA_def_property(srna, "parent", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_pointer_sdna(prop, NULL, "parent");
|
||||
RNA_def_property_struct_type(prop, "um_item_submenu");
|
||||
RNA_def_property_ui_text(prop, "item", "");
|
||||
|
||||
prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_string_sdna(prop, NULL, "ui_name");
|
||||
RNA_def_property_string_funcs(prop, "rna_UserDef_usermenus_item_name_get", NULL, NULL);
|
||||
RNA_def_property_ui_text(prop, "Name", "Name of the item");
|
||||
RNA_def_struct_name_property(srna, prop);
|
||||
|
||||
prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, NULL, "type");
|
||||
RNA_def_property_enum_items(prop, um_item_type);
|
||||
RNA_def_property_enum_funcs(
|
||||
prop, "rna_UserDef_usermenus_item_type_get", "rna_UserDef_usermenus_item_type_set", NULL);
|
||||
RNA_def_property_ui_text(prop, "type", "the type of item");
|
||||
|
||||
prop = RNA_def_property(srna, "icon", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, NULL, "icon");
|
||||
RNA_def_property_enum_items(prop, rna_enum_icon_items);
|
||||
RNA_def_property_enum_default(prop, ICON_NONE);
|
||||
RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_UserDef_icons_itemf");
|
||||
RNA_def_property_ui_text(prop, "Icon", "The item icon");
|
||||
|
||||
prop = RNA_def_property(srna, "is_selected", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "is_selected", 0);
|
||||
RNA_def_property_ui_text(prop, "is selected", "is selected");
|
||||
RNA_def_property_boolean_funcs(prop, NULL, "rna_UserDef_usermenus_active_item_set");
|
||||
|
||||
func = RNA_def_function(srna, "get_operator", "rna_UserDef_usermenus_item_operator_get");
|
||||
RNA_def_function_ui_description(func, "return the operator item");
|
||||
parm = RNA_def_pointer(func, "item_op", "um_item_op", "", "the item operator");
|
||||
RNA_def_function_return(func, parm);
|
||||
|
||||
func = RNA_def_function(srna, "get_menu", "rna_UserDef_usermenus_item_menu_get");
|
||||
RNA_def_function_ui_description(func, "return the operator menu");
|
||||
parm = RNA_def_pointer(func, "item_menu", "um_item_menu", "", "the item menu");
|
||||
RNA_def_function_return(func, parm);
|
||||
|
||||
func = RNA_def_function(srna, "get_property", "rna_UserDef_usermenus_item_property_get");
|
||||
RNA_def_function_ui_description(func, "return the operator property");
|
||||
parm = RNA_def_pointer(func, "item_prop", "um_item_prop", "", "the item property");
|
||||
RNA_def_function_return(func, parm);
|
||||
|
||||
func = RNA_def_function(srna, "get_submenu", "rna_UserDef_usermenus_item_submenu_get");
|
||||
RNA_def_function_ui_description(func, "return the submenu");
|
||||
parm = RNA_def_pointer(func, "item_submenu", "um_item_submenu", "", "the item submenu");
|
||||
RNA_def_function_return(func, parm);
|
||||
|
||||
rna_def_userdef_usermenus_items_subtypes(brna);
|
||||
}
|
||||
|
||||
static void rna_def_userdef_usermenusgroup(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
|
||||
static const EnumPropertyItem um_menu_type[] = {
|
||||
{0, "LIST", 0, "List", "List"},
|
||||
{1, "PIE", 0, "Pie", "Pie"},
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
/* user menus group */
|
||||
srna = RNA_def_struct(brna, "UserMenusGroup", NULL);
|
||||
RNA_def_struct_sdna(srna, "bUserMenusGroup");
|
||||
RNA_def_struct_ui_text(srna, "User Menus Group", "A whole displayble user menu");
|
||||
|
||||
prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_string_sdna(prop, NULL, "name");
|
||||
RNA_def_property_ui_text(prop, "Name", "Name of the user menu group");
|
||||
RNA_def_struct_name_property(srna, prop);
|
||||
RNA_def_property_update(prop, 0, "rna_UserDef_usermenus_group_idname_set");
|
||||
|
||||
prop = RNA_def_property(srna, "idname", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_string_sdna(prop, NULL, "idname");
|
||||
RNA_def_property_ui_text(prop, "ID Name", "ID Name of the user menu group");
|
||||
RNA_def_struct_name_property(srna, prop);
|
||||
|
||||
prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, NULL, "type");
|
||||
RNA_def_property_enum_items(prop, um_menu_type);
|
||||
RNA_def_property_ui_text(prop, "menu type", "change menu type between list and pie");
|
||||
RNA_def_property_enum_funcs(prop, NULL, "rna_UserDef_usermenus_pie_set", NULL);
|
||||
|
||||
prop = RNA_def_property(srna, "menus", PROP_COLLECTION, PROP_NONE);
|
||||
RNA_def_property_struct_type(prop, "UserMenu");
|
||||
RNA_def_property_ui_text(prop, "User Menu", "list of user sub menus contained in the group");
|
||||
}
|
||||
|
||||
static void rna_def_userdef_usermenus_editor(BlenderRNA *brna)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
FunctionRNA *func;
|
||||
PropertyRNA *parm;
|
||||
|
||||
static const EnumPropertyItem um_space_default[] = {
|
||||
{0, "NULL", 0, "None", "No spacetypes found"},
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
static const EnumPropertyItem um_context_default[] = {
|
||||
{0, "NULL", 0, "None", "No context found"},
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
StructRNA *srna = RNA_def_struct(brna, "PreferencesUserMenus", NULL);
|
||||
RNA_def_struct_sdna(srna, "UserDef");
|
||||
RNA_def_struct_nested(brna, srna, "Preferences");
|
||||
RNA_def_struct_clear_flag(srna, STRUCT_UNDO);
|
||||
RNA_def_struct_ui_text(srna, "User Menus", "User Menus editor");
|
||||
|
||||
prop = RNA_def_property(srna, "space_selected", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, NULL, "runtime.um_space_select");
|
||||
RNA_def_property_enum_items(prop, um_space_default);
|
||||
RNA_def_property_enum_funcs(prop,
|
||||
"rna_UserDef_usermenus_spacetypes_get",
|
||||
NULL,
|
||||
"rna_UserDef_usermenus_spacetypes_itemf");
|
||||
RNA_def_property_ui_text(prop, "space type selected", "the space type selected");
|
||||
RNA_def_property_update(prop, 0, "rna_userdef_update");
|
||||
|
||||
prop = RNA_def_property(srna, "context_selected", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, NULL, "runtime.um_context_select");
|
||||
RNA_def_property_enum_items(prop, um_context_default);
|
||||
RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_UserDef_usermenus_contexts_itemf");
|
||||
RNA_def_property_ui_text(prop, "context selected", "the context selected");
|
||||
RNA_def_property_update(prop, 0, "rna_userdef_update");
|
||||
|
||||
prop = RNA_def_property(srna, "active_item", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_pointer_sdna(prop, NULL, "runtime.um_item_select");
|
||||
RNA_def_property_struct_type(prop, "UserMenuItem");
|
||||
RNA_def_property_ui_text(prop, "item", "");
|
||||
|
||||
prop = RNA_def_property(srna, "active_group", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_flag(prop, PROP_NEVER_NULL);
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_pointer_sdna(prop, NULL, "runtime.umg_select");
|
||||
RNA_def_property_struct_type(prop, "UserMenusGroup");
|
||||
RNA_def_property_ui_text(prop, "active user menus group", "active user menus group");
|
||||
|
||||
prop = RNA_def_property(srna, "menus", PROP_COLLECTION, PROP_NONE);
|
||||
RNA_def_property_struct_type(prop, "UserMenusGroup");
|
||||
RNA_def_property_ui_text(prop, "Menus", "list of the menus");
|
||||
RNA_def_property_collection_funcs(prop,
|
||||
"rna_UserDef_usermenu_menus_begin",
|
||||
"rna_iterator_listbase_next",
|
||||
"rna_iterator_listbase_end",
|
||||
"rna_iterator_listbase_get",
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
prop = RNA_def_property(srna, "expanded", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "runtime.um_expanded", 0);
|
||||
RNA_def_property_ui_text(prop, "Items Expanded", "Expanded in the user interface");
|
||||
RNA_def_property_ui_icon(prop, ICON_DISCLOSURE_TRI_RIGHT, 1);
|
||||
|
||||
rna_def_userdef_usermenu(brna);
|
||||
rna_def_userdef_usermenusgroup(brna);
|
||||
|
||||
// functions
|
||||
func = RNA_def_function(srna, "get_current_menu", "rna_UserDef_usermenus_get_current");
|
||||
RNA_def_function_ui_description(func, "get active user menu");
|
||||
parm = RNA_def_boolean(func, "ensure", false, "ensure", "create the menu if don't exist");
|
||||
parm = RNA_def_pointer(func, "current_menu", "UserMenu", "", "the menu");
|
||||
RNA_def_function_return(func, parm);
|
||||
|
||||
func = RNA_def_function(srna, "get_group", "rna_UserDef_usermenus_get_group");
|
||||
RNA_def_function_ui_description(func, "get user menus group by idname");
|
||||
parm = RNA_def_string(func, "idname", NULL, 0, "ID name", "ID name of the group");
|
||||
parm = RNA_def_pointer(func, "menu", "UserMenusGroup", "", "the menu group");
|
||||
RNA_def_function_return(func, parm);
|
||||
|
||||
func = RNA_def_function(srna, "set_group", "rna_UserDef_usermenus_set_group");
|
||||
RNA_def_function_ui_description(func, "set the group menu to edit");
|
||||
parm = RNA_def_pointer(func, "new_group", "UserMenusGroup", "", "the group menu");
|
||||
|
||||
func = RNA_def_function(srna, "add_group", "rna_UserDef_usermenus_add_group");
|
||||
RNA_def_function_ui_description(func, "add a group");
|
||||
|
||||
func = RNA_def_function(srna, "remove_group", "rna_UserDef_usermenus_remove_group");
|
||||
RNA_def_function_ui_description(func, "remove a group");
|
||||
|
||||
func = RNA_def_function(srna, "items_len", "rna_UserDef_usermenus_items_length");
|
||||
RNA_def_function_ui_description(func, "Refresh custom menu editor");
|
||||
parm = RNA_def_int(func, "len", 0, 0, 1000, "", "the list len", 0, 1000);
|
||||
RNA_def_function_return(func, parm);
|
||||
|
||||
func = RNA_def_function(srna, "has_item", "rna_UserDef_usermenus_has_item");
|
||||
RNA_def_function_ui_description(func, "the current list has items");
|
||||
parm = RNA_def_boolean(func, "has_item", false, "", "is there items in the current list ?");
|
||||
RNA_def_function_return(func, parm);
|
||||
|
||||
func = RNA_def_function(srna, "item_add", "rna_UserDef_usermenus_item_add");
|
||||
RNA_def_function_ui_description(func, "add an item to a menu");
|
||||
parm = RNA_def_int(func, "index", 0, -1, 1000, "index", "index to insert the item", -1, 1000);
|
||||
|
||||
func = RNA_def_function(srna, "item_remove", "rna_UserDef_usermenus_item_remove");
|
||||
RNA_def_function_ui_description(func, "remove an item from a user menu");
|
||||
|
||||
func = RNA_def_function(srna, "item_move", "rna_UserDef_usermenus_item_move");
|
||||
RNA_def_function_ui_description(func, "up an item");
|
||||
parm = RNA_def_boolean(func, "up", false, "", "go up ?");
|
||||
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
|
||||
|
||||
func = RNA_def_function(srna, "pie_item_add", "rna_UserDef_usermenus_pie_item_add");
|
||||
RNA_def_function_ui_description(func, "add an item to a menu");
|
||||
parm = RNA_def_int(func, "index", 0, -1, 1000, "index", "index to insert the item", -1, 1000);
|
||||
|
||||
func = RNA_def_function(srna, "draw_menu", "rna_UserDef_usermenu_draw");
|
||||
RNA_def_function_ui_description(func, "draw items of usermenu");
|
||||
parm = RNA_def_pointer(func, "context", "Context", "", "context");
|
||||
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
|
||||
parm = RNA_def_pointer(func, "layout", "UILayout", "", "layout");
|
||||
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
|
||||
parm = RNA_def_pointer(func, "menu", "UserMenusGroup", "", "menu");
|
||||
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
|
||||
}
|
||||
|
||||
static void rna_def_userdef_filepaths(BlenderRNA *brna)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
@@ -6248,6 +7178,12 @@ void RNA_def_userdef(BlenderRNA *brna)
|
||||
RNA_def_property_pointer_funcs(prop, "rna_UserDef_keymap_get", NULL, NULL, NULL);
|
||||
RNA_def_property_ui_text(prop, "Keymap", "Shortcut setup for keyboards and other input devices");
|
||||
|
||||
prop = RNA_def_property(srna, "user_menus", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_flag(prop, PROP_NEVER_NULL);
|
||||
RNA_def_property_struct_type(prop, "PreferencesUserMenus");
|
||||
RNA_def_property_pointer_funcs(prop, "rna_UserDef_user_menus_get", NULL, NULL, NULL);
|
||||
RNA_def_property_ui_text(prop, "User Menus", "User Menus Editor");
|
||||
|
||||
prop = RNA_def_property(srna, "filepaths", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_flag(prop, PROP_NEVER_NULL);
|
||||
RNA_def_property_struct_type(prop, "PreferencesFilePaths");
|
||||
@@ -6315,6 +7251,7 @@ void RNA_def_userdef(BlenderRNA *brna)
|
||||
rna_def_userdef_edit(brna);
|
||||
rna_def_userdef_input(brna);
|
||||
rna_def_userdef_keymap(brna);
|
||||
rna_def_userdef_usermenus_editor(brna);
|
||||
rna_def_userdef_filepaths(brna);
|
||||
rna_def_userdef_system(brna);
|
||||
rna_def_userdef_addon(brna);
|
||||
|
@@ -1841,6 +1841,8 @@ static void rna_struct_update_when_changed(bContext *C,
|
||||
PointerRNA *ptr_b)
|
||||
{
|
||||
CollectionPropertyIterator iter;
|
||||
if (!ptr_a->data || !ptr_b->data)
|
||||
return;
|
||||
PropertyRNA *iterprop = RNA_struct_iterator_property(ptr_a->type);
|
||||
BLI_assert(ptr_a->type == ptr_b->type);
|
||||
RNA_property_collection_begin(ptr_a, iterprop, &iter);
|
||||
|
Submodule source/tools updated: 896c5f7895...6034199b44
Reference in New Issue
Block a user