Merge branch 'master' into blender2.8

This commit is contained in:
Campbell Barton 2017-03-25 13:49:13 +11:00
commit f730e386eb
28 changed files with 1247 additions and 150 deletions

View 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)

View 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()

View 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)

View File

@ -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.

View File

@ -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,
)
)

View File

@ -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):

View File

@ -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,
)
)

View File

@ -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):

View File

@ -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,
)
)

View File

@ -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,
)
)

View File

@ -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,
)
)

View File

@ -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,
)
)

View File

@ -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,
)
)

View File

@ -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,
)
)

View File

@ -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'

View File

@ -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,

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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");

View File

@ -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 */
}

View File

@ -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);

View File

@ -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);

View File

@ -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);