Merge branch 'master' into blender2.8
This commit is contained in:
commit
f730e386eb
200
release/scripts/modules/bl_app_override/__init__.py
Normal file
200
release/scripts/modules/bl_app_override/__init__.py
Normal file
@ -0,0 +1,200 @@
|
||||
# ##### 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-80 compliant>
|
||||
|
||||
"""
|
||||
Module to manage overriding various parts of Blender.
|
||||
|
||||
Intended for use with 'app_templates', though it can be used from anywhere.
|
||||
"""
|
||||
|
||||
|
||||
# TODO, how to check these aren't from add-ons.
|
||||
# templates might need to un-register while filtering.
|
||||
def class_filter(cls_parent, **kw):
|
||||
whitelist = kw.pop("whitelist", None)
|
||||
blacklist = kw.pop("blacklist", None)
|
||||
kw_items = tuple(kw.items())
|
||||
for cls in cls_parent.__subclasses__():
|
||||
# same as is_registered()
|
||||
if "bl_rna" in cls.__dict__:
|
||||
if blacklist is not None and cls.__name__ in blacklist:
|
||||
continue
|
||||
if ((whitelist is not None and cls.__name__ is whitelist) or
|
||||
all((getattr(cls, attr) in expect) for attr, expect in kw_items)):
|
||||
yield cls
|
||||
|
||||
|
||||
def ui_draw_filter_register(
|
||||
*,
|
||||
ui_ignore_classes=None,
|
||||
ui_ignore_operator=None,
|
||||
ui_ignore_property=None,
|
||||
ui_ignore_menu=None,
|
||||
ui_ignore_label=None,
|
||||
):
|
||||
import bpy
|
||||
|
||||
UILayout = bpy.types.UILayout
|
||||
|
||||
if ui_ignore_classes is None:
|
||||
ui_ignore_classes = (
|
||||
bpy.types.Panel,
|
||||
bpy.types.Menu,
|
||||
bpy.types.Header,
|
||||
)
|
||||
|
||||
class OperatorProperties_Fake:
|
||||
pass
|
||||
|
||||
class UILayout_Fake(bpy.types.UILayout):
|
||||
__slots__ = ()
|
||||
|
||||
def __getattribute__(self, attr):
|
||||
# ensure we always pass down UILayout_Fake instances
|
||||
if attr in {"row", "split", "column", "box", "column_flow"}:
|
||||
real_func = UILayout.__getattribute__(self, attr)
|
||||
|
||||
def dummy_func(*args, **kw):
|
||||
# print("wrapped", attr)
|
||||
ret = real_func(*args, **kw)
|
||||
return UILayout_Fake(ret)
|
||||
return dummy_func
|
||||
|
||||
elif attr in {"operator", "operator_menu_enum", "operator_enum"}:
|
||||
if ui_ignore_operator is None:
|
||||
return UILayout.__getattribute__(self, attr)
|
||||
|
||||
real_func = UILayout.__getattribute__(self, attr)
|
||||
|
||||
def dummy_func(*args, **kw):
|
||||
# print("wrapped", attr)
|
||||
if not ui_ignore_operator(args[0]):
|
||||
ret = real_func(*args, **kw)
|
||||
else:
|
||||
# UILayout.__getattribute__(self, "label")()
|
||||
# may need to be set
|
||||
ret = OperatorProperties_Fake()
|
||||
return ret
|
||||
return dummy_func
|
||||
|
||||
elif attr in {"prop", "prop_enum"}:
|
||||
if ui_ignore_property is None:
|
||||
return UILayout.__getattribute__(self, attr)
|
||||
|
||||
real_func = UILayout.__getattribute__(self, attr)
|
||||
|
||||
def dummy_func(*args, **kw):
|
||||
# print("wrapped", attr)
|
||||
if not ui_ignore_property(args[0].__class__.__name__, args[1]):
|
||||
ret = real_func(*args, **kw)
|
||||
else:
|
||||
ret = None
|
||||
return ret
|
||||
return dummy_func
|
||||
|
||||
elif attr == "menu":
|
||||
if ui_ignore_menu is None:
|
||||
return UILayout.__getattribute__(self, attr)
|
||||
|
||||
real_func = UILayout.__getattribute__(self, attr)
|
||||
|
||||
def dummy_func(*args, **kw):
|
||||
# print("wrapped", attr)
|
||||
if not ui_ignore_menu(args[0]):
|
||||
ret = real_func(*args, **kw)
|
||||
else:
|
||||
ret = None
|
||||
return ret
|
||||
return dummy_func
|
||||
|
||||
elif attr == "label":
|
||||
if ui_ignore_label is None:
|
||||
return UILayout.__getattribute__(self, attr)
|
||||
|
||||
real_func = UILayout.__getattribute__(self, attr)
|
||||
|
||||
def dummy_func(*args, **kw):
|
||||
# print("wrapped", attr)
|
||||
if not ui_ignore_label(args[0] if args else kw.get("text", "")):
|
||||
ret = real_func(*args, **kw)
|
||||
else:
|
||||
# ret = real_func()
|
||||
ret = None
|
||||
return ret
|
||||
return dummy_func
|
||||
else:
|
||||
return UILayout.__getattribute__(self, attr)
|
||||
# print(self, attr)
|
||||
|
||||
def operator(*args, **kw):
|
||||
return super().operator(*args, **kw)
|
||||
|
||||
def draw_override(func_orig, self_real, context):
|
||||
# simple, no wrapping
|
||||
# return func_orig(self_wrap, context)
|
||||
|
||||
class Wrapper(self_real.__class__):
|
||||
__slots__ = ()
|
||||
def __getattribute__(self, attr):
|
||||
if attr == "layout":
|
||||
return UILayout_Fake(self_real.layout)
|
||||
else:
|
||||
cls = super()
|
||||
try:
|
||||
return cls.__getattr__(self, attr)
|
||||
except AttributeError:
|
||||
# class variable
|
||||
try:
|
||||
return getattr(cls, attr)
|
||||
except AttributeError:
|
||||
# for preset bl_idname access
|
||||
return getattr(UILayout(self), attr)
|
||||
|
||||
@property
|
||||
def layout(self):
|
||||
# print("wrapped")
|
||||
return self_real.layout
|
||||
|
||||
return func_orig(Wrapper(self_real), context)
|
||||
|
||||
ui_ignore_store = []
|
||||
|
||||
for cls in ui_ignore_classes:
|
||||
for subcls in list(cls.__subclasses__()):
|
||||
if "draw" in subcls.__dict__: # don't want to get parents draw()
|
||||
|
||||
def replace_draw():
|
||||
# function also serves to hold draw_old in a local name-space
|
||||
draw_orig = subcls.draw
|
||||
|
||||
def draw(self, context):
|
||||
return draw_override(draw_orig, self, context)
|
||||
subcls.draw = draw
|
||||
|
||||
ui_ignore_store.append((subcls, "draw", subcls.draw))
|
||||
|
||||
replace_draw()
|
||||
|
||||
return ui_ignore_store
|
||||
|
||||
|
||||
def ui_draw_filter_unregister(ui_ignore_store):
|
||||
for (obj, attr, value) in ui_ignore_store:
|
||||
setattr(obj, attr, value)
|
167
release/scripts/modules/bl_app_override/helpers.py
Normal file
167
release/scripts/modules/bl_app_override/helpers.py
Normal file
@ -0,0 +1,167 @@
|
||||
# ##### 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-80 compliant>
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# AppOverrideState
|
||||
|
||||
|
||||
class AppOverrideState:
|
||||
"""
|
||||
Utility class to encapsulate overriding the application state
|
||||
so that settings can be restored afterwards.
|
||||
"""
|
||||
__slots__ = (
|
||||
# setup_classes
|
||||
"_class_store",
|
||||
# setup_ui_ignore
|
||||
"_ui_ignore_store",
|
||||
# setup_addons
|
||||
"_addon_store",
|
||||
)
|
||||
|
||||
# ---------
|
||||
# Callbacks
|
||||
#
|
||||
# Set as None, to make it simple to check if they're being overridden.
|
||||
|
||||
# setup/teardown classes
|
||||
class_ignore = None
|
||||
|
||||
# setup/teardown ui_ignore
|
||||
ui_ignore_classes = None
|
||||
ui_ignore_operator = None
|
||||
ui_ignore_property = None
|
||||
ui_ignore_menu = None
|
||||
ui_ignore_label = None
|
||||
|
||||
addon_paths = None
|
||||
addons = None
|
||||
|
||||
# End callbacks
|
||||
|
||||
def __init__(self):
|
||||
self._class_store = None
|
||||
self._addon_store = None
|
||||
self._ui_ignore_store = None
|
||||
|
||||
def _setup_classes(self):
|
||||
import bpy
|
||||
assert(self._class_store is None)
|
||||
self._class_store = self.class_ignore()
|
||||
from bpy.utils import unregister_class
|
||||
for cls in self._class_store:
|
||||
unregister_class(cls)
|
||||
|
||||
def _teardown_classes(self):
|
||||
assert(self._class_store is not None)
|
||||
|
||||
from bpy.utils import register_class
|
||||
for cls in self._class_store:
|
||||
register_class(cls)
|
||||
self._class_store = None
|
||||
|
||||
def _setup_ui_ignore(self):
|
||||
import bl_app_override
|
||||
|
||||
self._ui_ignore_store = bl_app_override.ui_draw_filter_register(
|
||||
ui_ignore_classes=(
|
||||
None if self.ui_ignore_classes is None
|
||||
else self.ui_ignore_classes()
|
||||
),
|
||||
ui_ignore_operator=self.ui_ignore_operator,
|
||||
ui_ignore_property=self.ui_ignore_property,
|
||||
ui_ignore_menu=self.ui_ignore_menu,
|
||||
ui_ignore_label=self.ui_ignore_label,
|
||||
)
|
||||
|
||||
def _teardown_ui_ignore(self):
|
||||
import bl_app_override
|
||||
bl_app_override.ui_draw_filter_unregister(
|
||||
self._ui_ignore_store
|
||||
)
|
||||
self._ui_ignore_store = None
|
||||
|
||||
def _setup_addons(self):
|
||||
import sys
|
||||
import os
|
||||
|
||||
sys_path = []
|
||||
if self.addon_paths is not None:
|
||||
for path in self.addon_paths():
|
||||
if path not in sys.path:
|
||||
sys.path.append(path)
|
||||
|
||||
import addon_utils
|
||||
addons = []
|
||||
if self.addons is not None:
|
||||
addons.extend(self.addons())
|
||||
for addon in addons:
|
||||
addon_utils.enable(addon)
|
||||
|
||||
self._addon_store = {
|
||||
"sys_path": sys_path,
|
||||
"addons": addons,
|
||||
}
|
||||
|
||||
def _teardown_addons(self):
|
||||
import sys
|
||||
|
||||
sys_path = self._addon_store["sys_path"]
|
||||
for path in sys_path:
|
||||
# should always succeed, but if not it doesn't matter
|
||||
# (someone else was changing the sys.path), ignore!
|
||||
try:
|
||||
sys.path.remove(path)
|
||||
except:
|
||||
pass
|
||||
|
||||
addons = self._addon_store["addons"]
|
||||
import addon_utils
|
||||
for addon in addons:
|
||||
addon_utils.disable(addon)
|
||||
|
||||
self._addon_store.clear()
|
||||
self._addon_store = None
|
||||
|
||||
def setup(self):
|
||||
if self.class_ignore is not None:
|
||||
self._setup_classes()
|
||||
|
||||
if any((self.addon_paths,
|
||||
self.addons,
|
||||
)):
|
||||
self._setup_addons()
|
||||
|
||||
if any((self.ui_ignore_operator,
|
||||
self.ui_ignore_property,
|
||||
self.ui_ignore_menu,
|
||||
self.ui_ignore_label,
|
||||
)):
|
||||
self._setup_ui_ignore()
|
||||
|
||||
def teardown(self):
|
||||
if self._class_store is not None:
|
||||
self._teardown_classes()
|
||||
|
||||
if self._addon_store is not None:
|
||||
self._teardown_addons()
|
||||
|
||||
if self._ui_ignore_store is not None:
|
||||
self._teardown_ui_ignore()
|
198
release/scripts/modules/bl_app_template_utils.py
Normal file
198
release/scripts/modules/bl_app_template_utils.py
Normal file
@ -0,0 +1,198 @@
|
||||
# ##### 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-80 compliant>
|
||||
|
||||
"""
|
||||
Similar to ``addon_utils``, except we can only have one active at a time.
|
||||
|
||||
In most cases users of this module will simply call 'activate'.
|
||||
"""
|
||||
|
||||
__all__ = (
|
||||
"activate",
|
||||
"import_from_path",
|
||||
"import_from_id",
|
||||
"reset",
|
||||
)
|
||||
|
||||
import bpy as _bpy
|
||||
|
||||
# Normally matches 'user_preferences.app_template_id',
|
||||
# but loading new preferences will get us out of sync.
|
||||
_app_template = {
|
||||
"id": "",
|
||||
}
|
||||
|
||||
# instead of sys.modules
|
||||
# note that we only ever have one template enabled at a time
|
||||
# so it may not seem necessary to use this.
|
||||
#
|
||||
# However, templates may want to share between each-other,
|
||||
# so any loaded modules are stored here?
|
||||
#
|
||||
# Note that the ID here is the app_template_id , not the modules __name__.
|
||||
_modules = {}
|
||||
|
||||
|
||||
def _enable(template_id, *, handle_error=None, ignore_not_found=False):
|
||||
import os
|
||||
import sys
|
||||
from bpy_restrict_state import RestrictBlend
|
||||
|
||||
if handle_error is None:
|
||||
def handle_error(ex):
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
# Split registering up into 2 steps so we can undo
|
||||
# if it fails par way through.
|
||||
|
||||
# disable the context, using the context at all is
|
||||
# really bad while loading an template, don't do it!
|
||||
with RestrictBlend():
|
||||
|
||||
# 1) try import
|
||||
try:
|
||||
mod = import_from_id(template_id, ignore_not_found=ignore_not_found)
|
||||
if mod is None:
|
||||
return None
|
||||
mod.__template_enabled__ = False
|
||||
_modules[template_id] = mod
|
||||
except Exception as ex:
|
||||
handle_error(ex)
|
||||
return None
|
||||
|
||||
# 2) try run the modules register function
|
||||
try:
|
||||
mod.register()
|
||||
except Exception as ex:
|
||||
print("Exception in module register(): %r" %
|
||||
getattr(mod, "__file__", template_id))
|
||||
handle_error(ex)
|
||||
del _modules[template_id]
|
||||
return None
|
||||
|
||||
# * OK loaded successfully! *
|
||||
mod.__template_enabled__ = True
|
||||
|
||||
if _bpy.app.debug_python:
|
||||
print("\tapp_template_utils.enable", mod.__name__)
|
||||
|
||||
return mod
|
||||
|
||||
|
||||
def _disable(template_id, *, handle_error=None):
|
||||
"""
|
||||
Disables a template by name.
|
||||
|
||||
:arg template_id: The name of the template and module.
|
||||
:type template_id: string
|
||||
:arg handle_error: Called in the case of an error,
|
||||
taking an exception argument.
|
||||
:type handle_error: function
|
||||
"""
|
||||
import sys
|
||||
|
||||
if handle_error is None:
|
||||
def handle_error(ex):
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
mod = _modules.get(template_id)
|
||||
|
||||
if mod and getattr(mod, "__template_enabled__", False) is not False:
|
||||
mod.__template_enabled__ = False
|
||||
|
||||
try:
|
||||
mod.unregister()
|
||||
except Exception as ex:
|
||||
print("Exception in module unregister(): %r" %
|
||||
getattr(mod, "__file__", template_id))
|
||||
handle_error(ex)
|
||||
else:
|
||||
print("\tapp_template_utils.disable: %s not %s." %
|
||||
(template_id, "disabled" if mod is None else "loaded"))
|
||||
|
||||
if _bpy.app.debug_python:
|
||||
print("\tapp_template_utils.disable", template_id)
|
||||
|
||||
|
||||
def import_from_path(path, ignore_not_found=False):
|
||||
import os
|
||||
from importlib import import_module
|
||||
base_module, template_id = path.rsplit(os.sep, 2)[-2:]
|
||||
module_name = base_module + "." + template_id
|
||||
|
||||
try:
|
||||
return import_module(module_name)
|
||||
except ModuleNotFoundError as ex:
|
||||
if ignore_not_found and ex.name == module_name:
|
||||
return None
|
||||
raise ex
|
||||
|
||||
|
||||
def import_from_id(template_id, ignore_not_found=False):
|
||||
import os
|
||||
path = next(iter(_bpy.utils.app_template_paths(template_id)), None)
|
||||
if path is None:
|
||||
if ignore_not_found:
|
||||
return None
|
||||
else:
|
||||
raise Exception("%r template not found!" % template_id)
|
||||
else:
|
||||
if ignore_not_found:
|
||||
if not os.path.exists(os.path.join(path, "__init__.py")):
|
||||
return None
|
||||
return import_from_path(path, ignore_not_found=ignore_not_found)
|
||||
|
||||
|
||||
def activate(template_id=None):
|
||||
template_id_prev = _app_template["id"]
|
||||
|
||||
# not needed but may as well avoid activating same template
|
||||
# ... in fact keep this, it will show errors early on!
|
||||
"""
|
||||
if template_id_prev == template_id:
|
||||
return
|
||||
"""
|
||||
|
||||
if template_id_prev:
|
||||
_disable(template_id_prev)
|
||||
|
||||
# Disable all addons, afterwards caller must reset.
|
||||
import addon_utils
|
||||
addon_utils.disable_all()
|
||||
|
||||
# ignore_not_found so modules that don't contain scripts don't raise errors
|
||||
mod = _enable(template_id, ignore_not_found=True) if template_id else None
|
||||
|
||||
_app_template["id"] = template_id
|
||||
|
||||
|
||||
def reset(*, reload_scripts=False):
|
||||
"""
|
||||
Sets default state.
|
||||
"""
|
||||
template_id = _bpy.context.user_preferences.app_template
|
||||
if _bpy.app.debug_python:
|
||||
print("bl_app_template_utils.reset('%s')" % template_id)
|
||||
|
||||
# TODO reload_scripts
|
||||
|
||||
activate(template_id)
|
@ -32,6 +32,7 @@ __all__ = (
|
||||
"preset_find",
|
||||
"preset_paths",
|
||||
"refresh_script_paths",
|
||||
"app_template_paths",
|
||||
"register_class",
|
||||
"register_module",
|
||||
"register_manual_map",
|
||||
@ -245,6 +246,12 @@ def load_scripts(reload_scripts=False, refresh_scripts=False):
|
||||
for mod in modules_from_path(path, loaded_modules):
|
||||
test_register(mod)
|
||||
|
||||
# load template (if set)
|
||||
if any(_bpy.utils.app_template_paths()):
|
||||
import bl_app_template_utils
|
||||
bl_app_template_utils.reset(reload_scripts=reload_scripts)
|
||||
del bl_app_template_utils
|
||||
|
||||
# deal with addons separately
|
||||
_initialize = getattr(_addon_utils, "_initialize", None)
|
||||
if _initialize is not None:
|
||||
@ -356,6 +363,38 @@ def refresh_script_paths():
|
||||
_sys_path_ensure(path)
|
||||
|
||||
|
||||
def app_template_paths(subdir=None):
|
||||
"""
|
||||
Returns valid application template paths.
|
||||
|
||||
:arg subdir: Optional subdir.
|
||||
:type subdir: string
|
||||
:return: app template paths.
|
||||
:rtype: generator
|
||||
"""
|
||||
|
||||
# note: LOCAL, USER, SYSTEM order matches script resolution order.
|
||||
subdir_tuple = (subdir,) if subdir is not None else ()
|
||||
|
||||
path = _os.path.join(*(
|
||||
resource_path('LOCAL'), "scripts", "startup",
|
||||
"bl_app_templates_user", *subdir_tuple))
|
||||
if _os.path.isdir(path):
|
||||
yield path
|
||||
else:
|
||||
path = _os.path.join(*(
|
||||
resource_path('USER'), "scripts", "startup",
|
||||
"bl_app_templates_user", *subdir_tuple))
|
||||
if _os.path.isdir(path):
|
||||
yield path
|
||||
|
||||
path = _os.path.join(*(
|
||||
resource_path('SYSTEM'), "scripts", "startup",
|
||||
"bl_app_templates_system", *subdir_tuple))
|
||||
if _os.path.isdir(path):
|
||||
yield path
|
||||
|
||||
|
||||
def preset_paths(subdir):
|
||||
"""
|
||||
Returns a list of paths for a specific preset.
|
||||
|
@ -21,7 +21,10 @@ import bpy
|
||||
import os
|
||||
from bpy.types import Operator
|
||||
from bpy.props import FloatProperty
|
||||
from mathutils import Vector, Matrix
|
||||
from mathutils import (
|
||||
Vector,
|
||||
Matrix,
|
||||
)
|
||||
|
||||
|
||||
def CLIP_spaces_walk(context, all_screens, tarea, tspace, callback, *args):
|
||||
@ -1084,4 +1087,4 @@ classes = (
|
||||
CLIP_OT_track_settings_as_default,
|
||||
CLIP_OT_track_settings_to_track,
|
||||
CLIP_OT_track_to_empty,
|
||||
)
|
||||
)
|
||||
|
@ -16,13 +16,15 @@
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
import bpy
|
||||
|
||||
from bpy.props import (
|
||||
BoolProperty,
|
||||
EnumProperty,
|
||||
StringProperty,
|
||||
)
|
||||
BoolProperty,
|
||||
EnumProperty,
|
||||
StringProperty,
|
||||
)
|
||||
|
||||
|
||||
class SCENE_OT_freestyle_fill_range_by_selection(bpy.types.Operator):
|
||||
|
@ -21,7 +21,10 @@
|
||||
import bpy
|
||||
from bpy.types import Operator
|
||||
|
||||
from bpy.props import EnumProperty, IntProperty
|
||||
from bpy.props import (
|
||||
EnumProperty,
|
||||
IntProperty,
|
||||
)
|
||||
|
||||
|
||||
class MeshMirrorUV(Operator):
|
||||
@ -254,4 +257,4 @@ classes = (
|
||||
MeshMirrorUV,
|
||||
MeshSelectNext,
|
||||
MeshSelectPrev,
|
||||
)
|
||||
)
|
||||
|
@ -21,16 +21,16 @@
|
||||
import bpy
|
||||
import nodeitems_utils
|
||||
from bpy.types import (
|
||||
Operator,
|
||||
PropertyGroup,
|
||||
)
|
||||
Operator,
|
||||
PropertyGroup,
|
||||
)
|
||||
from bpy.props import (
|
||||
BoolProperty,
|
||||
CollectionProperty,
|
||||
EnumProperty,
|
||||
IntProperty,
|
||||
StringProperty,
|
||||
)
|
||||
BoolProperty,
|
||||
CollectionProperty,
|
||||
EnumProperty,
|
||||
IntProperty,
|
||||
StringProperty,
|
||||
)
|
||||
|
||||
|
||||
class NodeSetting(PropertyGroup):
|
||||
|
@ -21,12 +21,12 @@
|
||||
import bpy
|
||||
from bpy.types import Operator
|
||||
from bpy.props import (
|
||||
StringProperty,
|
||||
BoolProperty,
|
||||
EnumProperty,
|
||||
IntProperty,
|
||||
FloatProperty,
|
||||
)
|
||||
BoolProperty,
|
||||
EnumProperty,
|
||||
FloatProperty,
|
||||
IntProperty,
|
||||
StringProperty,
|
||||
)
|
||||
|
||||
|
||||
class SelectPattern(Operator):
|
||||
@ -1054,4 +1054,4 @@ classes = (
|
||||
SubdivisionSet,
|
||||
TransformsToDeltas,
|
||||
TransformsToDeltasAnim,
|
||||
)
|
||||
)
|
||||
|
@ -26,13 +26,14 @@ from mathutils import Vector
|
||||
def GlobalBB_LQ(bb_world):
|
||||
|
||||
# Initialize the variables with the 8th vertex
|
||||
left, right, front, back, down, up = (bb_world[7][0],
|
||||
bb_world[7][0],
|
||||
bb_world[7][1],
|
||||
bb_world[7][1],
|
||||
bb_world[7][2],
|
||||
bb_world[7][2],
|
||||
)
|
||||
left, right, front, back, down, up = (
|
||||
bb_world[7][0],
|
||||
bb_world[7][0],
|
||||
bb_world[7][1],
|
||||
bb_world[7][1],
|
||||
bb_world[7][2],
|
||||
bb_world[7][2],
|
||||
)
|
||||
|
||||
# Test against the other 7 verts
|
||||
for i in range(7):
|
||||
@ -398,13 +399,15 @@ class AlignObjects(Operator):
|
||||
|
||||
def execute(self, context):
|
||||
align_axis = self.align_axis
|
||||
ret = align_objects(context,
|
||||
'X' in align_axis,
|
||||
'Y' in align_axis,
|
||||
'Z' in align_axis,
|
||||
self.align_mode,
|
||||
self.relative_to,
|
||||
self.bb_quality)
|
||||
ret = align_objects(
|
||||
context,
|
||||
'X' in align_axis,
|
||||
'Y' in align_axis,
|
||||
'Z' in align_axis,
|
||||
self.align_mode,
|
||||
self.relative_to,
|
||||
self.bb_quality,
|
||||
)
|
||||
|
||||
if not ret:
|
||||
self.report({'WARNING'}, "No objects with bound-box selected")
|
||||
@ -415,4 +418,4 @@ class AlignObjects(Operator):
|
||||
|
||||
classes = (
|
||||
AlignObjects,
|
||||
)
|
||||
)
|
||||
|
@ -22,12 +22,12 @@ from mathutils import Vector
|
||||
import bpy
|
||||
from bpy.types import Operator
|
||||
from bpy.props import (
|
||||
BoolProperty,
|
||||
EnumProperty,
|
||||
IntProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
)
|
||||
BoolProperty,
|
||||
EnumProperty,
|
||||
IntProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
)
|
||||
|
||||
|
||||
def object_ensure_material(obj, mat_name):
|
||||
@ -652,4 +652,4 @@ classes = (
|
||||
QuickFluid,
|
||||
QuickFur,
|
||||
QuickSmoke,
|
||||
)
|
||||
)
|
||||
|
@ -20,8 +20,10 @@
|
||||
|
||||
import bpy
|
||||
from bpy.types import Operator
|
||||
from bpy.props import IntProperty
|
||||
from bpy.props import EnumProperty
|
||||
from bpy.props import (
|
||||
EnumProperty,
|
||||
IntProperty,
|
||||
)
|
||||
|
||||
|
||||
class CopyRigidbodySettings(Operator):
|
||||
@ -315,4 +317,4 @@ classes = (
|
||||
BakeToKeyframes,
|
||||
ConnectRigidBodies,
|
||||
CopyRigidbodySettings,
|
||||
)
|
||||
)
|
||||
|
@ -18,7 +18,11 @@
|
||||
|
||||
# TODO <pep8 compliant>
|
||||
|
||||
from mathutils import Matrix, Vector, geometry
|
||||
from mathutils import (
|
||||
Matrix,
|
||||
Vector,
|
||||
geometry,
|
||||
)
|
||||
import bpy
|
||||
from bpy.types import Operator
|
||||
|
||||
@ -1104,4 +1108,4 @@ class SmartProject(Operator):
|
||||
|
||||
classes = (
|
||||
SmartProject,
|
||||
)
|
||||
)
|
||||
|
@ -21,12 +21,12 @@
|
||||
import bpy
|
||||
from bpy.types import Operator
|
||||
from bpy.props import (
|
||||
StringProperty,
|
||||
BoolProperty,
|
||||
IntProperty,
|
||||
FloatProperty,
|
||||
EnumProperty,
|
||||
)
|
||||
StringProperty,
|
||||
BoolProperty,
|
||||
IntProperty,
|
||||
FloatProperty,
|
||||
EnumProperty,
|
||||
)
|
||||
|
||||
from bpy.app.translations import pgettext_tip as tip_
|
||||
|
||||
@ -130,6 +130,20 @@ def execute_context_assign(self, context):
|
||||
return operator_path_undo_return(context, data_path)
|
||||
|
||||
|
||||
def module_filesystem_remove(path_base, module_name):
|
||||
import os
|
||||
module_name = os.path.splitext(module_name)[0]
|
||||
for f in os.listdir(path_base):
|
||||
f_base = os.path.splitext(f)[0]
|
||||
if f_base == module_name:
|
||||
f_full = os.path.join(path_base, f)
|
||||
|
||||
if os.path.isdir(f_full):
|
||||
os.rmdir(f_full)
|
||||
else:
|
||||
os.remove(f_full)
|
||||
|
||||
|
||||
class BRUSH_OT_active_index_set(Operator):
|
||||
"""Set active sculpt/paint brush from it's number"""
|
||||
bl_idname = "brush.active_index_set"
|
||||
@ -907,7 +921,10 @@ def _wm_doc_get_id(doc_id, do_url=True, url_prefix=""):
|
||||
# an operator (common case - just button referencing an op)
|
||||
if hasattr(bpy.types, class_name.upper() + "_OT_" + class_prop):
|
||||
if do_url:
|
||||
url = ("%s/bpy.ops.%s.html#bpy.ops.%s.%s" % (url_prefix, class_name, class_name, class_prop))
|
||||
url = (
|
||||
"%s/bpy.ops.%s.html#bpy.ops.%s.%s" %
|
||||
(url_prefix, class_name, class_name, class_prop)
|
||||
)
|
||||
else:
|
||||
rna = "bpy.ops.%s.%s" % (class_name, class_prop)
|
||||
else:
|
||||
@ -922,7 +939,10 @@ def _wm_doc_get_id(doc_id, do_url=True, url_prefix=""):
|
||||
class_name, class_prop = class_name.split("_OT_", 1)
|
||||
class_name = class_name.lower()
|
||||
if do_url:
|
||||
url = ("%s/bpy.ops.%s.html#bpy.ops.%s.%s" % (url_prefix, class_name, class_name, class_prop))
|
||||
url = (
|
||||
"%s/bpy.ops.%s.html#bpy.ops.%s.%s" %
|
||||
(url_prefix, class_name, class_name, class_prop)
|
||||
)
|
||||
else:
|
||||
rna = "bpy.ops.%s.%s" % (class_name, class_prop)
|
||||
else:
|
||||
@ -938,9 +958,12 @@ def _wm_doc_get_id(doc_id, do_url=True, url_prefix=""):
|
||||
rna_parent = rna_parent.base
|
||||
|
||||
if do_url:
|
||||
url = ("%s/bpy.types.%s.html#bpy.types.%s.%s" % (url_prefix, class_name, class_name, class_prop))
|
||||
url = (
|
||||
"%s/bpy.types.%s.html#bpy.types.%s.%s" %
|
||||
(url_prefix, class_name, class_name, class_prop)
|
||||
)
|
||||
else:
|
||||
rna = ("bpy.types.%s.%s" % (class_name, class_prop))
|
||||
rna = "bpy.types.%s.%s" % (class_name, class_prop)
|
||||
else:
|
||||
# We assume this is custom property, only try to generate generic url/rna_id...
|
||||
if do_url:
|
||||
@ -1087,10 +1110,10 @@ class WM_OT_properties_edit(Operator):
|
||||
|
||||
def execute(self, context):
|
||||
from rna_prop_ui import (
|
||||
rna_idprop_ui_prop_get,
|
||||
rna_idprop_ui_prop_clear,
|
||||
rna_idprop_ui_prop_update,
|
||||
)
|
||||
rna_idprop_ui_prop_get,
|
||||
rna_idprop_ui_prop_clear,
|
||||
rna_idprop_ui_prop_update,
|
||||
)
|
||||
|
||||
data_path = self.data_path
|
||||
value = self.value
|
||||
@ -1267,9 +1290,9 @@ class WM_OT_properties_add(Operator):
|
||||
|
||||
def execute(self, context):
|
||||
from rna_prop_ui import (
|
||||
rna_idprop_ui_prop_get,
|
||||
rna_idprop_ui_prop_update,
|
||||
)
|
||||
rna_idprop_ui_prop_get,
|
||||
rna_idprop_ui_prop_update,
|
||||
)
|
||||
|
||||
data_path = self.data_path
|
||||
item = eval("context.%s" % data_path)
|
||||
@ -1284,10 +1307,10 @@ class WM_OT_properties_add(Operator):
|
||||
|
||||
return prop_new
|
||||
|
||||
prop = unique_name(
|
||||
{*item.keys(),
|
||||
*type(item).bl_rna.properties.keys(),
|
||||
})
|
||||
prop = unique_name({
|
||||
*item.keys(),
|
||||
*type(item).bl_rna.properties.keys(),
|
||||
})
|
||||
|
||||
item[prop] = 1.0
|
||||
rna_idprop_ui_prop_update(item, prop)
|
||||
@ -1327,9 +1350,9 @@ class WM_OT_properties_remove(Operator):
|
||||
|
||||
def execute(self, context):
|
||||
from rna_prop_ui import (
|
||||
rna_idprop_ui_prop_clear,
|
||||
rna_idprop_ui_prop_update,
|
||||
)
|
||||
rna_idprop_ui_prop_clear,
|
||||
rna_idprop_ui_prop_update,
|
||||
)
|
||||
data_path = self.data_path
|
||||
item = eval("context.%s" % data_path)
|
||||
prop = self.property
|
||||
@ -1367,7 +1390,10 @@ class WM_OT_appconfig_default(Operator):
|
||||
filepath = os.path.join(bpy.utils.preset_paths("interaction")[0], "blender.py")
|
||||
|
||||
if os.path.exists(filepath):
|
||||
bpy.ops.script.execute_preset(filepath=filepath, menu_idname="USERPREF_MT_interaction_presets")
|
||||
bpy.ops.script.execute_preset(
|
||||
filepath=filepath,
|
||||
menu_idname="USERPREF_MT_interaction_presets",
|
||||
)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
@ -1387,7 +1413,10 @@ class WM_OT_appconfig_activate(Operator):
|
||||
filepath = self.filepath.replace("keyconfig", "interaction")
|
||||
|
||||
if os.path.exists(filepath):
|
||||
bpy.ops.script.execute_preset(filepath=filepath, menu_idname="USERPREF_MT_interaction_presets")
|
||||
bpy.ops.script.execute_preset(
|
||||
filepath=filepath,
|
||||
menu_idname="USERPREF_MT_interaction_presets",
|
||||
)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
@ -1492,7 +1521,7 @@ class WM_OT_blenderplayer_start(Operator):
|
||||
"-g", "show_profile", "=", "%d" % gs.show_framerate_profile,
|
||||
"-g", "show_properties", "=", "%d" % gs.show_debug_properties,
|
||||
"-g", "ignore_deprecation_warnings", "=", "%d" % (not gs.use_deprecation_warnings),
|
||||
])
|
||||
])
|
||||
|
||||
# finish the call with the path to the blend file
|
||||
args.append(filepath)
|
||||
@ -1622,10 +1651,11 @@ class WM_OT_keyconfig_export(Operator):
|
||||
|
||||
wm = context.window_manager
|
||||
|
||||
keyconfig_utils.keyconfig_export(wm,
|
||||
wm.keyconfigs.active,
|
||||
self.filepath,
|
||||
)
|
||||
keyconfig_utils.keyconfig_export(
|
||||
wm,
|
||||
wm.keyconfigs.active,
|
||||
self.filepath,
|
||||
)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
@ -1890,7 +1920,10 @@ class WM_OT_theme_install(Operator):
|
||||
|
||||
try:
|
||||
shutil.copyfile(xmlfile, path_dest)
|
||||
bpy.ops.script.execute_preset(filepath=path_dest, menu_idname="USERPREF_MT_interface_theme_presets")
|
||||
bpy.ops.script.execute_preset(
|
||||
filepath=path_dest,
|
||||
menu_idname="USERPREF_MT_interface_theme_presets",
|
||||
)
|
||||
|
||||
except:
|
||||
traceback.print_exc()
|
||||
@ -1917,10 +1950,12 @@ class WM_OT_addon_refresh(Operator):
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
# Note: shares some logic with WM_OT_app_template_install
|
||||
# but not enough to de-duplicate. Fixed here may apply to both.
|
||||
class WM_OT_addon_install(Operator):
|
||||
"Install an add-on"
|
||||
bl_idname = "wm.addon_install"
|
||||
bl_label = "Install from File..."
|
||||
bl_label = "Install Add-on from File..."
|
||||
|
||||
overwrite = BoolProperty(
|
||||
name="Overwrite",
|
||||
@ -1951,20 +1986,6 @@ class WM_OT_addon_install(Operator):
|
||||
options={'HIDDEN'},
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _module_remove(path_addons, module):
|
||||
import os
|
||||
module = os.path.splitext(module)[0]
|
||||
for f in os.listdir(path_addons):
|
||||
f_base = os.path.splitext(f)[0]
|
||||
if f_base == module:
|
||||
f_full = os.path.join(path_addons, f)
|
||||
|
||||
if os.path.isdir(f_full):
|
||||
os.rmdir(f_full)
|
||||
else:
|
||||
os.remove(f_full)
|
||||
|
||||
def execute(self, context):
|
||||
import addon_utils
|
||||
import traceback
|
||||
@ -2017,7 +2038,7 @@ class WM_OT_addon_install(Operator):
|
||||
|
||||
if self.overwrite:
|
||||
for f in file_to_extract.namelist():
|
||||
WM_OT_addon_install._module_remove(path_addons, f)
|
||||
module_filesystem_remove(path_addons, f)
|
||||
else:
|
||||
for f in file_to_extract.namelist():
|
||||
path_dest = os.path.join(path_addons, os.path.basename(f))
|
||||
@ -2035,7 +2056,7 @@ class WM_OT_addon_install(Operator):
|
||||
path_dest = os.path.join(path_addons, os.path.basename(pyfile))
|
||||
|
||||
if self.overwrite:
|
||||
WM_OT_addon_install._module_remove(path_addons, os.path.basename(pyfile))
|
||||
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)
|
||||
return {'CANCELLED'}
|
||||
@ -2070,7 +2091,10 @@ class WM_OT_addon_install(Operator):
|
||||
bpy.utils.refresh_script_paths()
|
||||
|
||||
# print message
|
||||
msg = tip_("Modules Installed from %r into %r (%s)") % (pyfile, path_addons, ", ".join(sorted(addons_new)))
|
||||
msg = (
|
||||
tip_("Modules Installed (%s) from %r into %r (%s)") %
|
||||
(", ".join(sorted(addons_new)), pyfile, path_addons)
|
||||
)
|
||||
print(msg)
|
||||
self.report({'INFO'}, msg)
|
||||
|
||||
@ -2164,6 +2188,7 @@ class WM_OT_addon_expand(Operator):
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class WM_OT_addon_userpref_show(Operator):
|
||||
"Show add-on user preferences"
|
||||
bl_idname = "wm.addon_userpref_show"
|
||||
@ -2194,6 +2219,123 @@ class WM_OT_addon_userpref_show(Operator):
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
# Note: shares some logic with WM_OT_addon_install
|
||||
# but not enough to de-duplicate. Fixes here may apply to both.
|
||||
class WM_OT_app_template_install(Operator):
|
||||
"Install an application-template"
|
||||
bl_idname = "wm.app_template_install"
|
||||
bl_label = "Install Template from File..."
|
||||
|
||||
overwrite = BoolProperty(
|
||||
name="Overwrite",
|
||||
description="Remove existing template with the same ID",
|
||||
default=True,
|
||||
)
|
||||
|
||||
filepath = StringProperty(
|
||||
subtype='FILE_PATH',
|
||||
)
|
||||
filter_folder = BoolProperty(
|
||||
name="Filter folders",
|
||||
default=True,
|
||||
options={'HIDDEN'},
|
||||
)
|
||||
filter_python = BoolProperty(
|
||||
name="Filter python",
|
||||
default=True,
|
||||
options={'HIDDEN'},
|
||||
)
|
||||
filter_glob = StringProperty(
|
||||
default="*.py;*.zip",
|
||||
options={'HIDDEN'},
|
||||
)
|
||||
|
||||
def execute(self, context):
|
||||
import traceback
|
||||
import zipfile
|
||||
import shutil
|
||||
import os
|
||||
|
||||
pyfile = self.filepath
|
||||
|
||||
path_app_templates = bpy.utils.user_resource(
|
||||
'SCRIPTS', os.path.join("startup", "bl_app_templates_user"),
|
||||
create=True,
|
||||
)
|
||||
|
||||
if not path_app_templates:
|
||||
self.report({'ERROR'}, "Failed to get add-ons path")
|
||||
return {'CANCELLED'}
|
||||
|
||||
if not os.path.isdir(path_app_templates):
|
||||
try:
|
||||
os.makedirs(path_app_templates, exist_ok=True)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
||||
app_templates_old = set(os.listdir(path_app_templates))
|
||||
|
||||
# check to see if the file is in compressed format (.zip)
|
||||
if zipfile.is_zipfile(pyfile):
|
||||
try:
|
||||
file_to_extract = zipfile.ZipFile(pyfile, 'r')
|
||||
except:
|
||||
traceback.print_exc()
|
||||
return {'CANCELLED'}
|
||||
|
||||
if self.overwrite:
|
||||
for f in file_to_extract.namelist():
|
||||
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))
|
||||
if os.path.exists(path_dest):
|
||||
self.report({'WARNING'}, "File already installed to %r\n" % path_dest)
|
||||
return {'CANCELLED'}
|
||||
|
||||
try: # extract the file to "bl_app_templates_user"
|
||||
file_to_extract.extractall(path_app_templates)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
return {'CANCELLED'}
|
||||
|
||||
else:
|
||||
path_dest = os.path.join(path_app_templates, os.path.basename(pyfile))
|
||||
|
||||
if self.overwrite:
|
||||
module_filesystem_remove(path_app_templates, os.path.basename(pyfile))
|
||||
elif os.path.exists(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
|
||||
try:
|
||||
shutil.copyfile(pyfile, path_dest)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
return {'CANCELLED'}
|
||||
|
||||
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()
|
||||
|
||||
# print message
|
||||
msg = (
|
||||
tip_("Template Installed (%s) from %r into %r") %
|
||||
(", ".join(sorted(app_templates_new)), pyfile, path_app_templates)
|
||||
)
|
||||
print(msg)
|
||||
self.report({'INFO'}, msg)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
wm = context.window_manager
|
||||
wm.fileselect_add(self)
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
|
||||
classes = (
|
||||
BRUSH_OT_active_index_set,
|
||||
WM_OT_addon_disable,
|
||||
@ -2203,6 +2345,7 @@ classes = (
|
||||
WM_OT_addon_refresh,
|
||||
WM_OT_addon_remove,
|
||||
WM_OT_addon_userpref_show,
|
||||
WM_OT_app_template_install,
|
||||
WM_OT_appconfig_activate,
|
||||
WM_OT_appconfig_default,
|
||||
WM_OT_blenderplayer_start,
|
||||
@ -2246,4 +2389,4 @@ classes = (
|
||||
WM_OT_sysinfo,
|
||||
WM_OT_theme_install,
|
||||
WM_OT_url_open,
|
||||
)
|
||||
)
|
||||
|
@ -127,6 +127,18 @@ class INFO_MT_file(Menu):
|
||||
layout.operator("wm.save_homefile", icon='SAVE_PREFS')
|
||||
layout.operator("wm.read_factory_settings", icon='LOAD_FACTORY')
|
||||
|
||||
if any(bpy.utils.app_template_paths()):
|
||||
app_template = context.user_preferences.app_template
|
||||
if app_template:
|
||||
layout.operator(
|
||||
"wm.read_factory_settings",
|
||||
text="Load Factory Template Settings",
|
||||
icon='LOAD_FACTORY',
|
||||
).app_template = app_template
|
||||
del app_template
|
||||
|
||||
layout.menu("USERPREF_MT_app_templates", icon='FILE_BLEND')
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator_context = 'INVOKE_AREA'
|
||||
|
@ -90,6 +90,63 @@ class USERPREF_MT_interaction_presets(Menu):
|
||||
draw = Menu.draw_preset
|
||||
|
||||
|
||||
class USERPREF_MT_app_templates(Menu):
|
||||
bl_label = "Application Templates"
|
||||
preset_subdir = "app_templates"
|
||||
|
||||
def draw_ex(self, context, *, use_splash=False, use_default=False, use_install=False):
|
||||
import os
|
||||
|
||||
layout = self.layout
|
||||
|
||||
# now draw the presets
|
||||
layout.operator_context = 'EXEC_DEFAULT'
|
||||
|
||||
if use_default:
|
||||
props = layout.operator("wm.read_homefile", text="Default")
|
||||
props.use_splash = True
|
||||
props.app_template = ""
|
||||
layout.separator()
|
||||
|
||||
template_paths = bpy.utils.app_template_paths()
|
||||
|
||||
# expand template paths
|
||||
app_templates = []
|
||||
for path in template_paths:
|
||||
for d in os.listdir(path):
|
||||
if d.startswith(("__", ".")):
|
||||
continue
|
||||
template = os.path.join(path, d)
|
||||
if os.path.isdir(template):
|
||||
# template_paths_expand.append(template)
|
||||
app_templates.append(d)
|
||||
|
||||
for d in sorted(app_templates):
|
||||
props = layout.operator(
|
||||
"wm.read_homefile",
|
||||
text=bpy.path.display_name(d),
|
||||
)
|
||||
props.use_splash = True
|
||||
props.app_template = d;
|
||||
|
||||
if use_install:
|
||||
layout.separator()
|
||||
layout.operator_context = 'INVOKE_DEFAULT'
|
||||
props = layout.operator("wm.app_template_install")
|
||||
|
||||
|
||||
def draw(self, context):
|
||||
self.draw_ex(context, use_splash=False, use_default=True, use_install=True)
|
||||
|
||||
|
||||
class USERPREF_MT_templates_splash(Menu):
|
||||
bl_label = "Startup Templates"
|
||||
preset_subdir = "templates"
|
||||
|
||||
def draw(self, context):
|
||||
USERPREF_MT_app_templates.draw_ex(self, context, use_splash=True, use_default=True)
|
||||
|
||||
|
||||
class USERPREF_MT_appconfigs(Menu):
|
||||
bl_label = "AppPresets"
|
||||
preset_subdir = "keyconfig"
|
||||
@ -110,7 +167,17 @@ class USERPREF_MT_splash(Menu):
|
||||
|
||||
split = layout.split()
|
||||
row = split.row()
|
||||
row.label("")
|
||||
|
||||
if any(bpy.utils.app_template_paths()):
|
||||
row.label("Template:")
|
||||
template = context.user_preferences.app_template
|
||||
row.menu(
|
||||
"USERPREF_MT_templates_splash",
|
||||
text=bpy.path.display_name(template) if template else "Default",
|
||||
)
|
||||
else:
|
||||
row.label("")
|
||||
|
||||
row = split.row()
|
||||
row.label("Interaction:")
|
||||
|
||||
@ -1485,6 +1552,8 @@ classes = (
|
||||
USERPREF_HT_header,
|
||||
USERPREF_PT_tabs,
|
||||
USERPREF_MT_interaction_presets,
|
||||
USERPREF_MT_templates_splash,
|
||||
USERPREF_MT_app_templates,
|
||||
USERPREF_MT_appconfigs,
|
||||
USERPREF_MT_splash,
|
||||
USERPREF_MT_splash_footer,
|
||||
|
@ -19,7 +19,11 @@
|
||||
# <pep8 compliant>
|
||||
import bpy
|
||||
import nodeitems_utils
|
||||
from nodeitems_utils import NodeCategory, NodeItem, NodeItemCustom
|
||||
from nodeitems_utils import (
|
||||
NodeCategory,
|
||||
NodeItem,
|
||||
NodeItemCustom,
|
||||
)
|
||||
|
||||
|
||||
# Subclasses for standard node types
|
||||
|
@ -33,6 +33,9 @@ const char *BKE_appdir_folder_id_create(const int folder_id, const char *subfold
|
||||
const char *BKE_appdir_folder_id_user_notest(const int folder_id, const char *subfolder);
|
||||
const char *BKE_appdir_folder_id_version(const int folder_id, const int ver, const bool do_check);
|
||||
|
||||
bool BKE_appdir_app_template_any(void);
|
||||
bool BKE_appdir_app_template_id_search(const char *app_template, char *path, size_t path_len);
|
||||
|
||||
/* Initialize path to program executable */
|
||||
void BKE_appdir_program_path_init(const char *argv0);
|
||||
|
||||
|
@ -52,6 +52,8 @@ void BKE_blender_userdef_set_data(struct UserDef *userdef);
|
||||
void BKE_blender_userdef_free_data(struct UserDef *userdef);
|
||||
void BKE_blender_userdef_refresh(void);
|
||||
|
||||
void BKE_blender_userdef_set_app_template(struct UserDef *userdef);
|
||||
|
||||
/* set this callback when a UI is running */
|
||||
void BKE_blender_callback_test_break_set(void (*func)(void));
|
||||
int BKE_blender_test_break(void);
|
||||
|
@ -683,6 +683,48 @@ bool BKE_appdir_program_python_search(
|
||||
return is_found;
|
||||
}
|
||||
|
||||
static const char *app_template_directory_search[2] = {
|
||||
"startup" SEP_STR "bl_app_templates_user",
|
||||
"startup" SEP_STR "bl_app_templates_system",
|
||||
};
|
||||
|
||||
static const int app_template_directory_id[2] = {
|
||||
BLENDER_USER_SCRIPTS,
|
||||
BLENDER_SYSTEM_SCRIPTS,
|
||||
};
|
||||
|
||||
/**
|
||||
* Return true if templates exist
|
||||
*/
|
||||
bool BKE_appdir_app_template_any(void)
|
||||
{
|
||||
char temp_dir[FILE_MAX];
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (BKE_appdir_folder_id_ex(
|
||||
app_template_directory_id[i], app_template_directory_search[i],
|
||||
temp_dir, sizeof(temp_dir)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BKE_appdir_app_template_id_search(const char *app_template, char *path, size_t path_len)
|
||||
{
|
||||
for (int i = 0; i < 2; i++) {
|
||||
char subdir[FILE_MAX];
|
||||
BLI_join_dirfile(subdir, sizeof(subdir), app_template_directory_search[i], app_template);
|
||||
if (BKE_appdir_folder_id_ex(
|
||||
app_template_directory_id[i], subdir,
|
||||
path, path_len))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the temp directory when blender first runs.
|
||||
* If the default path is not found, use try $TEMP
|
||||
|
@ -238,6 +238,44 @@ void BKE_blender_userdef_refresh(void)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Write U from userdef.
|
||||
* This function defines which settings a template will override for the user preferences.
|
||||
*/
|
||||
void BKE_blender_userdef_set_app_template(UserDef *userdef)
|
||||
{
|
||||
/* TODO:
|
||||
* - keymaps
|
||||
* - various minor settings (add as needed).
|
||||
*/
|
||||
|
||||
#define LIST_OVERRIDE(id) { \
|
||||
BLI_freelistN(&U.id); \
|
||||
BLI_movelisttolist(&U.id, &userdef->id); \
|
||||
} ((void)0)
|
||||
|
||||
#define MEMCPY_OVERRIDE(id) \
|
||||
memcpy(U.id, userdef->id, sizeof(U.id));
|
||||
|
||||
/* for some types we need custom free functions */
|
||||
userdef_free_addons(&U);
|
||||
userdef_free_keymaps(&U);
|
||||
|
||||
LIST_OVERRIDE(uistyles);
|
||||
LIST_OVERRIDE(uifonts);
|
||||
LIST_OVERRIDE(themes);
|
||||
LIST_OVERRIDE(addons);
|
||||
LIST_OVERRIDE(user_keymaps);
|
||||
|
||||
MEMCPY_OVERRIDE(light);
|
||||
|
||||
MEMCPY_OVERRIDE(font_path_ui);
|
||||
MEMCPY_OVERRIDE(font_path_ui_mono);
|
||||
|
||||
#undef LIST_OVERRIDE
|
||||
#undef MEMCPY_OVERRIDE
|
||||
}
|
||||
|
||||
/* ***************** testing for break ************* */
|
||||
|
||||
static void (*blender_test_break_cb)(void) = NULL;
|
||||
|
@ -33,14 +33,10 @@
|
||||
/** \file blender/blenkernel/intern/customdata.c
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "DNA_customdata_types.h"
|
||||
#include "DNA_meshdata_types.h"
|
||||
#include "DNA_ID.h"
|
||||
|
||||
@ -48,7 +44,6 @@
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_utils.h"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_math_color_blend.h"
|
||||
#include "BLI_mempool.h"
|
||||
|
||||
@ -66,16 +61,11 @@
|
||||
|
||||
#include "bmesh.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
/* number of layers to add when growing a CustomData object */
|
||||
#define CUSTOMDATA_GROW 5
|
||||
|
||||
/* ensure typemap size is ok */
|
||||
BLI_STATIC_ASSERT(sizeof(((CustomData *)NULL)->typemap) /
|
||||
sizeof(((CustomData *)NULL)->typemap[0]) == CD_NUMTYPES,
|
||||
"size mismatch");
|
||||
BLI_STATIC_ASSERT(ARRAY_SIZE(((CustomData *)NULL)->typemap) == CD_NUMTYPES, "size mismatch");
|
||||
|
||||
|
||||
/********************* Layer type information **********************/
|
||||
@ -1062,15 +1052,10 @@ static void layerInterp_mcol(
|
||||
|
||||
/* Subdivide smooth or fractal can cause problems without clamping
|
||||
* although weights should also not cause this situation */
|
||||
CLAMP(col[j].a, 0.0f, 255.0f);
|
||||
CLAMP(col[j].r, 0.0f, 255.0f);
|
||||
CLAMP(col[j].g, 0.0f, 255.0f);
|
||||
CLAMP(col[j].b, 0.0f, 255.0f);
|
||||
|
||||
mc[j].a = (int)col[j].a;
|
||||
mc[j].r = (int)col[j].r;
|
||||
mc[j].g = (int)col[j].g;
|
||||
mc[j].b = (int)col[j].b;
|
||||
mc[j].a = CLAMPIS(iroundf(col[j].a), 0, 255);
|
||||
mc[j].r = CLAMPIS(iroundf(col[j].r), 0, 255);
|
||||
mc[j].g = CLAMPIS(iroundf(col[j].g), 0, 255);
|
||||
mc[j].b = CLAMPIS(iroundf(col[j].b), 0, 255);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -473,7 +473,10 @@ typedef struct UserDef {
|
||||
char pad2;
|
||||
short transopts;
|
||||
short menuthreshold1, menuthreshold2;
|
||||
|
||||
|
||||
/* startup template */
|
||||
char app_template[64];
|
||||
|
||||
struct ListBase themes;
|
||||
struct ListBase uifonts;
|
||||
struct ListBase uistyles;
|
||||
|
@ -4678,6 +4678,11 @@ void RNA_def_userdef(BlenderRNA *brna)
|
||||
"Active section of the user preferences shown in the user interface");
|
||||
RNA_def_property_update(prop, 0, "rna_userdef_update");
|
||||
|
||||
/* don't expose this directly via the UI, modify via an operator */
|
||||
prop = RNA_def_property(srna, "app_template", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_string_sdna(prop, NULL, "app_template");
|
||||
RNA_def_property_ui_text(prop, "Application Template", "");
|
||||
|
||||
prop = RNA_def_property(srna, "themes", PROP_COLLECTION, PROP_NONE);
|
||||
RNA_def_property_collection_sdna(prop, NULL, "themes", NULL);
|
||||
RNA_def_property_struct_type(prop, "Theme");
|
||||
|
@ -470,6 +470,10 @@ static void wm_file_read_post(bContext *C, bool is_startup_file)
|
||||
if (is_startup_file) {
|
||||
/* possible python hasn't been initialized */
|
||||
if (CTX_py_init_get(C)) {
|
||||
/* Only run when we have a template path found. */
|
||||
if (BKE_appdir_app_template_any()) {
|
||||
BPY_execute_string(C, "__import__('bl_app_template_utils').reset()");
|
||||
}
|
||||
/* sync addons, these may have changed from the defaults */
|
||||
BPY_execute_string(C, "__import__('addon_utils').reset_all()");
|
||||
|
||||
@ -635,15 +639,23 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports)
|
||||
* \param use_factory_settings: Ignore on-disk startup file, use bundled ``datatoc_startup_blend`` instead.
|
||||
* Used for "Restore Factory Settings".
|
||||
* \param filepath_startup_override: Optional path pointing to an alternative blend file (may be NULL).
|
||||
* \param app_template_override: Template to use instead of the template defined in user-preferences.
|
||||
* When not-null, this is written into the user preferences.
|
||||
*/
|
||||
int wm_homefile_read(
|
||||
bContext *C, ReportList *reports,
|
||||
bool use_factory_settings, const char *filepath_startup_override)
|
||||
bContext *C, ReportList *reports, bool use_factory_settings,
|
||||
const char *filepath_startup_override, const char *app_template_override)
|
||||
{
|
||||
ListBase wmbase;
|
||||
bool success = false;
|
||||
|
||||
char filepath_startup[FILE_MAX];
|
||||
char filepath_userdef[FILE_MAX];
|
||||
bool success = false;
|
||||
|
||||
/* When 'app_template' is set: '{BLENDER_USER_CONFIG}/{app_template}' */
|
||||
char app_template_system[FILE_MAX];
|
||||
/* When 'app_template' is set: '{BLENDER_SYSTEM_SCRIPTS}/startup/bl_app_templates_system/{app_template}' */
|
||||
char app_template_config[FILE_MAX];
|
||||
|
||||
/* Indicates whether user preferences were really load from memory.
|
||||
*
|
||||
@ -675,12 +687,14 @@ int wm_homefile_read(
|
||||
|
||||
filepath_startup[0] = '\0';
|
||||
filepath_userdef[0] = '\0';
|
||||
app_template_system[0] = '\0';
|
||||
app_template_config[0] = '\0';
|
||||
|
||||
const char * const cfgdir = BKE_appdir_folder_id(BLENDER_USER_CONFIG, NULL);
|
||||
if (!use_factory_settings) {
|
||||
const char * const cfgdir = BKE_appdir_folder_id(BLENDER_USER_CONFIG, NULL);
|
||||
if (cfgdir) {
|
||||
BLI_make_file_string("/", filepath_startup, cfgdir, BLENDER_STARTUP_FILE);
|
||||
BLI_make_file_string("/", filepath_userdef, cfgdir, BLENDER_USERPREF_FILE);
|
||||
BLI_path_join(filepath_startup, sizeof(filepath_startup), cfgdir, BLENDER_STARTUP_FILE, NULL);
|
||||
BLI_path_join(filepath_userdef, sizeof(filepath_startup), cfgdir, BLENDER_USERPREF_FILE, NULL);
|
||||
}
|
||||
else {
|
||||
use_factory_settings = true;
|
||||
@ -704,7 +718,43 @@ int wm_homefile_read(
|
||||
}
|
||||
}
|
||||
|
||||
if (!use_factory_settings) {
|
||||
const char *app_template = NULL;
|
||||
|
||||
if (filepath_startup_override != NULL) {
|
||||
/* pass */
|
||||
}
|
||||
else if (app_template_override) {
|
||||
app_template = app_template_override;
|
||||
}
|
||||
else if (!use_factory_settings && U.app_template[0]) {
|
||||
app_template = U.app_template;
|
||||
}
|
||||
|
||||
if (app_template != NULL) {
|
||||
BKE_appdir_app_template_id_search(app_template, app_template_system, sizeof(app_template_system));
|
||||
BLI_path_join(app_template_config, sizeof(app_template_config), cfgdir, app_template, NULL);
|
||||
}
|
||||
|
||||
/* insert template name into startup file */
|
||||
if (app_template != NULL) {
|
||||
/* note that the path is being set even when 'use_factory_settings == true'
|
||||
* this is done so we can load a templates factory-settings */
|
||||
if (!use_factory_settings) {
|
||||
BLI_path_join(filepath_startup, sizeof(filepath_startup), app_template_config, BLENDER_STARTUP_FILE, NULL);
|
||||
if (BLI_access(filepath_startup, R_OK) != 0) {
|
||||
filepath_startup[0] = '\0';
|
||||
}
|
||||
}
|
||||
else {
|
||||
filepath_startup[0] = '\0';
|
||||
}
|
||||
|
||||
if (filepath_startup[0] == '\0') {
|
||||
BLI_path_join(filepath_startup, sizeof(filepath_startup), app_template_system, BLENDER_STARTUP_FILE, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (!use_factory_settings || (filepath_startup[0] != '\0')) {
|
||||
if (BLI_access(filepath_startup, R_OK) == 0) {
|
||||
success = (BKE_blendfile_read(C, filepath_startup, NULL, skip_flags) != BKE_BLENDFILE_READ_FAIL);
|
||||
}
|
||||
@ -716,8 +766,8 @@ int wm_homefile_read(
|
||||
}
|
||||
|
||||
if (success == false && filepath_startup_override && reports) {
|
||||
/* We can not return from here because wm is already reset */
|
||||
BKE_reportf(reports, RPT_ERROR, "Could not read '%s'", filepath_startup_override);
|
||||
/*We can not return from here because wm is already reset*/
|
||||
}
|
||||
|
||||
if (success == false) {
|
||||
@ -733,7 +783,45 @@ int wm_homefile_read(
|
||||
U.flag |= USER_SCRIPT_AUTOEXEC_DISABLE;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* Load template preferences,
|
||||
* unlike regular preferences we only use some of the settings,
|
||||
* see: BKE_blender_userdef_set_app_template */
|
||||
if (app_template_system[0] != '\0') {
|
||||
char temp_path[FILE_MAX];
|
||||
temp_path[0] = '\0';
|
||||
if (!use_factory_settings) {
|
||||
BLI_path_join(temp_path, sizeof(temp_path), app_template_config, BLENDER_USERPREF_FILE, NULL);
|
||||
if (BLI_access(temp_path, R_OK) != 0) {
|
||||
temp_path[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
if (temp_path[0] == '\0') {
|
||||
BLI_path_join(temp_path, sizeof(temp_path), app_template_system, BLENDER_USERPREF_FILE, NULL);
|
||||
}
|
||||
|
||||
UserDef *userdef_template = NULL;
|
||||
/* just avoids missing file warning */
|
||||
if (BLI_exists(temp_path)) {
|
||||
userdef_template = BKE_blendfile_userdef_read(temp_path, NULL);
|
||||
}
|
||||
if (userdef_template == NULL) {
|
||||
/* we need to have preferences load to overwrite preferences from previous template */
|
||||
userdef_template = BKE_blendfile_userdef_read_from_memory(
|
||||
datatoc_startup_blend, datatoc_startup_blend_size, NULL);
|
||||
}
|
||||
if (userdef_template) {
|
||||
BKE_blender_userdef_set_app_template(userdef_template);
|
||||
BKE_blender_userdef_free_data(userdef_template);
|
||||
MEM_freeN(userdef_template);
|
||||
}
|
||||
}
|
||||
|
||||
if (app_template_override) {
|
||||
BLI_strncpy(U.app_template, app_template_override, sizeof(U.app_template));
|
||||
}
|
||||
|
||||
/* prevent buggy files that had G_FILE_RELATIVE_REMAP written out by mistake. Screws up autosaves otherwise
|
||||
* can remove this eventually, only in a 2.53 and older, now its not written */
|
||||
G.fileflags &= ~G_FILE_RELATIVE_REMAP;
|
||||
@ -1271,6 +1359,13 @@ static int wm_homefile_write_exec(bContext *C, wmOperator *op)
|
||||
char filepath[FILE_MAX];
|
||||
int fileflags;
|
||||
|
||||
const char *app_template = U.app_template[0] ? U.app_template : NULL;
|
||||
const char * const cfgdir = BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, app_template);
|
||||
if (cfgdir == NULL) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Unable to create user config path");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
BLI_callback_exec(G.main, NULL, BLI_CB_EVT_SAVE_PRE);
|
||||
|
||||
/* check current window and close it if temp */
|
||||
@ -1280,7 +1375,8 @@ static int wm_homefile_write_exec(bContext *C, wmOperator *op)
|
||||
/* update keymaps in user preferences */
|
||||
WM_keyconfig_update(wm);
|
||||
|
||||
BLI_make_file_string("/", filepath, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_STARTUP_FILE);
|
||||
BLI_path_join(filepath, sizeof(filepath), cfgdir, BLENDER_STARTUP_FILE, NULL);
|
||||
|
||||
printf("trying to save homefile at %s ", filepath);
|
||||
|
||||
ED_editors_flush_edits(C, false);
|
||||
@ -1358,21 +1454,44 @@ static int wm_userpref_write_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
char filepath[FILE_MAX];
|
||||
const char *cfgdir;
|
||||
bool ok = false;
|
||||
|
||||
/* update keymaps in user preferences */
|
||||
WM_keyconfig_update(wm);
|
||||
|
||||
BLI_make_file_string("/", filepath, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_USERPREF_FILE);
|
||||
printf("trying to save userpref at %s ", filepath);
|
||||
|
||||
if (BKE_blendfile_userdef_write(filepath, op->reports) == 0) {
|
||||
printf("fail\n");
|
||||
return OPERATOR_CANCELLED;
|
||||
if ((cfgdir = BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL))) {
|
||||
BLI_path_join(filepath, sizeof(filepath), cfgdir, BLENDER_USERPREF_FILE, NULL);
|
||||
printf("trying to save userpref at %s ", filepath);
|
||||
if (BKE_blendfile_userdef_write(filepath, op->reports) != 0) {
|
||||
printf("ok\n");
|
||||
ok = true;
|
||||
}
|
||||
else {
|
||||
printf("fail\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
BKE_report(op->reports, RPT_ERROR, "Unable to create userpref path");
|
||||
}
|
||||
|
||||
printf("ok\n");
|
||||
if (U.app_template[0] && (cfgdir = BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, U.app_template))) {
|
||||
/* Also save app-template prefs */
|
||||
BLI_path_join(filepath, sizeof(filepath), cfgdir, BLENDER_USERPREF_FILE, NULL);
|
||||
printf("trying to save app-template userpref at %s ", filepath);
|
||||
if (BKE_blendfile_userdef_write(filepath, op->reports) == 0) {
|
||||
printf("fail\n");
|
||||
ok = true;
|
||||
}
|
||||
else {
|
||||
printf("ok\n");
|
||||
}
|
||||
}
|
||||
else if (U.app_template[0]) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Unable to create app-template userpref path");
|
||||
}
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
void WM_OT_save_userpref(wmOperatorType *ot)
|
||||
@ -1433,9 +1552,21 @@ static int wm_homefile_read_exec(bContext *C, wmOperator *op)
|
||||
G.fileflags &= ~G_FILE_NO_UI;
|
||||
}
|
||||
|
||||
if (wm_homefile_read(C, op->reports, use_factory_settings, filepath)) {
|
||||
/* Load a file but keep the splash open */
|
||||
if (!use_factory_settings && RNA_boolean_get(op->ptr, "use_splash")) {
|
||||
char app_template_buf[sizeof(U.app_template)];
|
||||
const char *app_template;
|
||||
PropertyRNA *prop_app_template = RNA_struct_find_property(op->ptr, "app_template");
|
||||
const bool use_splash = !use_factory_settings && RNA_boolean_get(op->ptr, "use_splash");
|
||||
|
||||
if (prop_app_template && RNA_property_is_set(op->ptr, prop_app_template)) {
|
||||
RNA_property_string_get(op->ptr, prop_app_template, app_template_buf);
|
||||
app_template = app_template_buf;
|
||||
}
|
||||
else {
|
||||
app_template = NULL;
|
||||
}
|
||||
|
||||
if (wm_homefile_read(C, op->reports, use_factory_settings, filepath, app_template)) {
|
||||
if (use_splash) {
|
||||
WM_init_splash(C);
|
||||
}
|
||||
return OPERATOR_FINISHED;
|
||||
@ -1469,17 +1600,26 @@ void WM_OT_read_homefile(wmOperatorType *ot)
|
||||
prop = RNA_def_boolean(ot->srna, "use_splash", false, "Splash", "");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
|
||||
prop = RNA_def_string(ot->srna, "app_template", "Template", sizeof(U.app_template), "", "");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
|
||||
/* omit poll to run in background mode */
|
||||
}
|
||||
|
||||
void WM_OT_read_factory_settings(wmOperatorType *ot)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
ot->name = "Load Factory Settings";
|
||||
ot->idname = "WM_OT_read_factory_settings";
|
||||
ot->description = "Load default file and user preferences";
|
||||
|
||||
ot->invoke = WM_operator_confirm;
|
||||
ot->exec = wm_homefile_read_exec;
|
||||
|
||||
prop = RNA_def_string(ot->srna, "app_template", "Template", sizeof(U.app_template), "", "");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
|
||||
/* omit poll to run in background mode */
|
||||
}
|
||||
|
||||
|
@ -193,7 +193,7 @@ void WM_init(bContext *C, int argc, const char **argv)
|
||||
wm_init_reports(C);
|
||||
|
||||
/* get the default database, plus a wm */
|
||||
wm_homefile_read(C, NULL, G.factory_startup, NULL);
|
||||
wm_homefile_read(C, NULL, G.factory_startup, NULL, NULL);
|
||||
|
||||
|
||||
BLT_lang_set(NULL);
|
||||
|
@ -1791,6 +1791,36 @@ static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(ar
|
||||
ibuf = IMB_ibImageFromMemory((unsigned char *)datatoc_splash_png,
|
||||
datatoc_splash_png_size, IB_rect, NULL, "<splash screen>");
|
||||
}
|
||||
|
||||
/* overwrite splash with template image */
|
||||
if (U.app_template[0] != '\0') {
|
||||
ImBuf *ibuf_template = NULL;
|
||||
char splash_filepath[FILE_MAX];
|
||||
char template_directory[FILE_MAX];
|
||||
|
||||
if (BKE_appdir_app_template_id_search(
|
||||
U.app_template,
|
||||
template_directory, sizeof(template_directory)))
|
||||
{
|
||||
BLI_join_dirfile(
|
||||
splash_filepath, sizeof(splash_filepath), template_directory,
|
||||
(U.pixelsize == 2) ? "splash_2x.png" : "splash.png");
|
||||
ibuf_template = IMB_loadiffname(splash_filepath, IB_rect, NULL);
|
||||
if (ibuf_template) {
|
||||
const int x_expect = ibuf_template->x;
|
||||
const int y_expect = 230 * (int)U.pixelsize;
|
||||
/* don't cover the header text */
|
||||
if (ibuf_template->x == x_expect && ibuf_template->y == y_expect) {
|
||||
memcpy(ibuf->rect, ibuf_template->rect, ibuf_template->x * ibuf_template->y * sizeof(char[4]));
|
||||
}
|
||||
else {
|
||||
printf("Splash expected %dx%d found %dx%d, ignoring: %s\n",
|
||||
x_expect, y_expect, ibuf_template->x, ibuf_template->y, splash_filepath);
|
||||
}
|
||||
IMB_freeImBuf(ibuf_template);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
block = UI_block_begin(C, ar, "_popup", UI_EMBOSS);
|
||||
|
@ -36,8 +36,8 @@ struct wmOperatorType;
|
||||
/* wm_files.c */
|
||||
void wm_history_file_read(void);
|
||||
int wm_homefile_read(
|
||||
struct bContext *C, struct ReportList *reports,
|
||||
bool use_factory_settings, const char *filepath_startup_override);
|
||||
struct bContext *C, struct ReportList *reports, bool use_factory_settings,
|
||||
const char *filepath_startup_override, const char *app_template_override);
|
||||
void wm_file_read_report(bContext *C);
|
||||
|
||||
void WM_OT_save_homefile(struct wmOperatorType *ot);
|
||||
|
Loading…
Reference in New Issue
Block a user