2009-11-04 20:50:09 +00:00
|
|
|
# ##### 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.
|
2009-11-21 23:55:14 +00:00
|
|
|
#
|
2009-11-04 20:50:09 +00:00
|
|
|
# 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.
|
2009-11-21 23:55:14 +00:00
|
|
|
#
|
2009-11-04 20:50:09 +00:00
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with this program; if not, write to the Free Software Foundation,
|
2010-02-12 13:34:04 +00:00
|
|
|
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
2009-11-04 20:50:09 +00:00
|
|
|
#
|
|
|
|
# ##### END GPL LICENSE BLOCK #####
|
|
|
|
|
2010-06-09 19:12:03 +00:00
|
|
|
# <pep8 compliant>
|
2009-11-04 20:50:09 +00:00
|
|
|
|
2009-11-04 20:21:08 +00:00
|
|
|
import bpy
|
2018-06-07 16:01:57 +02:00
|
|
|
from bpy.types import (
|
2018-09-18 17:44:14 +02:00
|
|
|
Menu,
|
2018-06-07 16:01:57 +02:00
|
|
|
Operator,
|
|
|
|
)
|
2015-01-27 17:46:07 +11:00
|
|
|
from bpy.props import (
|
2017-03-25 11:07:05 +11:00
|
|
|
BoolProperty,
|
|
|
|
EnumProperty,
|
2017-11-29 18:00:41 +11:00
|
|
|
FloatProperty,
|
|
|
|
IntProperty,
|
|
|
|
StringProperty,
|
2017-03-25 11:07:05 +11:00
|
|
|
)
|
2011-06-27 07:51:52 +00:00
|
|
|
|
2018-11-15 17:12:33 +11:00
|
|
|
# FIXME, we need a way to detect key repeat events.
|
|
|
|
# unfortunately checking event previous values isn't reliable.
|
|
|
|
use_toolbar_release_hack = True
|
|
|
|
|
2012-01-01 08:52:54 +00:00
|
|
|
|
2011-08-18 12:20:10 +00:00
|
|
|
rna_path_prop = StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Context Attributes",
|
|
|
|
description="RNA context string",
|
|
|
|
maxlen=1024,
|
|
|
|
)
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2011-08-18 12:20:10 +00:00
|
|
|
rna_reverse_prop = BoolProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Reverse",
|
|
|
|
description="Cycle backwards",
|
|
|
|
default=False,
|
|
|
|
)
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2015-12-18 16:17:13 +11:00
|
|
|
rna_wrap_prop = BoolProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Wrap",
|
|
|
|
description="Wrap back to the first/last values",
|
|
|
|
default=False,
|
|
|
|
)
|
2015-12-18 16:17:13 +11:00
|
|
|
|
2011-08-18 12:20:10 +00:00
|
|
|
rna_relative_prop = BoolProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Relative",
|
|
|
|
description="Apply relative to the current value (delta)",
|
|
|
|
default=False,
|
|
|
|
)
|
2010-01-31 14:46:28 +00:00
|
|
|
|
2018-05-30 22:15:10 +02:00
|
|
|
rna_space_type_prop = EnumProperty(
|
2018-06-26 22:56:39 +02:00
|
|
|
name="Type",
|
|
|
|
items=tuple(
|
|
|
|
(e.identifier, e.name, "", e. value)
|
|
|
|
for e in bpy.types.Space.bl_rna.properties["type"].enum_items
|
|
|
|
),
|
|
|
|
default='EMPTY',
|
|
|
|
)
|
2018-05-30 22:15:10 +02:00
|
|
|
|
2009-12-13 13:59:16 +00:00
|
|
|
|
2010-06-14 03:52:10 +00:00
|
|
|
def context_path_validate(context, data_path):
|
2009-11-04 20:21:08 +00:00
|
|
|
try:
|
2011-02-19 04:28:07 +00:00
|
|
|
value = eval("context.%s" % data_path) if data_path else Ellipsis
|
2018-02-01 13:58:44 +11:00
|
|
|
except AttributeError as ex:
|
|
|
|
if str(ex).startswith("'NoneType'"):
|
2009-11-04 20:21:08 +00:00
|
|
|
# One of the items in the rna path is None, just ignore this
|
2009-12-14 01:58:08 +00:00
|
|
|
value = Ellipsis
|
2009-11-04 20:21:08 +00:00
|
|
|
else:
|
2011-10-17 06:58:07 +00:00
|
|
|
# We have a real error in the rna path, don't ignore that
|
2009-11-04 20:21:08 +00:00
|
|
|
raise
|
|
|
|
|
|
|
|
return value
|
|
|
|
|
|
|
|
|
2011-08-18 16:01:11 +00:00
|
|
|
def operator_value_is_undo(value):
|
|
|
|
if value in {None, Ellipsis}:
|
|
|
|
return False
|
|
|
|
|
|
|
|
# typical properties or objects
|
|
|
|
id_data = getattr(value, "id_data", Ellipsis)
|
|
|
|
|
|
|
|
if id_data is None:
|
|
|
|
return False
|
|
|
|
elif id_data is Ellipsis:
|
|
|
|
# handle mathutils types
|
|
|
|
id_data = getattr(getattr(value, "owner", None), "id_data", None)
|
|
|
|
|
|
|
|
if id_data is None:
|
|
|
|
return False
|
|
|
|
|
|
|
|
# return True if its a non window ID type
|
|
|
|
return (isinstance(id_data, bpy.types.ID) and
|
|
|
|
(not isinstance(id_data, (bpy.types.WindowManager,
|
|
|
|
bpy.types.Screen,
|
|
|
|
bpy.types.Brush,
|
|
|
|
))))
|
|
|
|
|
|
|
|
|
|
|
|
def operator_path_is_undo(context, data_path):
|
|
|
|
# note that if we have data paths that use strings this could fail
|
2011-10-17 06:58:07 +00:00
|
|
|
# luckily we don't do this!
|
2011-08-18 16:01:11 +00:00
|
|
|
#
|
2018-09-03 16:49:08 +02:00
|
|
|
# When we can't find the data owner assume no undo is needed.
|
2011-11-16 16:38:37 +00:00
|
|
|
data_path_head = data_path.rpartition(".")[0]
|
2011-08-18 16:01:11 +00:00
|
|
|
|
|
|
|
if not data_path_head:
|
|
|
|
return False
|
|
|
|
|
|
|
|
value = context_path_validate(context, data_path_head)
|
|
|
|
|
|
|
|
return operator_value_is_undo(value)
|
|
|
|
|
|
|
|
|
|
|
|
def operator_path_undo_return(context, data_path):
|
|
|
|
return {'FINISHED'} if operator_path_is_undo(context, data_path) else {'CANCELLED'}
|
|
|
|
|
|
|
|
|
|
|
|
def operator_value_undo_return(value):
|
|
|
|
return {'FINISHED'} if operator_value_is_undo(value) else {'CANCELLED'}
|
|
|
|
|
|
|
|
|
2009-11-04 20:21:08 +00:00
|
|
|
def execute_context_assign(self, context):
|
2011-08-18 15:25:18 +00:00
|
|
|
data_path = self.data_path
|
|
|
|
if context_path_validate(context, data_path) is Ellipsis:
|
2009-12-24 19:50:43 +00:00
|
|
|
return {'PASS_THROUGH'}
|
2010-01-04 08:24:24 +00:00
|
|
|
|
2010-09-09 18:03:57 +00:00
|
|
|
if getattr(self, "relative", False):
|
2011-08-18 15:25:18 +00:00
|
|
|
exec("context.%s += self.value" % data_path)
|
2010-01-04 08:24:24 +00:00
|
|
|
else:
|
2011-08-18 15:25:18 +00:00
|
|
|
exec("context.%s = self.value" % data_path)
|
2010-01-04 08:24:24 +00:00
|
|
|
|
2011-08-18 16:01:11 +00:00
|
|
|
return operator_path_undo_return(context, data_path)
|
2009-11-04 20:21:08 +00:00
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_context_set_boolean(Operator):
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Set a context value"""
|
2009-11-04 20:21:08 +00:00
|
|
|
bl_idname = "wm.context_set_boolean"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Context Set Boolean"
|
2011-06-06 11:56:54 +00:00
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path: rna_path_prop
|
|
|
|
value: BoolProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Value",
|
|
|
|
description="Assignment value",
|
|
|
|
default=True,
|
|
|
|
)
|
2009-11-04 20:21:08 +00:00
|
|
|
|
|
|
|
execute = execute_context_assign
|
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_context_set_int(Operator): # same as enum
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Set a context value"""
|
2009-11-04 20:21:08 +00:00
|
|
|
bl_idname = "wm.context_set_int"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Context Set"
|
2011-06-06 11:56:54 +00:00
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path: rna_path_prop
|
|
|
|
value: IntProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Value",
|
|
|
|
description="Assign value",
|
|
|
|
default=0,
|
|
|
|
)
|
2018-07-11 22:18:09 +02:00
|
|
|
relative: rna_relative_prop
|
2009-11-04 20:21:08 +00:00
|
|
|
|
|
|
|
execute = execute_context_assign
|
|
|
|
|
|
|
|
|
2014-02-12 15:31:23 +11:00
|
|
|
class WM_OT_context_scale_float(Operator):
|
|
|
|
"""Scale a float context value"""
|
|
|
|
bl_idname = "wm.context_scale_float"
|
|
|
|
bl_label = "Context Scale Float"
|
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path: rna_path_prop
|
|
|
|
value: FloatProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Value",
|
|
|
|
description="Assign value",
|
|
|
|
default=1.0,
|
|
|
|
)
|
2014-02-12 15:31:23 +11:00
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
data_path = self.data_path
|
|
|
|
if context_path_validate(context, data_path) is Ellipsis:
|
|
|
|
return {'PASS_THROUGH'}
|
|
|
|
|
|
|
|
value = self.value
|
|
|
|
|
|
|
|
if value == 1.0: # nothing to do
|
|
|
|
return {'CANCELLED'}
|
|
|
|
|
|
|
|
exec("context.%s *= value" % data_path)
|
|
|
|
|
|
|
|
return operator_path_undo_return(context, data_path)
|
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_context_scale_int(Operator):
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Scale an int context value"""
|
2010-03-08 23:34:38 +00:00
|
|
|
bl_idname = "wm.context_scale_int"
|
2014-02-12 15:31:23 +11:00
|
|
|
bl_label = "Context Scale Int"
|
2011-06-06 11:56:54 +00:00
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2010-03-08 23:34:38 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path: rna_path_prop
|
|
|
|
value: FloatProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Value",
|
|
|
|
description="Assign value",
|
|
|
|
default=1.0,
|
|
|
|
)
|
2018-07-11 22:18:09 +02:00
|
|
|
always_step: BoolProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Always Step",
|
|
|
|
description="Always adjust the value by a minimum of 1 when 'value' is not 1.0",
|
|
|
|
default=True,
|
|
|
|
)
|
2010-03-08 23:34:38 +00:00
|
|
|
|
|
|
|
def execute(self, context):
|
2011-08-18 15:25:18 +00:00
|
|
|
data_path = self.data_path
|
|
|
|
if context_path_validate(context, data_path) is Ellipsis:
|
2010-03-08 23:34:38 +00:00
|
|
|
return {'PASS_THROUGH'}
|
|
|
|
|
2010-09-09 18:03:57 +00:00
|
|
|
value = self.value
|
2010-03-08 23:34:38 +00:00
|
|
|
|
2010-09-07 15:17:42 +00:00
|
|
|
if value == 1.0: # nothing to do
|
2010-03-08 23:34:38 +00:00
|
|
|
return {'CANCELLED'}
|
|
|
|
|
2010-09-09 18:03:57 +00:00
|
|
|
if getattr(self, "always_step", False):
|
2010-03-08 23:34:38 +00:00
|
|
|
if value > 1.0:
|
|
|
|
add = "1"
|
|
|
|
func = "max"
|
|
|
|
else:
|
|
|
|
add = "-1"
|
|
|
|
func = "min"
|
2011-08-18 12:20:10 +00:00
|
|
|
exec("context.%s = %s(round(context.%s * value), context.%s + %s)" %
|
|
|
|
(data_path, func, data_path, data_path, add))
|
2010-03-08 23:34:38 +00:00
|
|
|
else:
|
2011-08-18 15:25:18 +00:00
|
|
|
exec("context.%s *= value" % data_path)
|
2010-03-08 23:34:38 +00:00
|
|
|
|
2011-08-18 16:01:11 +00:00
|
|
|
return operator_path_undo_return(context, data_path)
|
2010-03-08 23:34:38 +00:00
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_context_set_float(Operator): # same as enum
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Set a context value"""
|
2009-11-17 16:04:17 +00:00
|
|
|
bl_idname = "wm.context_set_float"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Context Set Float"
|
2011-06-06 11:56:54 +00:00
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path: rna_path_prop
|
|
|
|
value: FloatProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Value",
|
|
|
|
description="Assignment value",
|
|
|
|
default=0.0,
|
|
|
|
)
|
2018-07-11 22:18:09 +02:00
|
|
|
relative: rna_relative_prop
|
2009-11-04 20:21:08 +00:00
|
|
|
|
|
|
|
execute = execute_context_assign
|
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_context_set_string(Operator): # same as enum
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Set a context value"""
|
2009-11-04 20:21:08 +00:00
|
|
|
bl_idname = "wm.context_set_string"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Context Set String"
|
2011-06-06 11:56:54 +00:00
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path: rna_path_prop
|
|
|
|
value: StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Value",
|
|
|
|
description="Assign value",
|
|
|
|
maxlen=1024,
|
|
|
|
)
|
2009-11-04 20:21:08 +00:00
|
|
|
|
|
|
|
execute = execute_context_assign
|
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_context_set_enum(Operator):
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Set a context value"""
|
2009-11-04 20:21:08 +00:00
|
|
|
bl_idname = "wm.context_set_enum"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Context Set Enum"
|
2011-06-06 11:56:54 +00:00
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path: rna_path_prop
|
|
|
|
value: StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Value",
|
|
|
|
description="Assignment value (as a string)",
|
|
|
|
maxlen=1024,
|
|
|
|
)
|
2009-11-04 20:21:08 +00:00
|
|
|
|
|
|
|
execute = execute_context_assign
|
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_context_set_value(Operator):
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Set a context value"""
|
2009-12-22 16:11:11 +00:00
|
|
|
bl_idname = "wm.context_set_value"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Context Set Value"
|
2011-06-06 11:56:54 +00:00
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2009-12-22 16:11:11 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path: rna_path_prop
|
|
|
|
value: StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Value",
|
|
|
|
description="Assignment value (as a string)",
|
|
|
|
maxlen=1024,
|
|
|
|
)
|
2009-12-22 16:11:11 +00:00
|
|
|
|
|
|
|
def execute(self, context):
|
2011-08-18 15:25:18 +00:00
|
|
|
data_path = self.data_path
|
|
|
|
if context_path_validate(context, data_path) is Ellipsis:
|
2009-12-24 19:50:43 +00:00
|
|
|
return {'PASS_THROUGH'}
|
2011-08-18 15:25:18 +00:00
|
|
|
exec("context.%s = %s" % (data_path, self.value))
|
2011-08-18 16:01:11 +00:00
|
|
|
return operator_path_undo_return(context, data_path)
|
2009-12-22 16:11:11 +00:00
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_context_toggle(Operator):
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Toggle a context value"""
|
2009-11-04 20:21:08 +00:00
|
|
|
bl_idname = "wm.context_toggle"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Context Toggle"
|
2011-06-06 11:56:54 +00:00
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2009-12-22 16:11:11 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path: rna_path_prop
|
2009-11-04 20:21:08 +00:00
|
|
|
|
|
|
|
def execute(self, context):
|
2011-08-18 15:25:18 +00:00
|
|
|
data_path = self.data_path
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2011-08-18 15:25:18 +00:00
|
|
|
if context_path_validate(context, data_path) is Ellipsis:
|
2009-12-24 19:50:43 +00:00
|
|
|
return {'PASS_THROUGH'}
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2011-08-18 15:25:18 +00:00
|
|
|
exec("context.%s = not (context.%s)" % (data_path, data_path))
|
2009-12-13 13:59:16 +00:00
|
|
|
|
2011-08-18 16:01:11 +00:00
|
|
|
return operator_path_undo_return(context, data_path)
|
2009-11-04 20:21:08 +00:00
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_context_toggle_enum(Operator):
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Toggle a context value"""
|
2009-11-04 20:21:08 +00:00
|
|
|
bl_idname = "wm.context_toggle_enum"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Context Toggle Values"
|
2011-06-06 11:56:54 +00:00
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path: rna_path_prop
|
|
|
|
value_1: StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Value",
|
|
|
|
description="Toggle enum",
|
|
|
|
maxlen=1024,
|
|
|
|
)
|
2018-07-11 22:18:09 +02:00
|
|
|
value_2: StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Value",
|
|
|
|
description="Toggle enum",
|
|
|
|
maxlen=1024,
|
|
|
|
)
|
2009-11-04 20:21:08 +00:00
|
|
|
|
|
|
|
def execute(self, context):
|
2011-08-18 15:25:18 +00:00
|
|
|
data_path = self.data_path
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2011-08-18 15:25:18 +00:00
|
|
|
if context_path_validate(context, data_path) is Ellipsis:
|
2009-12-24 19:50:43 +00:00
|
|
|
return {'PASS_THROUGH'}
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2014-01-11 00:59:22 +01:00
|
|
|
# failing silently is not ideal, but we don't want errors for shortcut
|
|
|
|
# keys that some values that are only available in a particular context
|
|
|
|
try:
|
|
|
|
exec("context.%s = ('%s', '%s')[context.%s != '%s']" %
|
|
|
|
(data_path, self.value_1,
|
|
|
|
self.value_2, data_path,
|
|
|
|
self.value_2,
|
|
|
|
))
|
|
|
|
except:
|
|
|
|
return {'PASS_THROUGH'}
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2011-08-18 16:01:11 +00:00
|
|
|
return operator_path_undo_return(context, data_path)
|
2009-11-04 20:21:08 +00:00
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_context_cycle_int(Operator):
|
2012-07-07 13:51:07 +00:00
|
|
|
"""Set a context value (useful for cycling active material, """ \
|
2018-06-26 19:41:37 +02:00
|
|
|
"""vertex keys, groups, etc.)"""
|
2009-11-04 20:21:08 +00:00
|
|
|
bl_idname = "wm.context_cycle_int"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Context Int Cycle"
|
2011-06-06 11:56:54 +00:00
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2009-12-22 16:11:11 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path: rna_path_prop
|
|
|
|
reverse: rna_reverse_prop
|
|
|
|
wrap: rna_wrap_prop
|
2009-11-04 20:21:08 +00:00
|
|
|
|
|
|
|
def execute(self, context):
|
2010-09-09 18:03:57 +00:00
|
|
|
data_path = self.data_path
|
2010-06-14 03:52:10 +00:00
|
|
|
value = context_path_validate(context, data_path)
|
2009-12-14 01:58:08 +00:00
|
|
|
if value is Ellipsis:
|
2009-12-24 19:50:43 +00:00
|
|
|
return {'PASS_THROUGH'}
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2010-09-09 18:03:57 +00:00
|
|
|
if self.reverse:
|
2010-01-03 02:24:53 +00:00
|
|
|
value -= 1
|
2009-11-04 20:21:08 +00:00
|
|
|
else:
|
2010-01-03 02:24:53 +00:00
|
|
|
value += 1
|
|
|
|
|
2011-08-18 15:25:18 +00:00
|
|
|
exec("context.%s = value" % data_path)
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2015-12-18 16:17:13 +11:00
|
|
|
if self.wrap:
|
|
|
|
if value != eval("context.%s" % data_path):
|
|
|
|
# relies on rna clamping integers out of the range
|
|
|
|
if self.reverse:
|
|
|
|
value = (1 << 31) - 1
|
|
|
|
else:
|
|
|
|
value = -1 << 31
|
2010-01-03 02:24:53 +00:00
|
|
|
|
2015-12-18 16:17:13 +11:00
|
|
|
exec("context.%s = value" % data_path)
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2011-08-18 16:01:11 +00:00
|
|
|
return operator_path_undo_return(context, data_path)
|
2009-11-04 20:21:08 +00:00
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_context_cycle_enum(Operator):
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Toggle a context value"""
|
2009-11-04 20:21:08 +00:00
|
|
|
bl_idname = "wm.context_cycle_enum"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Context Enum Cycle"
|
2011-06-06 11:56:54 +00:00
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path: rna_path_prop
|
|
|
|
reverse: rna_reverse_prop
|
|
|
|
wrap: rna_wrap_prop
|
2009-11-04 20:21:08 +00:00
|
|
|
|
|
|
|
def execute(self, context):
|
2011-08-18 15:25:18 +00:00
|
|
|
data_path = self.data_path
|
|
|
|
value = context_path_validate(context, data_path)
|
2009-12-14 01:58:08 +00:00
|
|
|
if value is Ellipsis:
|
2009-12-24 19:50:43 +00:00
|
|
|
return {'PASS_THROUGH'}
|
2009-11-04 20:21:08 +00:00
|
|
|
|
|
|
|
orig_value = value
|
|
|
|
|
|
|
|
# Have to get rna enum values
|
2011-08-18 15:25:18 +00:00
|
|
|
rna_struct_str, rna_prop_str = data_path.rsplit('.', 1)
|
2009-11-04 20:21:08 +00:00
|
|
|
i = rna_prop_str.find('[')
|
|
|
|
|
2011-10-17 06:58:07 +00:00
|
|
|
# just in case we get "context.foo.bar[0]"
|
2009-11-04 20:21:08 +00:00
|
|
|
if i != -1:
|
|
|
|
rna_prop_str = rna_prop_str[0:i]
|
|
|
|
|
|
|
|
rna_struct = eval("context.%s.rna_type" % rna_struct_str)
|
|
|
|
|
|
|
|
rna_prop = rna_struct.properties[rna_prop_str]
|
|
|
|
|
|
|
|
if type(rna_prop) != bpy.types.EnumProperty:
|
|
|
|
raise Exception("expected an enum property")
|
|
|
|
|
2011-04-03 22:28:33 +00:00
|
|
|
enums = rna_struct.properties[rna_prop_str].enum_items.keys()
|
2009-11-04 20:21:08 +00:00
|
|
|
orig_index = enums.index(orig_value)
|
|
|
|
|
2015-12-18 16:17:13 +11:00
|
|
|
# Have the info we need, advance to the next item.
|
|
|
|
#
|
|
|
|
# When wrap's disabled we may set the value to its self,
|
|
|
|
# this is done to ensure update callbacks run.
|
2010-09-09 18:03:57 +00:00
|
|
|
if self.reverse:
|
2009-11-04 20:21:08 +00:00
|
|
|
if orig_index == 0:
|
2015-12-18 16:17:13 +11:00
|
|
|
advance_enum = enums[-1] if self.wrap else enums[0]
|
2009-11-04 20:21:08 +00:00
|
|
|
else:
|
2010-09-07 15:17:42 +00:00
|
|
|
advance_enum = enums[orig_index - 1]
|
2009-11-04 20:21:08 +00:00
|
|
|
else:
|
|
|
|
if orig_index == len(enums) - 1:
|
2015-12-18 16:17:13 +11:00
|
|
|
advance_enum = enums[0] if self.wrap else enums[-1]
|
2009-11-04 20:21:08 +00:00
|
|
|
else:
|
|
|
|
advance_enum = enums[orig_index + 1]
|
|
|
|
|
|
|
|
# set the new value
|
2011-08-18 15:25:18 +00:00
|
|
|
exec("context.%s = advance_enum" % data_path)
|
2011-08-18 16:01:11 +00:00
|
|
|
return operator_path_undo_return(context, data_path)
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2010-08-04 12:18:07 +00:00
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_context_cycle_array(Operator):
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Set a context array value """ \
|
2018-06-26 19:41:37 +02:00
|
|
|
"""(useful for cycling the active mesh edit mode)"""
|
2010-09-06 22:43:09 +00:00
|
|
|
bl_idname = "wm.context_cycle_array"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Context Array Cycle"
|
2011-06-06 11:56:54 +00:00
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2010-09-06 22:43:09 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path: rna_path_prop
|
|
|
|
reverse: rna_reverse_prop
|
2010-09-06 22:43:09 +00:00
|
|
|
|
|
|
|
def execute(self, context):
|
2010-09-09 18:03:57 +00:00
|
|
|
data_path = self.data_path
|
2010-09-06 22:43:09 +00:00
|
|
|
value = context_path_validate(context, data_path)
|
|
|
|
if value is Ellipsis:
|
|
|
|
return {'PASS_THROUGH'}
|
|
|
|
|
|
|
|
def cycle(array):
|
2010-09-09 18:03:57 +00:00
|
|
|
if self.reverse:
|
2010-09-06 22:43:09 +00:00
|
|
|
array.insert(0, array.pop())
|
|
|
|
else:
|
|
|
|
array.append(array.pop(0))
|
|
|
|
return array
|
|
|
|
|
2011-08-18 15:25:18 +00:00
|
|
|
exec("context.%s = cycle(context.%s[:])" % (data_path, data_path))
|
2010-09-06 22:43:09 +00:00
|
|
|
|
2011-08-18 16:01:11 +00:00
|
|
|
return operator_path_undo_return(context, data_path)
|
2010-09-06 22:43:09 +00:00
|
|
|
|
|
|
|
|
2013-07-06 03:04:48 +00:00
|
|
|
class WM_OT_context_menu_enum(Operator):
|
|
|
|
bl_idname = "wm.context_menu_enum"
|
|
|
|
bl_label = "Context Enum Menu"
|
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2018-07-11 22:18:09 +02:00
|
|
|
|
|
|
|
data_path: rna_path_prop
|
2011-02-16 02:25:03 +00:00
|
|
|
|
2013-07-06 03:04:48 +00:00
|
|
|
def execute(self, context):
|
2011-02-15 18:12:41 +00:00
|
|
|
data_path = self.data_path
|
2013-01-13 16:25:46 +00:00
|
|
|
value = context_path_validate(context, data_path)
|
2013-07-06 03:04:48 +00:00
|
|
|
|
2011-02-15 18:12:41 +00:00
|
|
|
if value is Ellipsis:
|
|
|
|
return {'PASS_THROUGH'}
|
2013-07-06 03:04:48 +00:00
|
|
|
|
2011-02-15 18:12:41 +00:00
|
|
|
base_path, prop_string = data_path.rsplit(".", 1)
|
|
|
|
value_base = context_path_validate(context, base_path)
|
2013-01-17 00:29:39 +00:00
|
|
|
prop = value_base.bl_rna.properties[prop_string]
|
|
|
|
|
2013-07-06 03:04:48 +00:00
|
|
|
def draw_cb(self, context):
|
|
|
|
layout = self.layout
|
|
|
|
layout.prop(value_base, prop_string, expand=True)
|
2011-02-15 18:12:41 +00:00
|
|
|
|
2013-07-06 03:04:48 +00:00
|
|
|
context.window_manager.popup_menu(draw_func=draw_cb, title=prop.name, icon=prop.icon)
|
2011-02-15 18:12:41 +00:00
|
|
|
|
Pie Menus C code backend.
This commit merges the code in the pie-menu branch.
As per decisions taken the last few days, there are no pie menus
included and there will be an official add-on including overrides of
some keys with pie menus. However, people will now be able to use the
new code in python.
Full Documentation is in http://wiki.blender.org/index.php/Dev:Ref/
Thanks:
Campbell Barton, Dalai Felinto and Ton Roosendaal for the code review
and design comments
Jonathan Williamson, Pawel Lyczkowski, Pablo Vazquez among others for
suggestions during the development.
Special Thanks to Sean Olson, for his support, suggestions, testing and
merciless bugging so that I would finish the pie menu code. Without him
we wouldn't be here. Also to the rest of the developers of the original
python add-on, Patrick Moore and Dan Eicher and finally to Matt Ebb, who
did the research and first implementation and whose code I used to get
started.
2014-08-11 10:39:59 +02:00
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
|
|
|
|
|
|
class WM_OT_context_pie_enum(Operator):
|
|
|
|
bl_idname = "wm.context_pie_enum"
|
|
|
|
bl_label = "Context Enum Pie"
|
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2018-07-11 22:18:09 +02:00
|
|
|
|
|
|
|
data_path: rna_path_prop
|
Pie Menus C code backend.
This commit merges the code in the pie-menu branch.
As per decisions taken the last few days, there are no pie menus
included and there will be an official add-on including overrides of
some keys with pie menus. However, people will now be able to use the
new code in python.
Full Documentation is in http://wiki.blender.org/index.php/Dev:Ref/
Thanks:
Campbell Barton, Dalai Felinto and Ton Roosendaal for the code review
and design comments
Jonathan Williamson, Pawel Lyczkowski, Pablo Vazquez among others for
suggestions during the development.
Special Thanks to Sean Olson, for his support, suggestions, testing and
merciless bugging so that I would finish the pie menu code. Without him
we wouldn't be here. Also to the rest of the developers of the original
python add-on, Patrick Moore and Dan Eicher and finally to Matt Ebb, who
did the research and first implementation and whose code I used to get
started.
2014-08-11 10:39:59 +02:00
|
|
|
|
|
|
|
def invoke(self, context, event):
|
2014-08-14 07:29:38 +10:00
|
|
|
wm = context.window_manager
|
Pie Menus C code backend.
This commit merges the code in the pie-menu branch.
As per decisions taken the last few days, there are no pie menus
included and there will be an official add-on including overrides of
some keys with pie menus. However, people will now be able to use the
new code in python.
Full Documentation is in http://wiki.blender.org/index.php/Dev:Ref/
Thanks:
Campbell Barton, Dalai Felinto and Ton Roosendaal for the code review
and design comments
Jonathan Williamson, Pawel Lyczkowski, Pablo Vazquez among others for
suggestions during the development.
Special Thanks to Sean Olson, for his support, suggestions, testing and
merciless bugging so that I would finish the pie menu code. Without him
we wouldn't be here. Also to the rest of the developers of the original
python add-on, Patrick Moore and Dan Eicher and finally to Matt Ebb, who
did the research and first implementation and whose code I used to get
started.
2014-08-11 10:39:59 +02:00
|
|
|
data_path = self.data_path
|
|
|
|
value = context_path_validate(context, data_path)
|
|
|
|
|
|
|
|
if value is Ellipsis:
|
|
|
|
return {'PASS_THROUGH'}
|
|
|
|
|
|
|
|
base_path, prop_string = data_path.rsplit(".", 1)
|
|
|
|
value_base = context_path_validate(context, base_path)
|
|
|
|
prop = value_base.bl_rna.properties[prop_string]
|
|
|
|
|
|
|
|
def draw_cb(self, context):
|
|
|
|
layout = self.layout
|
|
|
|
layout.prop(value_base, prop_string, expand=True)
|
|
|
|
|
2014-08-14 07:29:38 +10:00
|
|
|
wm.popup_menu_pie(draw_func=draw_cb, title=prop.name, icon=prop.icon, event=event)
|
Pie Menus C code backend.
This commit merges the code in the pie-menu branch.
As per decisions taken the last few days, there are no pie menus
included and there will be an official add-on including overrides of
some keys with pie menus. However, people will now be able to use the
new code in python.
Full Documentation is in http://wiki.blender.org/index.php/Dev:Ref/
Thanks:
Campbell Barton, Dalai Felinto and Ton Roosendaal for the code review
and design comments
Jonathan Williamson, Pawel Lyczkowski, Pablo Vazquez among others for
suggestions during the development.
Special Thanks to Sean Olson, for his support, suggestions, testing and
merciless bugging so that I would finish the pie menu code. Without him
we wouldn't be here. Also to the rest of the developers of the original
python add-on, Patrick Moore and Dan Eicher and finally to Matt Ebb, who
did the research and first implementation and whose code I used to get
started.
2014-08-11 10:39:59 +02:00
|
|
|
|
|
|
|
return {'FINISHED'}
|
2011-02-15 18:12:41 +00:00
|
|
|
|
|
|
|
|
2014-08-13 14:04:37 +02:00
|
|
|
class WM_OT_operator_pie_enum(Operator):
|
|
|
|
bl_idname = "wm.operator_pie_enum"
|
|
|
|
bl_label = "Operator Enum Pie"
|
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2018-07-11 22:18:09 +02:00
|
|
|
|
|
|
|
data_path: StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Operator",
|
|
|
|
description="Operator name (in python as string)",
|
|
|
|
maxlen=1024,
|
|
|
|
)
|
2018-07-11 22:18:09 +02:00
|
|
|
prop_string: StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Property",
|
|
|
|
description="Property name (as a string)",
|
|
|
|
maxlen=1024,
|
|
|
|
)
|
2014-08-13 14:04:37 +02:00
|
|
|
|
|
|
|
def invoke(self, context, event):
|
2014-08-14 07:29:38 +10:00
|
|
|
wm = context.window_manager
|
2014-08-13 14:04:37 +02:00
|
|
|
|
2014-08-14 07:29:38 +10:00
|
|
|
data_path = self.data_path
|
|
|
|
prop_string = self.prop_string
|
2014-08-13 14:04:37 +02:00
|
|
|
|
2014-08-14 07:29:38 +10:00
|
|
|
# same as eval("bpy.ops." + data_path)
|
|
|
|
op_mod_str, ob_id_str = data_path.split(".", 1)
|
|
|
|
op = getattr(getattr(bpy.ops, op_mod_str), ob_id_str)
|
|
|
|
del op_mod_str, ob_id_str
|
|
|
|
|
|
|
|
try:
|
2018-09-13 18:16:06 +10:00
|
|
|
op_rna = op.get_rna_type()
|
2014-08-14 07:29:38 +10:00
|
|
|
except KeyError:
|
|
|
|
self.report({'ERROR'}, "Operator not found: bpy.ops.%s" % data_path)
|
|
|
|
return {'CANCELLED'}
|
2014-08-13 14:04:37 +02:00
|
|
|
|
|
|
|
def draw_cb(self, context):
|
|
|
|
layout = self.layout
|
|
|
|
pie = layout.menu_pie()
|
2014-08-14 07:29:38 +10:00
|
|
|
pie.operator_enum(data_path, prop_string)
|
2014-08-13 14:04:37 +02:00
|
|
|
|
2018-09-13 18:16:06 +10:00
|
|
|
wm.popup_menu_pie(draw_func=draw_cb, title=op_rna.name, event=event)
|
2014-08-13 14:04:37 +02:00
|
|
|
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_context_set_id(Operator):
|
2013-09-30 05:50:41 +00:00
|
|
|
"""Set a context value to an ID data-block"""
|
2010-08-04 12:18:07 +00:00
|
|
|
bl_idname = "wm.context_set_id"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Set Library ID"
|
2011-06-06 11:56:54 +00:00
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2010-08-04 12:18:07 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path: rna_path_prop
|
|
|
|
value: StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Value",
|
|
|
|
description="Assign value",
|
|
|
|
maxlen=1024,
|
|
|
|
)
|
2010-08-04 12:18:07 +00:00
|
|
|
|
|
|
|
def execute(self, context):
|
2010-09-09 18:03:57 +00:00
|
|
|
value = self.value
|
|
|
|
data_path = self.data_path
|
2010-08-04 13:59:25 +00:00
|
|
|
|
|
|
|
# match the pointer type from the target property to bpy.data.*
|
|
|
|
# so we lookup the correct list.
|
|
|
|
data_path_base, data_path_prop = data_path.rsplit(".", 1)
|
|
|
|
data_prop_rna = eval("context.%s" % data_path_base).rna_type.properties[data_path_prop]
|
|
|
|
data_prop_rna_type = data_prop_rna.fixed_type
|
|
|
|
|
|
|
|
id_iter = None
|
|
|
|
|
|
|
|
for prop in bpy.data.rna_type.properties:
|
|
|
|
if prop.rna_type.identifier == "CollectionProperty":
|
|
|
|
if prop.fixed_type == data_prop_rna_type:
|
|
|
|
id_iter = prop.identifier
|
|
|
|
break
|
|
|
|
|
|
|
|
if id_iter:
|
|
|
|
value_id = getattr(bpy.data, id_iter).get(value)
|
2011-08-18 15:25:18 +00:00
|
|
|
exec("context.%s = value_id" % data_path)
|
2010-08-04 12:18:07 +00:00
|
|
|
|
2011-08-18 16:01:11 +00:00
|
|
|
return operator_path_undo_return(context, data_path)
|
2010-08-04 12:18:07 +00:00
|
|
|
|
|
|
|
|
2011-08-18 12:20:10 +00:00
|
|
|
doc_id = StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Doc ID",
|
|
|
|
maxlen=1024,
|
|
|
|
options={'HIDDEN'},
|
|
|
|
)
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2011-06-27 07:51:52 +00:00
|
|
|
data_path_iter = StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
description="The data path relative to the context, must point to an iterable")
|
2011-06-27 07:51:52 +00:00
|
|
|
|
|
|
|
data_path_item = StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
description="The data path from each iterable to the value (int or float)")
|
2011-06-27 07:51:52 +00:00
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_context_collection_boolean_set(Operator):
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Set boolean values for a collection of items"""
|
2011-06-27 07:51:52 +00:00
|
|
|
bl_idname = "wm.context_collection_boolean_set"
|
|
|
|
bl_label = "Context Collection Boolean Set"
|
|
|
|
bl_options = {'UNDO', 'REGISTER', 'INTERNAL'}
|
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path_iter: data_path_iter
|
|
|
|
data_path_item: data_path_item
|
2011-06-27 07:51:52 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
type: EnumProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Type",
|
2019-03-14 11:08:48 +11:00
|
|
|
items=(
|
|
|
|
('TOGGLE', "Toggle", ""),
|
|
|
|
('ENABLE', "Enable", ""),
|
|
|
|
('DISABLE', "Disable", ""),
|
|
|
|
),
|
2018-06-26 19:41:37 +02:00
|
|
|
)
|
2011-06-27 07:51:52 +00:00
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
data_path_iter = self.data_path_iter
|
|
|
|
data_path_item = self.data_path_item
|
|
|
|
|
|
|
|
items = list(getattr(context, data_path_iter))
|
|
|
|
items_ok = []
|
|
|
|
is_set = False
|
|
|
|
for item in items:
|
|
|
|
try:
|
|
|
|
value_orig = eval("item." + data_path_item)
|
|
|
|
except:
|
|
|
|
continue
|
|
|
|
|
2012-10-08 08:28:05 +00:00
|
|
|
if value_orig is True:
|
2011-06-27 07:51:52 +00:00
|
|
|
is_set = True
|
2012-10-08 08:28:05 +00:00
|
|
|
elif value_orig is False:
|
2011-06-27 07:51:52 +00:00
|
|
|
pass
|
|
|
|
else:
|
|
|
|
self.report({'WARNING'}, "Non boolean value found: %s[ ].%s" %
|
|
|
|
(data_path_iter, data_path_item))
|
|
|
|
return {'CANCELLED'}
|
|
|
|
|
|
|
|
items_ok.append(item)
|
|
|
|
|
2011-08-18 16:01:11 +00:00
|
|
|
# avoid undo push when nothing to do
|
|
|
|
if not items_ok:
|
|
|
|
return {'CANCELLED'}
|
|
|
|
|
2011-06-27 07:51:52 +00:00
|
|
|
if self.type == 'ENABLE':
|
|
|
|
is_set = True
|
|
|
|
elif self.type == 'DISABLE':
|
|
|
|
is_set = False
|
|
|
|
else:
|
|
|
|
is_set = not is_set
|
|
|
|
|
|
|
|
exec_str = "item.%s = %s" % (data_path_item, is_set)
|
|
|
|
for item in items_ok:
|
|
|
|
exec(exec_str)
|
|
|
|
|
2011-08-18 16:01:11 +00:00
|
|
|
return operator_value_undo_return(item)
|
2011-06-27 07:51:52 +00:00
|
|
|
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_context_modal_mouse(Operator):
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Adjust arbitrary values with mouse input"""
|
2010-02-21 14:05:02 +00:00
|
|
|
bl_idname = "wm.context_modal_mouse"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Context Modal Mouse"
|
2015-04-27 18:44:27 +10:00
|
|
|
bl_options = {'GRAB_CURSOR', 'BLOCKING', 'UNDO', 'INTERNAL'}
|
2010-02-22 23:32:58 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path_iter: data_path_iter
|
|
|
|
data_path_item: data_path_item
|
|
|
|
header_text: StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Header Text",
|
|
|
|
description="Text to display in header during scale",
|
|
|
|
)
|
2010-02-22 23:32:58 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
input_scale: FloatProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
description="Scale the mouse movement by this value before applying the delta",
|
|
|
|
default=0.01,
|
|
|
|
)
|
2018-07-11 22:18:09 +02:00
|
|
|
invert: BoolProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
description="Invert the mouse input",
|
|
|
|
default=False,
|
|
|
|
)
|
2018-07-11 22:18:09 +02:00
|
|
|
initial_x: IntProperty(options={'HIDDEN'})
|
2010-02-21 14:05:02 +00:00
|
|
|
|
|
|
|
def _values_store(self, context):
|
2010-09-09 18:03:57 +00:00
|
|
|
data_path_iter = self.data_path_iter
|
|
|
|
data_path_item = self.data_path_item
|
2010-02-21 14:05:02 +00:00
|
|
|
|
2010-02-27 14:54:45 +00:00
|
|
|
self._values = values = {}
|
2010-02-21 14:05:02 +00:00
|
|
|
|
2010-06-14 03:52:10 +00:00
|
|
|
for item in getattr(context, data_path_iter):
|
2010-02-21 14:05:02 +00:00
|
|
|
try:
|
2010-06-14 03:52:10 +00:00
|
|
|
value_orig = eval("item." + data_path_item)
|
2010-02-21 14:05:02 +00:00
|
|
|
except:
|
|
|
|
continue
|
2010-02-22 23:32:58 +00:00
|
|
|
|
2010-02-21 14:05:02 +00:00
|
|
|
# check this can be set, maybe this is library data.
|
|
|
|
try:
|
2010-06-14 03:52:10 +00:00
|
|
|
exec("item.%s = %s" % (data_path_item, value_orig))
|
2010-02-21 14:05:02 +00:00
|
|
|
except:
|
|
|
|
continue
|
|
|
|
|
|
|
|
values[item] = value_orig
|
|
|
|
|
|
|
|
def _values_delta(self, delta):
|
2010-09-09 18:03:57 +00:00
|
|
|
delta *= self.input_scale
|
|
|
|
if self.invert:
|
2010-02-22 23:32:58 +00:00
|
|
|
delta = - delta
|
2010-02-21 14:05:02 +00:00
|
|
|
|
2010-09-09 18:03:57 +00:00
|
|
|
data_path_item = self.data_path_item
|
2010-02-21 14:05:02 +00:00
|
|
|
for item, value_orig in self._values.items():
|
2010-02-23 15:34:02 +00:00
|
|
|
if type(value_orig) == int:
|
2010-06-14 03:52:10 +00:00
|
|
|
exec("item.%s = int(%d)" % (data_path_item, round(value_orig + delta)))
|
2010-02-23 15:34:02 +00:00
|
|
|
else:
|
2010-06-14 03:52:10 +00:00
|
|
|
exec("item.%s = %f" % (data_path_item, value_orig + delta))
|
2010-02-21 14:05:02 +00:00
|
|
|
|
|
|
|
def _values_restore(self):
|
2010-09-09 18:03:57 +00:00
|
|
|
data_path_item = self.data_path_item
|
2010-02-21 14:05:02 +00:00
|
|
|
for item, value_orig in self._values.items():
|
2010-06-14 03:52:10 +00:00
|
|
|
exec("item.%s = %s" % (data_path_item, value_orig))
|
2010-02-21 14:05:02 +00:00
|
|
|
|
|
|
|
self._values.clear()
|
2010-02-22 23:32:58 +00:00
|
|
|
|
2010-02-21 14:05:02 +00:00
|
|
|
def _values_clear(self):
|
|
|
|
self._values.clear()
|
|
|
|
|
|
|
|
def modal(self, context, event):
|
|
|
|
event_type = event.type
|
|
|
|
|
|
|
|
if event_type == 'MOUSEMOVE':
|
2010-09-09 18:03:57 +00:00
|
|
|
delta = event.mouse_x - self.initial_x
|
2010-02-21 14:05:02 +00:00
|
|
|
self._values_delta(delta)
|
2012-02-25 21:15:02 +00:00
|
|
|
header_text = self.header_text
|
|
|
|
if header_text:
|
|
|
|
if len(self._values) == 1:
|
|
|
|
(item, ) = self._values.keys()
|
|
|
|
header_text = header_text % eval("item.%s" % self.data_path_item)
|
|
|
|
else:
|
|
|
|
header_text = (self.header_text % delta) + " (delta)"
|
2018-09-08 06:23:25 +10:00
|
|
|
context.area.header_text_set(header_text)
|
2010-02-21 14:05:02 +00:00
|
|
|
|
|
|
|
elif 'LEFTMOUSE' == event_type:
|
2011-08-18 16:01:11 +00:00
|
|
|
item = next(iter(self._values.keys()))
|
2010-02-21 14:05:02 +00:00
|
|
|
self._values_clear()
|
2018-10-30 16:16:41 +11:00
|
|
|
context.area.header_text_set(None)
|
2011-08-18 16:01:11 +00:00
|
|
|
return operator_value_undo_return(item)
|
2010-02-21 14:05:02 +00:00
|
|
|
|
2011-08-08 05:21:37 +00:00
|
|
|
elif event_type in {'RIGHTMOUSE', 'ESC'}:
|
2010-02-21 14:05:02 +00:00
|
|
|
self._values_restore()
|
2018-10-30 16:16:41 +11:00
|
|
|
context.area.header_text_set(None)
|
2011-08-18 16:01:11 +00:00
|
|
|
return {'CANCELLED'}
|
2010-02-22 23:32:58 +00:00
|
|
|
|
2010-02-21 14:05:02 +00:00
|
|
|
return {'RUNNING_MODAL'}
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
|
|
|
self._values_store(context)
|
|
|
|
|
|
|
|
if not self._values:
|
2010-03-06 01:40:29 +00:00
|
|
|
self.report({'WARNING'}, "Nothing to operate on: %s[ ].%s" %
|
2012-09-05 00:11:39 +00:00
|
|
|
(self.data_path_iter, self.data_path_item))
|
2010-03-06 01:40:29 +00:00
|
|
|
|
2010-02-21 14:05:02 +00:00
|
|
|
return {'CANCELLED'}
|
|
|
|
else:
|
2010-09-09 18:03:57 +00:00
|
|
|
self.initial_x = event.mouse_x
|
2010-02-21 14:05:02 +00:00
|
|
|
|
2010-12-08 11:42:11 +00:00
|
|
|
context.window_manager.modal_handler_add(self)
|
2010-02-21 14:05:02 +00:00
|
|
|
return {'RUNNING_MODAL'}
|
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_url_open(Operator):
|
2017-04-14 20:01:43 +10:00
|
|
|
"""Open a website in the web-browser"""
|
2010-03-14 20:07:15 +00:00
|
|
|
bl_idname = "wm.url_open"
|
|
|
|
bl_label = ""
|
2014-02-26 13:48:41 +11:00
|
|
|
bl_options = {'INTERNAL'}
|
2010-03-14 20:07:15 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
url: StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="URL",
|
|
|
|
description="URL to open",
|
|
|
|
)
|
2010-03-14 20:07:15 +00:00
|
|
|
|
2019-05-09 09:15:01 +10:00
|
|
|
def execute(self, _context):
|
2010-03-14 20:07:15 +00:00
|
|
|
import webbrowser
|
2010-09-09 18:03:57 +00:00
|
|
|
webbrowser.open(self.url)
|
2010-03-14 20:07:15 +00:00
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_path_open(Operator):
|
2017-04-14 20:01:43 +10:00
|
|
|
"""Open a path in a file browser"""
|
2010-07-04 10:02:30 +00:00
|
|
|
bl_idname = "wm.path_open"
|
|
|
|
bl_label = ""
|
2014-02-26 13:48:41 +11:00
|
|
|
bl_options = {'INTERNAL'}
|
2010-07-04 10:02:30 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
filepath: StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
subtype='FILE_PATH',
|
|
|
|
options={'SKIP_SAVE'},
|
|
|
|
)
|
2010-07-04 10:02:30 +00:00
|
|
|
|
2019-05-09 09:15:01 +10:00
|
|
|
def execute(self, _context):
|
2010-07-04 10:02:30 +00:00
|
|
|
import sys
|
2011-09-23 13:47:29 +00:00
|
|
|
import os
|
2010-07-04 10:02:30 +00:00
|
|
|
import subprocess
|
|
|
|
|
2012-05-12 21:11:48 +00:00
|
|
|
filepath = self.filepath
|
|
|
|
|
|
|
|
if not filepath:
|
|
|
|
self.report({'ERROR'}, "File path was not set")
|
|
|
|
return {'CANCELLED'}
|
|
|
|
|
|
|
|
filepath = bpy.path.abspath(filepath)
|
2010-07-04 10:02:30 +00:00
|
|
|
filepath = os.path.normpath(filepath)
|
2010-07-05 22:22:22 +00:00
|
|
|
|
2010-07-04 10:02:30 +00:00
|
|
|
if not os.path.exists(filepath):
|
|
|
|
self.report({'ERROR'}, "File '%s' not found" % filepath)
|
|
|
|
return {'CANCELLED'}
|
2010-07-05 22:22:22 +00:00
|
|
|
|
2011-03-11 01:24:16 +00:00
|
|
|
if sys.platform[:3] == "win":
|
2012-08-23 11:18:31 +00:00
|
|
|
os.startfile(filepath)
|
2011-12-22 03:56:21 +00:00
|
|
|
elif sys.platform == "darwin":
|
2014-06-20 01:57:06 +10:00
|
|
|
subprocess.check_call(["open", filepath])
|
2010-07-04 10:02:30 +00:00
|
|
|
else:
|
|
|
|
try:
|
2014-06-20 01:57:06 +10:00
|
|
|
subprocess.check_call(["xdg-open", filepath])
|
|
|
|
except:
|
2010-07-04 10:02:30 +00:00
|
|
|
# xdg-open *should* be supported by recent Gnome, KDE, Xfce
|
2014-06-20 01:57:06 +10:00
|
|
|
import traceback
|
|
|
|
traceback.print_exc()
|
2010-07-04 10:02:30 +00:00
|
|
|
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
|
|
|
2012-06-01 20:38:40 +00:00
|
|
|
def _wm_doc_get_id(doc_id, do_url=True, url_prefix=""):
|
2018-01-22 09:45:51 +11:00
|
|
|
|
|
|
|
def operator_exists_pair(a, b):
|
|
|
|
# Not fast, this is only for docs.
|
|
|
|
return b in dir(getattr(bpy.ops, a))
|
|
|
|
|
|
|
|
def operator_exists_single(a):
|
|
|
|
a, b = a.partition("_OT_")[::2]
|
|
|
|
return operator_exists_pair(a.lower(), b)
|
|
|
|
|
2012-06-01 20:38:40 +00:00
|
|
|
id_split = doc_id.split(".")
|
|
|
|
url = rna = None
|
|
|
|
|
|
|
|
if len(id_split) == 1: # rna, class
|
|
|
|
if do_url:
|
|
|
|
url = "%s/bpy.types.%s.html" % (url_prefix, id_split[0])
|
|
|
|
else:
|
|
|
|
rna = "bpy.types.%s" % id_split[0]
|
|
|
|
|
|
|
|
elif len(id_split) == 2: # rna, class.prop
|
|
|
|
class_name, class_prop = id_split
|
|
|
|
|
2013-02-25 04:19:28 +00:00
|
|
|
# an operator (common case - just button referencing an op)
|
2018-01-22 09:45:51 +11:00
|
|
|
if operator_exists_pair(class_name, class_prop):
|
|
|
|
if do_url:
|
|
|
|
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)
|
|
|
|
elif operator_exists_single(class_name):
|
|
|
|
# note: ignore the prop name since we don't have a way to link into it
|
|
|
|
class_name, class_prop = class_name.split("_OT_", 1)
|
|
|
|
class_name = class_name.lower()
|
2012-06-01 20:38:40 +00:00
|
|
|
if do_url:
|
2017-03-25 11:07:05 +11:00
|
|
|
url = (
|
|
|
|
"%s/bpy.ops.%s.html#bpy.ops.%s.%s" %
|
|
|
|
(url_prefix, class_name, class_name, class_prop)
|
|
|
|
)
|
2012-06-01 20:38:40 +00:00
|
|
|
else:
|
|
|
|
rna = "bpy.ops.%s.%s" % (class_name, class_prop)
|
|
|
|
else:
|
2018-01-22 09:45:51 +11:00
|
|
|
# an RNA setting, common case
|
2013-02-25 04:19:28 +00:00
|
|
|
rna_class = getattr(bpy.types, class_name)
|
2013-03-28 19:33:14 +00:00
|
|
|
|
2018-01-22 09:45:51 +11:00
|
|
|
# detect if this is a inherited member and use that name instead
|
|
|
|
rna_parent = rna_class.bl_rna
|
|
|
|
rna_prop = rna_parent.properties.get(class_prop)
|
|
|
|
if rna_prop:
|
|
|
|
rna_parent = rna_parent.base
|
|
|
|
while rna_parent and rna_prop == rna_parent.properties.get(class_prop):
|
|
|
|
class_name = rna_parent.identifier
|
|
|
|
rna_parent = rna_parent.base
|
|
|
|
|
2013-02-25 04:19:28 +00:00
|
|
|
if do_url:
|
2017-03-25 11:07:05 +11:00
|
|
|
url = (
|
2018-01-22 09:45:51 +11:00
|
|
|
"%s/bpy.types.%s.html#bpy.types.%s.%s" %
|
2017-03-25 11:07:05 +11:00
|
|
|
(url_prefix, class_name, class_name, class_prop)
|
|
|
|
)
|
2013-02-25 04:19:28 +00:00
|
|
|
else:
|
2018-01-22 09:45:51 +11:00
|
|
|
rna = "bpy.types.%s.%s" % (class_name, class_prop)
|
2013-02-25 04:19:28 +00:00
|
|
|
else:
|
2018-01-22 09:45:51 +11:00
|
|
|
# We assume this is custom property, only try to generate generic url/rna_id...
|
|
|
|
if do_url:
|
|
|
|
url = ("%s/bpy.types.bpy_struct.html#bpy.types.bpy_struct.items" % (url_prefix,))
|
2013-02-25 04:19:28 +00:00
|
|
|
else:
|
2018-01-22 09:45:51 +11:00
|
|
|
rna = "bpy.types.bpy_struct"
|
2012-06-19 22:17:19 +00:00
|
|
|
|
2012-06-01 20:38:40 +00:00
|
|
|
return url if do_url else rna
|
|
|
|
|
|
|
|
|
|
|
|
class WM_OT_doc_view_manual(Operator):
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Load online manual"""
|
2012-06-01 20:38:40 +00:00
|
|
|
bl_idname = "wm.doc_view_manual"
|
|
|
|
bl_label = "View Manual"
|
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
doc_id: doc_id
|
2012-06-01 20:38:40 +00:00
|
|
|
|
|
|
|
@staticmethod
|
2012-09-13 00:26:01 +00:00
|
|
|
def _find_reference(rna_id, url_mapping, verbose=True):
|
|
|
|
if verbose:
|
|
|
|
print("online manual check for: '%s'... " % rna_id)
|
2018-01-22 09:50:41 +11:00
|
|
|
from fnmatch import fnmatchcase
|
|
|
|
# XXX, for some reason all RNA ID's are stored lowercase
|
|
|
|
# Adding case into all ID's isn't worth the hassle so force lowercase.
|
|
|
|
rna_id = rna_id.lower()
|
2012-06-01 20:38:40 +00:00
|
|
|
for pattern, url_suffix in url_mapping:
|
2018-01-22 09:50:41 +11:00
|
|
|
if fnmatchcase(rna_id, pattern):
|
2012-09-13 00:26:01 +00:00
|
|
|
if verbose:
|
|
|
|
print(" match found: '%s' --> '%s'" % (pattern, url_suffix))
|
2012-06-01 20:38:40 +00:00
|
|
|
return url_suffix
|
2012-09-13 00:26:01 +00:00
|
|
|
if verbose:
|
|
|
|
print("match not found")
|
2012-06-01 20:38:40 +00:00
|
|
|
return None
|
|
|
|
|
2012-09-13 00:26:01 +00:00
|
|
|
@staticmethod
|
|
|
|
def _lookup_rna_url(rna_id, verbose=True):
|
2012-08-25 14:07:51 +00:00
|
|
|
for prefix, url_manual_mapping in bpy.utils.manual_map():
|
2012-09-13 00:26:01 +00:00
|
|
|
rna_ref = WM_OT_doc_view_manual._find_reference(rna_id, url_manual_mapping, verbose=verbose)
|
2012-08-25 14:07:51 +00:00
|
|
|
if rna_ref is not None:
|
|
|
|
url = prefix + rna_ref
|
2013-04-20 13:23:53 +00:00
|
|
|
return url
|
2012-09-13 00:26:01 +00:00
|
|
|
|
2019-05-09 09:15:01 +10:00
|
|
|
def execute(self, _context):
|
2012-09-13 00:26:01 +00:00
|
|
|
rna_id = _wm_doc_get_id(self.doc_id, do_url=False)
|
|
|
|
if rna_id is None:
|
|
|
|
return {'PASS_THROUGH'}
|
|
|
|
|
|
|
|
url = self._lookup_rna_url(rna_id)
|
2012-08-25 14:07:51 +00:00
|
|
|
|
|
|
|
if url is None:
|
2015-06-08 11:33:03 +10:00
|
|
|
self.report(
|
2018-02-01 13:58:44 +11:00
|
|
|
{'WARNING'},
|
|
|
|
"No reference available %r, "
|
|
|
|
"Update info in 'rna_manual_reference.py' "
|
|
|
|
"or callback to bpy.utils.manual_map()" %
|
|
|
|
self.doc_id
|
|
|
|
)
|
2012-06-01 20:38:40 +00:00
|
|
|
return {'CANCELLED'}
|
|
|
|
else:
|
2012-08-25 14:07:51 +00:00
|
|
|
import webbrowser
|
|
|
|
webbrowser.open(url)
|
|
|
|
return {'FINISHED'}
|
2012-06-01 20:38:40 +00:00
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_doc_view(Operator):
|
2019-05-07 10:01:25 +10:00
|
|
|
"""Open online reference docs in a web browser"""
|
2009-11-04 20:21:08 +00:00
|
|
|
bl_idname = "wm.doc_view"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "View Documentation"
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
doc_id: doc_id
|
2011-04-01 15:13:58 +00:00
|
|
|
if bpy.app.version_cycle == "release":
|
2018-12-12 11:55:40 +01:00
|
|
|
_prefix = ("https://docs.blender.org/api/current")
|
2011-04-01 15:13:58 +00:00
|
|
|
else:
|
2019-03-29 17:34:16 +01:00
|
|
|
_prefix = ("https://docs.blender.org/api/master")
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2019-05-09 09:15:01 +10:00
|
|
|
def execute(self, _context):
|
2012-06-01 20:38:40 +00:00
|
|
|
url = _wm_doc_get_id(self.doc_id, do_url=True, url_prefix=self._prefix)
|
|
|
|
if url is None:
|
2009-12-24 19:50:43 +00:00
|
|
|
return {'PASS_THROUGH'}
|
2009-11-04 20:21:08 +00:00
|
|
|
|
|
|
|
import webbrowser
|
|
|
|
webbrowser.open(url)
|
|
|
|
|
2009-12-24 19:50:43 +00:00
|
|
|
return {'FINISHED'}
|
2009-11-04 20:21:08 +00:00
|
|
|
|
|
|
|
|
2011-08-18 12:20:10 +00:00
|
|
|
rna_path = StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Property Edit",
|
|
|
|
description="Property data_path edit",
|
|
|
|
maxlen=1024,
|
|
|
|
options={'HIDDEN'},
|
|
|
|
)
|
2011-08-18 12:20:10 +00:00
|
|
|
|
|
|
|
rna_value = StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Property Value",
|
|
|
|
description="Property value edit",
|
|
|
|
maxlen=1024,
|
|
|
|
)
|
2010-08-02 02:55:12 +00:00
|
|
|
|
2018-12-15 22:37:12 +03:00
|
|
|
rna_default = StringProperty(
|
|
|
|
name="Default Value",
|
|
|
|
description="Default value of the property. Important for NLA mixing",
|
|
|
|
maxlen=1024,
|
|
|
|
)
|
|
|
|
|
2011-08-18 12:20:10 +00:00
|
|
|
rna_property = StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Property Name",
|
|
|
|
description="Property name edit",
|
|
|
|
maxlen=1024,
|
|
|
|
)
|
2010-08-02 02:55:12 +00:00
|
|
|
|
2011-08-18 12:20:10 +00:00
|
|
|
rna_min = FloatProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Min",
|
|
|
|
default=-10000.0,
|
|
|
|
precision=3,
|
|
|
|
)
|
2010-08-02 02:55:12 +00:00
|
|
|
|
2011-08-18 12:20:10 +00:00
|
|
|
rna_max = FloatProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Max",
|
|
|
|
default=10000.0,
|
|
|
|
precision=3,
|
|
|
|
)
|
2010-08-02 02:55:12 +00:00
|
|
|
|
2016-02-24 02:53:11 +11:00
|
|
|
rna_use_soft_limits = BoolProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Use Soft Limits",
|
|
|
|
)
|
2016-02-24 02:53:11 +11:00
|
|
|
|
2018-04-17 18:06:41 +02:00
|
|
|
rna_is_overridable_static = BoolProperty(
|
2018-06-26 22:56:39 +02:00
|
|
|
name="Is Statically Overridable",
|
|
|
|
default=False,
|
|
|
|
)
|
2018-04-17 18:06:41 +02:00
|
|
|
|
2010-08-02 02:55:12 +00:00
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_properties_edit(Operator):
|
2010-08-02 02:55:12 +00:00
|
|
|
bl_idname = "wm.properties_edit"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Edit Property"
|
2013-04-20 13:23:53 +00:00
|
|
|
# register only because invoke_props_popup requires.
|
|
|
|
bl_options = {'REGISTER', 'INTERNAL'}
|
2010-08-02 02:55:12 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path: rna_path
|
|
|
|
property: rna_property
|
|
|
|
value: rna_value
|
2018-12-15 22:37:12 +03:00
|
|
|
default: rna_default
|
2018-07-11 22:18:09 +02:00
|
|
|
min: rna_min
|
|
|
|
max: rna_max
|
|
|
|
use_soft_limits: rna_use_soft_limits
|
|
|
|
is_overridable_static: rna_is_overridable_static
|
|
|
|
soft_min: rna_min
|
|
|
|
soft_max: rna_max
|
|
|
|
description: StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Tooltip",
|
|
|
|
)
|
2010-08-02 02:55:12 +00:00
|
|
|
|
2016-02-24 02:53:11 +11:00
|
|
|
def _cmp_props_get(self):
|
|
|
|
# Changing these properties will refresh the UI
|
|
|
|
return {
|
|
|
|
"use_soft_limits": self.use_soft_limits,
|
|
|
|
"soft_range": (self.soft_min, self.soft_max),
|
|
|
|
"hard_range": (self.min, self.max),
|
2018-02-01 13:58:44 +11:00
|
|
|
}
|
2016-02-24 02:53:11 +11:00
|
|
|
|
2018-12-15 22:37:12 +03:00
|
|
|
def get_value_eval(self):
|
|
|
|
try:
|
|
|
|
value_eval = eval(self.value)
|
|
|
|
# assert else None -> None, not "None", see [#33431]
|
|
|
|
assert(type(value_eval) in {str, float, int, bool, tuple, list})
|
|
|
|
except:
|
|
|
|
value_eval = self.value
|
|
|
|
|
|
|
|
return value_eval
|
|
|
|
|
|
|
|
def get_default_eval(self):
|
|
|
|
try:
|
|
|
|
default_eval = eval(self.default)
|
|
|
|
# assert else None -> None, not "None", see [#33431]
|
|
|
|
assert(type(default_eval) in {str, float, int, bool, tuple, list})
|
|
|
|
except:
|
|
|
|
default_eval = self.default
|
|
|
|
|
|
|
|
return default_eval
|
|
|
|
|
2010-08-02 02:55:12 +00:00
|
|
|
def execute(self, context):
|
2015-09-11 04:26:30 +10:00
|
|
|
from rna_prop_ui import (
|
2017-03-25 11:07:05 +11:00
|
|
|
rna_idprop_ui_prop_get,
|
|
|
|
rna_idprop_ui_prop_clear,
|
|
|
|
rna_idprop_ui_prop_update,
|
|
|
|
)
|
2013-07-03 01:20:32 +00:00
|
|
|
|
2010-09-09 18:03:57 +00:00
|
|
|
data_path = self.data_path
|
|
|
|
prop = self.property
|
2011-03-21 23:53:19 +00:00
|
|
|
|
|
|
|
prop_old = getattr(self, "_last_prop", [None])[0]
|
|
|
|
|
|
|
|
if prop_old is None:
|
|
|
|
self.report({'ERROR'}, "Direct execution not supported")
|
|
|
|
return {'CANCELLED'}
|
2010-08-02 02:55:12 +00:00
|
|
|
|
2018-12-15 22:37:12 +03:00
|
|
|
value_eval = self.get_value_eval()
|
|
|
|
default_eval = self.get_default_eval()
|
2010-08-02 02:55:12 +00:00
|
|
|
|
|
|
|
# First remove
|
|
|
|
item = eval("context.%s" % data_path)
|
2013-12-17 09:40:52 +01:00
|
|
|
prop_type_old = type(item[prop_old])
|
2010-08-02 02:55:12 +00:00
|
|
|
|
|
|
|
rna_idprop_ui_prop_clear(item, prop_old)
|
2014-02-25 16:49:14 +11:00
|
|
|
exec_str = "del item[%r]" % prop_old
|
2010-08-02 02:55:12 +00:00
|
|
|
# print(exec_str)
|
|
|
|
exec(exec_str)
|
|
|
|
|
|
|
|
# Reassign
|
2014-02-25 16:49:14 +11:00
|
|
|
exec_str = "item[%r] = %s" % (prop, repr(value_eval))
|
2010-08-02 02:55:12 +00:00
|
|
|
# print(exec_str)
|
|
|
|
exec(exec_str)
|
2015-09-11 04:26:30 +10:00
|
|
|
|
2018-04-17 18:06:41 +02:00
|
|
|
exec_str = "item.property_overridable_static_set('[\"%s\"]', %s)" % (prop, self.is_overridable_static)
|
|
|
|
exec(exec_str)
|
|
|
|
|
2015-09-11 04:26:30 +10:00
|
|
|
rna_idprop_ui_prop_update(item, prop)
|
|
|
|
|
2010-08-02 02:55:12 +00:00
|
|
|
self._last_prop[:] = [prop]
|
|
|
|
|
|
|
|
prop_type = type(item[prop])
|
|
|
|
|
|
|
|
prop_ui = rna_idprop_ui_prop_get(item, prop)
|
|
|
|
|
2011-08-08 05:21:37 +00:00
|
|
|
if prop_type in {float, int}:
|
2016-02-24 02:53:11 +11:00
|
|
|
prop_ui["min"] = prop_type(self.min)
|
|
|
|
prop_ui["max"] = prop_type(self.max)
|
2018-12-15 22:37:12 +03:00
|
|
|
if type(default_eval) in {float, int} and default_eval != 0:
|
|
|
|
prop_ui["default"] = prop_type(default_eval)
|
2016-02-24 02:53:11 +11:00
|
|
|
|
|
|
|
if self.use_soft_limits:
|
|
|
|
prop_ui["soft_min"] = prop_type(self.soft_min)
|
|
|
|
prop_ui["soft_max"] = prop_type(self.soft_max)
|
|
|
|
else:
|
|
|
|
prop_ui["soft_min"] = prop_type(self.min)
|
|
|
|
prop_ui["soft_max"] = prop_type(self.max)
|
2010-02-14 11:21:21 +00:00
|
|
|
|
2012-06-19 22:17:19 +00:00
|
|
|
prop_ui["description"] = self.description
|
2010-08-02 02:55:12 +00:00
|
|
|
|
2013-12-17 09:40:52 +01:00
|
|
|
# If we have changed the type of the property, update its potential anim curves!
|
|
|
|
if prop_type_old != prop_type:
|
2014-02-25 16:49:14 +11:00
|
|
|
data_path = '["%s"]' % bpy.utils.escape_identifier(prop)
|
2013-12-17 09:40:52 +01:00
|
|
|
done = set()
|
2014-02-19 08:02:59 +11:00
|
|
|
|
2013-12-17 09:40:52 +01:00
|
|
|
def _update(fcurves):
|
|
|
|
for fcu in fcurves:
|
|
|
|
if fcu not in done and fcu.data_path == data_path:
|
|
|
|
fcu.update_autoflags(item)
|
|
|
|
done.add(fcu)
|
|
|
|
|
|
|
|
def _update_strips(strips):
|
|
|
|
for st in strips:
|
2014-02-25 16:49:14 +11:00
|
|
|
if st.type == 'CLIP' and st.action:
|
2013-12-17 09:40:52 +01:00
|
|
|
_update(st.action.fcurves)
|
2014-02-25 16:49:14 +11:00
|
|
|
elif st.type == 'META':
|
2013-12-17 09:40:52 +01:00
|
|
|
_update_strips(st.strips)
|
|
|
|
|
|
|
|
adt = getattr(item, "animation_data", None)
|
|
|
|
if adt is not None:
|
|
|
|
if adt.action:
|
|
|
|
_update(adt.action.fcurves)
|
|
|
|
if adt.drivers:
|
|
|
|
_update(adt.drivers)
|
|
|
|
if adt.nla_tracks:
|
|
|
|
for nt in adt.nla_tracks:
|
|
|
|
_update_strips(nt.strips)
|
|
|
|
|
2011-03-18 06:36:57 +00:00
|
|
|
# otherwise existing buttons which reference freed
|
|
|
|
# memory may crash blender [#26510]
|
2011-03-24 11:38:20 +00:00
|
|
|
# context.area.tag_redraw()
|
|
|
|
for win in context.window_manager.windows:
|
|
|
|
for area in win.screen.areas:
|
|
|
|
area.tag_redraw()
|
2011-03-18 06:36:57 +00:00
|
|
|
|
2010-08-02 02:55:12 +00:00
|
|
|
return {'FINISHED'}
|
|
|
|
|
2019-05-09 09:15:01 +10:00
|
|
|
def invoke(self, context, _event):
|
2013-07-03 01:20:32 +00:00
|
|
|
from rna_prop_ui import rna_idprop_ui_prop_get
|
|
|
|
|
2011-08-18 15:25:18 +00:00
|
|
|
data_path = self.data_path
|
2010-08-02 02:55:12 +00:00
|
|
|
|
2011-08-18 15:25:18 +00:00
|
|
|
if not data_path:
|
2011-03-21 23:53:19 +00:00
|
|
|
self.report({'ERROR'}, "Data path not set")
|
|
|
|
return {'CANCELLED'}
|
|
|
|
|
2010-09-09 18:03:57 +00:00
|
|
|
self._last_prop = [self.property]
|
2010-08-02 02:55:12 +00:00
|
|
|
|
2011-08-18 15:25:18 +00:00
|
|
|
item = eval("context.%s" % data_path)
|
2010-08-02 02:55:12 +00:00
|
|
|
|
2018-12-15 18:50:44 +03:00
|
|
|
# retrieve overridable static
|
|
|
|
exec_str = "item.is_property_overridable_static('[\"%s\"]')" % (self.property)
|
|
|
|
self.is_overridable_static = bool(eval(exec_str))
|
|
|
|
|
2018-12-15 22:37:12 +03:00
|
|
|
# default default value
|
|
|
|
prop_type = type(self.get_value_eval())
|
2018-12-20 13:01:40 +11:00
|
|
|
if prop_type in {int, float}:
|
2018-12-15 22:37:12 +03:00
|
|
|
self.default = str(prop_type(0))
|
|
|
|
else:
|
|
|
|
self.default = ""
|
|
|
|
|
2010-08-02 02:55:12 +00:00
|
|
|
# setup defaults
|
2011-10-17 06:58:07 +00:00
|
|
|
prop_ui = rna_idprop_ui_prop_get(item, self.property, False) # don't create
|
2010-08-02 02:55:12 +00:00
|
|
|
if prop_ui:
|
2010-09-09 18:03:57 +00:00
|
|
|
self.min = prop_ui.get("min", -1000000000)
|
|
|
|
self.max = prop_ui.get("max", 1000000000)
|
|
|
|
self.description = prop_ui.get("description", "")
|
2010-08-02 02:55:12 +00:00
|
|
|
|
2018-12-15 22:37:12 +03:00
|
|
|
defval = prop_ui.get("default", None)
|
|
|
|
if defval is not None:
|
|
|
|
self.default = str(defval)
|
|
|
|
|
2016-02-24 02:53:11 +11:00
|
|
|
self.soft_min = prop_ui.get("soft_min", self.min)
|
|
|
|
self.soft_max = prop_ui.get("soft_max", self.max)
|
|
|
|
self.use_soft_limits = (
|
2018-02-01 13:58:44 +11:00
|
|
|
self.min != self.soft_min or
|
|
|
|
self.max != self.soft_max
|
|
|
|
)
|
2016-02-24 02:53:11 +11:00
|
|
|
|
|
|
|
# store for comparison
|
|
|
|
self._cmp_props = self._cmp_props_get()
|
|
|
|
|
2010-09-02 04:53:05 +00:00
|
|
|
wm = context.window_manager
|
2011-01-15 19:15:35 +00:00
|
|
|
return wm.invoke_props_dialog(self)
|
2010-02-14 11:21:21 +00:00
|
|
|
|
2019-05-09 09:15:01 +10:00
|
|
|
def check(self, _context):
|
2016-02-24 02:53:11 +11:00
|
|
|
cmp_props = self._cmp_props_get()
|
|
|
|
changed = False
|
|
|
|
if self._cmp_props != cmp_props:
|
|
|
|
if cmp_props["use_soft_limits"]:
|
|
|
|
if cmp_props["soft_range"] != self._cmp_props["soft_range"]:
|
|
|
|
self.min = min(self.min, self.soft_min)
|
|
|
|
self.max = max(self.max, self.soft_max)
|
|
|
|
changed = True
|
|
|
|
if cmp_props["hard_range"] != self._cmp_props["hard_range"]:
|
|
|
|
self.soft_min = max(self.min, self.soft_min)
|
|
|
|
self.soft_max = min(self.max, self.soft_max)
|
|
|
|
changed = True
|
|
|
|
else:
|
|
|
|
if cmp_props["soft_range"] != cmp_props["hard_range"]:
|
|
|
|
self.soft_min = self.min
|
|
|
|
self.soft_max = self.max
|
|
|
|
changed = True
|
|
|
|
|
|
|
|
changed |= (cmp_props["use_soft_limits"] != self._cmp_props["use_soft_limits"])
|
|
|
|
|
|
|
|
if changed:
|
|
|
|
cmp_props = self._cmp_props_get()
|
|
|
|
|
|
|
|
self._cmp_props = cmp_props
|
|
|
|
|
|
|
|
return changed
|
|
|
|
|
2019-05-09 09:15:01 +10:00
|
|
|
def draw(self, _context):
|
2016-02-24 02:53:11 +11:00
|
|
|
layout = self.layout
|
|
|
|
layout.prop(self, "property")
|
|
|
|
layout.prop(self, "value")
|
2018-12-15 22:37:12 +03:00
|
|
|
|
|
|
|
row = layout.row()
|
2018-12-20 13:01:40 +11:00
|
|
|
row.enabled = type(self.get_value_eval()) in {int, float}
|
2018-12-15 22:37:12 +03:00
|
|
|
row.prop(self, "default")
|
|
|
|
|
2016-02-24 02:53:11 +11:00
|
|
|
row = layout.row(align=True)
|
|
|
|
row.prop(self, "min")
|
|
|
|
row.prop(self, "max")
|
|
|
|
|
2018-04-17 18:06:41 +02:00
|
|
|
row = layout.row()
|
|
|
|
row.prop(self, "use_soft_limits")
|
|
|
|
row.prop(self, "is_overridable_static")
|
2016-02-24 02:53:11 +11:00
|
|
|
|
|
|
|
row = layout.row(align=True)
|
|
|
|
row.enabled = self.use_soft_limits
|
|
|
|
row.prop(self, "soft_min", text="Soft Min")
|
|
|
|
row.prop(self, "soft_max", text="Soft Max")
|
|
|
|
layout.prop(self, "description")
|
|
|
|
|
2010-03-14 23:26:17 +00:00
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_properties_add(Operator):
|
2010-08-02 02:55:12 +00:00
|
|
|
bl_idname = "wm.properties_add"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Add Property"
|
2013-04-20 13:23:53 +00:00
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2010-02-14 11:21:21 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path: rna_path
|
2010-02-14 11:21:21 +00:00
|
|
|
|
2010-08-02 02:55:12 +00:00
|
|
|
def execute(self, context):
|
2015-09-11 04:26:30 +10:00
|
|
|
from rna_prop_ui import (
|
2019-03-30 12:53:39 +03:00
|
|
|
rna_idprop_ui_create,
|
2017-03-25 11:07:05 +11:00
|
|
|
)
|
2013-07-03 01:20:32 +00:00
|
|
|
|
2011-08-18 15:25:18 +00:00
|
|
|
data_path = self.data_path
|
|
|
|
item = eval("context.%s" % data_path)
|
2010-08-02 02:55:12 +00:00
|
|
|
|
|
|
|
def unique_name(names):
|
2011-12-22 03:56:21 +00:00
|
|
|
prop = "prop"
|
2010-08-02 02:55:12 +00:00
|
|
|
prop_new = prop
|
|
|
|
i = 1
|
|
|
|
while prop_new in names:
|
|
|
|
prop_new = prop + str(i)
|
|
|
|
i += 1
|
|
|
|
|
|
|
|
return prop_new
|
|
|
|
|
2017-03-25 11:07:05 +11:00
|
|
|
prop = unique_name({
|
|
|
|
*item.keys(),
|
|
|
|
*type(item).bl_rna.properties.keys(),
|
|
|
|
})
|
2012-10-26 10:54:02 +00:00
|
|
|
|
2019-04-16 16:00:24 +02:00
|
|
|
rna_idprop_ui_create(item, prop, default=1.0)
|
2010-08-02 02:55:12 +00:00
|
|
|
|
|
|
|
return {'FINISHED'}
|
2010-02-14 11:21:21 +00:00
|
|
|
|
2011-05-02 17:29:30 +00:00
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_properties_context_change(Operator):
|
2017-04-14 20:01:43 +10:00
|
|
|
"""Jump to a different tab inside the properties editor"""
|
2011-04-25 13:47:15 +00:00
|
|
|
bl_idname = "wm.properties_context_change"
|
|
|
|
bl_label = ""
|
2013-04-20 13:23:53 +00:00
|
|
|
bl_options = {'INTERNAL'}
|
2011-04-25 13:47:15 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
context: StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Context",
|
|
|
|
maxlen=64,
|
|
|
|
)
|
2011-04-25 13:47:15 +00:00
|
|
|
|
|
|
|
def execute(self, context):
|
2011-08-18 15:25:18 +00:00
|
|
|
context.space_data.context = self.context
|
2011-04-25 13:47:15 +00:00
|
|
|
return {'FINISHED'}
|
|
|
|
|
2010-02-22 23:32:58 +00:00
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_properties_remove(Operator):
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Internal use (edit a property data_path)"""
|
2010-08-02 02:55:12 +00:00
|
|
|
bl_idname = "wm.properties_remove"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Remove Property"
|
2013-04-20 13:23:53 +00:00
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2010-08-02 02:55:12 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path: rna_path
|
|
|
|
property: rna_property
|
2010-08-02 02:55:12 +00:00
|
|
|
|
|
|
|
def execute(self, context):
|
2015-09-11 04:26:30 +10:00
|
|
|
from rna_prop_ui import (
|
2017-03-25 11:07:05 +11:00
|
|
|
rna_idprop_ui_prop_clear,
|
|
|
|
rna_idprop_ui_prop_update,
|
|
|
|
)
|
2011-08-18 15:25:18 +00:00
|
|
|
data_path = self.data_path
|
|
|
|
item = eval("context.%s" % data_path)
|
2015-03-26 00:10:39 +11:00
|
|
|
prop = self.property
|
2015-09-11 04:26:30 +10:00
|
|
|
rna_idprop_ui_prop_update(item, prop)
|
2015-03-26 00:10:39 +11:00
|
|
|
del item[prop]
|
|
|
|
rna_idprop_ui_prop_clear(item, prop)
|
|
|
|
|
2010-08-02 02:55:12 +00:00
|
|
|
return {'FINISHED'}
|
|
|
|
|
2010-09-07 15:17:42 +00:00
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_sysinfo(Operator):
|
2016-01-07 00:18:40 +11:00
|
|
|
"""Generate system information, saved into a text file"""
|
2016-01-03 23:21:44 +11:00
|
|
|
|
2010-10-16 17:26:40 +00:00
|
|
|
bl_idname = "wm.sysinfo"
|
2016-01-07 00:18:40 +11:00
|
|
|
bl_label = "Save System Info"
|
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
filepath: StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
subtype='FILE_PATH',
|
|
|
|
options={'SKIP_SAVE'},
|
|
|
|
)
|
2010-10-16 17:26:40 +00:00
|
|
|
|
2019-05-09 09:15:01 +10:00
|
|
|
def execute(self, _context):
|
2010-10-16 17:26:40 +00:00
|
|
|
import sys_info
|
2016-01-07 00:18:40 +11:00
|
|
|
sys_info.write_sysinfo(self.filepath)
|
2010-10-16 17:26:40 +00:00
|
|
|
return {'FINISHED'}
|
2011-03-23 13:04:35 +00:00
|
|
|
|
2019-05-10 17:15:05 +02:00
|
|
|
def invoke(self, context, _event):
|
2016-01-07 00:18:40 +11:00
|
|
|
import os
|
|
|
|
|
|
|
|
if not self.filepath:
|
|
|
|
self.filepath = os.path.join(
|
2018-06-26 19:41:37 +02:00
|
|
|
os.path.expanduser("~"), "system-info.txt")
|
2016-01-07 00:18:40 +11:00
|
|
|
|
|
|
|
wm = context.window_manager
|
|
|
|
wm.fileselect_add(self)
|
|
|
|
return {'RUNNING_MODAL'}
|
|
|
|
|
2011-03-23 13:04:35 +00:00
|
|
|
|
2011-09-22 19:50:41 +00:00
|
|
|
class WM_OT_operator_cheat_sheet(Operator):
|
2017-04-14 20:01:43 +10:00
|
|
|
"""List all the Operators in a text-block, useful for scripting"""
|
2011-09-22 19:50:41 +00:00
|
|
|
bl_idname = "wm.operator_cheat_sheet"
|
|
|
|
bl_label = "Operator Cheat Sheet"
|
|
|
|
|
2019-05-09 09:15:01 +10:00
|
|
|
def execute(self, _context):
|
2011-09-22 19:50:41 +00:00
|
|
|
op_strings = []
|
|
|
|
tot = 0
|
|
|
|
for op_module_name in dir(bpy.ops):
|
|
|
|
op_module = getattr(bpy.ops, op_module_name)
|
|
|
|
for op_submodule_name in dir(op_module):
|
|
|
|
op = getattr(op_module, op_submodule_name)
|
|
|
|
text = repr(op)
|
2011-12-22 03:56:21 +00:00
|
|
|
if text.split("\n")[-1].startswith("bpy.ops."):
|
2011-09-22 19:50:41 +00:00
|
|
|
op_strings.append(text)
|
|
|
|
tot += 1
|
|
|
|
|
|
|
|
op_strings.append('')
|
|
|
|
|
|
|
|
textblock = bpy.data.texts.new("OperatorList.txt")
|
|
|
|
textblock.write('# %d Operators\n\n' % tot)
|
|
|
|
textblock.write('\n'.join(op_strings))
|
|
|
|
self.report({'INFO'}, "See OperatorList.txt textblock")
|
|
|
|
return {'FINISHED'}
|
2011-09-26 15:39:15 +00:00
|
|
|
|
2012-01-01 08:52:54 +00:00
|
|
|
|
2012-01-01 08:12:51 +00:00
|
|
|
# -----------------------------------------------------------------------------
|
2016-04-13 00:29:01 +10:00
|
|
|
# Add-on Operators
|
2011-09-26 15:39:15 +00:00
|
|
|
|
2018-03-01 01:26:02 +11:00
|
|
|
class WM_OT_owner_enable(Operator):
|
|
|
|
"""Enable workspace owner ID"""
|
|
|
|
bl_idname = "wm.owner_enable"
|
|
|
|
bl_label = "Enable Add-on"
|
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
owner_id: StringProperty(
|
2018-06-26 22:56:39 +02:00
|
|
|
name="UI Tag",
|
|
|
|
)
|
2018-03-01 01:26:02 +11:00
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
workspace = context.workspace
|
|
|
|
workspace.owner_ids.new(self.owner_id)
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
|
|
|
|
|
|
class WM_OT_owner_disable(Operator):
|
|
|
|
"""Enable workspace owner ID"""
|
|
|
|
bl_idname = "wm.owner_disable"
|
|
|
|
bl_label = "Disable UI Tag"
|
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
owner_id: StringProperty(
|
2018-06-26 22:56:39 +02:00
|
|
|
name="UI Tag",
|
|
|
|
)
|
2018-03-01 01:26:02 +11:00
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
workspace = context.workspace
|
|
|
|
owner_id = workspace.owner_ids[self.owner_id]
|
|
|
|
workspace.owner_ids.remove(owner_id)
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
|
|
|
2019-03-15 12:45:41 +11:00
|
|
|
class WM_OT_tool_set_by_id(Operator):
|
2018-05-13 08:59:50 +02:00
|
|
|
"""Set the tool by name (for keymaps)"""
|
2019-03-15 12:45:41 +11:00
|
|
|
bl_idname = "wm.tool_set_by_id"
|
2018-05-13 08:59:50 +02:00
|
|
|
bl_label = "Set Tool By Name"
|
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
name: StringProperty(
|
2019-03-15 12:45:41 +11:00
|
|
|
name="Identifier",
|
|
|
|
description="Identifier of the tool",
|
2018-06-26 22:56:39 +02:00
|
|
|
)
|
2018-07-11 22:18:09 +02:00
|
|
|
cycle: BoolProperty(
|
2018-06-26 22:56:39 +02:00
|
|
|
name="Cycle",
|
|
|
|
description="Cycle through tools in this group",
|
|
|
|
default=False,
|
|
|
|
options={'SKIP_SAVE'},
|
|
|
|
)
|
2018-05-13 08:59:50 +02:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
space_type: rna_space_type_prop
|
2018-05-30 22:15:10 +02:00
|
|
|
|
2018-11-15 17:12:33 +11:00
|
|
|
if use_toolbar_release_hack:
|
|
|
|
def invoke(self, context, event):
|
|
|
|
# Hack :S
|
|
|
|
if not self.properties.is_property_set("name"):
|
|
|
|
WM_OT_toolbar._key_held = False
|
|
|
|
return {'PASS_THROUGH'}
|
2018-11-16 09:19:33 +11:00
|
|
|
elif (WM_OT_toolbar._key_held == event.type) and (event.value != 'RELEASE'):
|
2018-11-15 17:12:33 +11:00
|
|
|
return {'PASS_THROUGH'}
|
2018-11-16 09:19:33 +11:00
|
|
|
WM_OT_toolbar._key_held = None
|
2018-11-15 17:12:33 +11:00
|
|
|
|
|
|
|
return self.execute(context)
|
|
|
|
|
2018-05-13 08:59:50 +02:00
|
|
|
def execute(self, context):
|
2018-05-30 22:15:10 +02:00
|
|
|
from bl_ui.space_toolsystem_common import (
|
2019-03-15 12:45:41 +11:00
|
|
|
activate_by_id,
|
|
|
|
activate_by_id_or_cycle,
|
2018-05-30 22:15:10 +02:00
|
|
|
)
|
2018-05-31 12:54:44 +02:00
|
|
|
|
|
|
|
if self.properties.is_property_set("space_type"):
|
|
|
|
space_type = self.space_type
|
|
|
|
else:
|
2018-05-16 18:41:11 +02:00
|
|
|
space_type = context.space_data.type
|
2018-05-30 22:15:10 +02:00
|
|
|
|
2019-03-15 12:45:41 +11:00
|
|
|
fn = activate_by_id_or_cycle if self.cycle else activate_by_id
|
2018-05-30 22:15:10 +02:00
|
|
|
if fn(context, space_type, self.name):
|
2018-05-13 08:59:50 +02:00
|
|
|
return {'FINISHED'}
|
|
|
|
else:
|
2018-10-26 10:42:31 +11:00
|
|
|
self.report({'WARNING'}, f"Tool {self.name!r:s} not found for space {space_type!r:s}.")
|
2018-05-13 08:59:50 +02:00
|
|
|
return {'CANCELLED'}
|
|
|
|
|
|
|
|
|
2018-05-18 22:27:59 +02:00
|
|
|
class WM_OT_toolbar(Operator):
|
|
|
|
bl_idname = "wm.toolbar"
|
|
|
|
bl_label = "Toolbar"
|
|
|
|
|
2018-09-03 14:22:58 +10:00
|
|
|
@classmethod
|
|
|
|
def poll(cls, context):
|
|
|
|
return context.space_data is not None
|
|
|
|
|
2018-11-16 09:19:33 +11:00
|
|
|
if use_toolbar_release_hack:
|
|
|
|
_key_held = None
|
2018-12-20 13:01:40 +11:00
|
|
|
|
2018-11-16 09:19:33 +11:00
|
|
|
def invoke(self, context, event):
|
|
|
|
WM_OT_toolbar._key_held = event.type
|
|
|
|
return self.execute(context)
|
|
|
|
|
2018-05-18 22:27:59 +02:00
|
|
|
def execute(self, context):
|
2018-11-20 11:15:37 +11:00
|
|
|
from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
|
|
|
|
from bl_keymap_utils import keymap_from_toolbar
|
2018-05-21 10:43:15 +02:00
|
|
|
|
2018-11-20 11:15:37 +11:00
|
|
|
space_type = context.space_data.type
|
2018-05-18 23:14:56 +02:00
|
|
|
cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type)
|
|
|
|
if cls is None:
|
|
|
|
return {'CANCELLED'}
|
2018-05-18 22:27:59 +02:00
|
|
|
|
2018-05-20 22:37:35 +02:00
|
|
|
wm = context.window_manager
|
2018-11-20 11:15:37 +11:00
|
|
|
keymap = keymap_from_toolbar.generate(context, space_type)
|
2018-05-20 22:37:35 +02:00
|
|
|
|
2018-05-18 22:27:59 +02:00
|
|
|
def draw_menu(popover, context):
|
2018-05-20 22:37:35 +02:00
|
|
|
layout = popover.layout
|
2018-11-12 11:56:13 +11:00
|
|
|
layout.operator_context = 'INVOKE_REGION_WIN'
|
2018-06-06 08:31:31 +02:00
|
|
|
cls.draw_cls(layout, context, detect_layout=False, scale_y=1.0)
|
2018-05-18 22:27:59 +02:00
|
|
|
|
2018-07-03 19:55:51 +02:00
|
|
|
wm.popover(draw_menu, ui_units_x=8, keymap=keymap)
|
2018-05-18 22:27:59 +02:00
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
|
|
|
2018-09-18 17:44:14 +02:00
|
|
|
class WM_MT_splash(Menu):
|
|
|
|
bl_label = "Splash"
|
|
|
|
|
|
|
|
def draw_setup(self, context):
|
2018-11-15 15:04:23 +01:00
|
|
|
wm = context.window_manager
|
2018-12-21 12:47:44 +11:00
|
|
|
# prefs = context.preferences
|
2018-11-15 15:04:23 +01:00
|
|
|
|
2018-09-18 17:44:14 +02:00
|
|
|
layout = self.layout
|
2018-11-15 15:04:23 +01:00
|
|
|
|
2018-09-18 17:44:14 +02:00
|
|
|
layout.operator_context = 'EXEC_DEFAULT'
|
|
|
|
|
|
|
|
layout.label(text="Quick Setup")
|
|
|
|
|
|
|
|
split = layout.split(factor=0.25)
|
|
|
|
split.label()
|
2018-09-20 07:11:01 +10:00
|
|
|
split = split.split(factor=2.0 / 3.0)
|
2018-09-18 17:44:14 +02:00
|
|
|
|
|
|
|
col = split.column()
|
|
|
|
|
2018-11-15 15:04:23 +01:00
|
|
|
col.label()
|
|
|
|
|
|
|
|
sub = col.split(factor=0.35)
|
|
|
|
row = sub.row()
|
|
|
|
row.alignment = 'RIGHT'
|
|
|
|
row.label(text="Shortcuts")
|
2018-11-15 15:04:23 +01:00
|
|
|
text = bpy.path.display_name(wm.keyconfigs.active.name)
|
2018-09-18 17:44:14 +02:00
|
|
|
if not text:
|
2018-11-15 15:04:23 +01:00
|
|
|
text = "Blender"
|
|
|
|
sub.menu("USERPREF_MT_keyconfigs", text=text)
|
|
|
|
|
2018-11-19 06:14:20 +11:00
|
|
|
kc = wm.keyconfigs.active
|
|
|
|
kc_prefs = kc.preferences
|
|
|
|
has_select_mouse = hasattr(kc_prefs, "select_mouse")
|
|
|
|
if has_select_mouse:
|
2018-11-16 08:28:58 +11:00
|
|
|
sub = col.split(factor=0.35)
|
|
|
|
row = sub.row()
|
|
|
|
row.alignment = 'RIGHT'
|
|
|
|
row.label(text="Select With")
|
2018-11-28 06:57:25 +11:00
|
|
|
sub.row().prop(kc_prefs, "select_mouse", expand=True)
|
2018-11-19 06:14:20 +11:00
|
|
|
has_select_mouse = True
|
|
|
|
|
2018-11-28 06:57:25 +11:00
|
|
|
has_spacebar_action = hasattr(kc_prefs, "spacebar_action")
|
|
|
|
if has_spacebar_action:
|
|
|
|
sub = col.split(factor=0.35)
|
|
|
|
row = sub.row()
|
|
|
|
row.alignment = 'RIGHT'
|
|
|
|
row.label(text="Spacebar")
|
|
|
|
sub.row().prop(kc_prefs, "spacebar_action", expand=True)
|
|
|
|
has_select_mouse = True
|
2018-09-18 17:44:14 +02:00
|
|
|
|
|
|
|
col.separator()
|
|
|
|
|
2018-11-15 15:04:23 +01:00
|
|
|
sub = col.split(factor=0.35)
|
|
|
|
row = sub.row()
|
|
|
|
row.alignment = 'RIGHT'
|
|
|
|
row.label(text="Theme")
|
2018-10-08 19:19:05 +02:00
|
|
|
label = bpy.types.USERPREF_MT_interface_theme_presets.bl_label
|
|
|
|
if label == "Presets":
|
|
|
|
label = "Blender Dark"
|
|
|
|
sub.menu("USERPREF_MT_interface_theme_presets", text=label)
|
|
|
|
|
2018-09-18 17:44:14 +02:00
|
|
|
# We need to make switching to a language easier first
|
2018-11-15 15:04:23 +01:00
|
|
|
#sub = col.split(factor=0.35)
|
|
|
|
#row = sub.row()
|
|
|
|
#row.alignment = 'RIGHT'
|
2018-12-20 13:01:40 +11:00
|
|
|
# row.label(text="Language:")
|
2018-12-21 12:47:44 +11:00
|
|
|
#prefs = context.preferences
|
|
|
|
#sub.prop(prefs.system, "language", text="")
|
2018-09-18 17:44:14 +02:00
|
|
|
|
2018-11-16 08:28:58 +11:00
|
|
|
# Keep height constant
|
2018-11-19 06:14:20 +11:00
|
|
|
if not has_select_mouse:
|
2018-11-16 08:28:58 +11:00
|
|
|
col.label()
|
2018-11-28 06:57:25 +11:00
|
|
|
if not has_spacebar_action:
|
|
|
|
col.label()
|
2018-11-15 15:04:23 +01:00
|
|
|
|
2018-09-18 17:44:14 +02:00
|
|
|
layout.label()
|
|
|
|
|
|
|
|
row = layout.row()
|
|
|
|
|
|
|
|
sub = row.row()
|
2019-03-02 00:21:05 +11:00
|
|
|
if bpy.types.PREFERENCES_OT_copy_prev.poll(context):
|
|
|
|
old_version = bpy.types.PREFERENCES_OT_copy_prev.previous_version()
|
|
|
|
sub.operator("preferences.copy_prev", text="Load %d.%d Settings" % old_version)
|
2018-09-18 17:44:14 +02:00
|
|
|
sub.operator("wm.save_userpref", text="Save New Settings")
|
|
|
|
else:
|
|
|
|
sub.label()
|
|
|
|
sub.label()
|
|
|
|
sub.operator("wm.save_userpref", text="Next")
|
|
|
|
|
|
|
|
layout.separator()
|
2018-11-28 16:36:48 +01:00
|
|
|
layout.separator()
|
2018-09-18 17:44:14 +02:00
|
|
|
|
|
|
|
def draw(self, context):
|
2018-12-21 12:47:44 +11:00
|
|
|
# Draw setup screen if no preferences have been saved yet.
|
2018-09-18 17:44:14 +02:00
|
|
|
import os
|
|
|
|
|
2019-04-09 23:16:11 +10:00
|
|
|
userconfig_path = bpy.utils.user_resource('CONFIG')
|
2019-01-04 16:24:11 +01:00
|
|
|
userdef_path = os.path.join(userconfig_path, "userpref.blend")
|
2018-09-18 17:44:14 +02:00
|
|
|
|
|
|
|
if not os.path.isfile(userdef_path):
|
|
|
|
self.draw_setup(context)
|
|
|
|
return
|
|
|
|
|
|
|
|
# Pass
|
|
|
|
layout = self.layout
|
|
|
|
layout.operator_context = 'EXEC_DEFAULT'
|
|
|
|
layout.emboss = 'PULLDOWN_MENU'
|
|
|
|
|
|
|
|
split = layout.split()
|
|
|
|
|
|
|
|
# Templates
|
|
|
|
col1 = split.column()
|
|
|
|
col1.label(text="New File")
|
|
|
|
|
|
|
|
bpy.types.TOPBAR_MT_file_new.draw_ex(col1, context, use_splash=True)
|
|
|
|
|
|
|
|
# Recent
|
|
|
|
col2 = split.column()
|
|
|
|
col2_title = col2.row()
|
|
|
|
|
|
|
|
found_recent = col2.template_recent_files()
|
|
|
|
|
|
|
|
if found_recent:
|
|
|
|
col2_title.label(text="Recent Files")
|
|
|
|
else:
|
|
|
|
# Links if no recent files
|
|
|
|
col2_title.label(text="Getting Started")
|
|
|
|
|
|
|
|
col2.operator(
|
|
|
|
"wm.url_open", text="Manual", icon='URL'
|
|
|
|
).url = "https://docs.blender.org/manual/en/dev/"
|
|
|
|
col2.operator(
|
|
|
|
"wm.url_open", text="Release Notes", icon='URL',
|
|
|
|
).url = "https://www.blender.org/download/releases/%d-%d/" % bpy.app.version[:2]
|
|
|
|
col2.operator(
|
|
|
|
"wm.url_open", text="Blender Website", icon='URL',
|
|
|
|
).url = "https://www.blender.org"
|
|
|
|
col2.operator(
|
|
|
|
"wm.url_open", text="Credits", icon='URL',
|
|
|
|
).url = "https://www.blender.org/about/credits/"
|
|
|
|
|
|
|
|
layout.separator()
|
|
|
|
|
|
|
|
split = layout.split()
|
|
|
|
|
|
|
|
col1 = split.column()
|
|
|
|
sub = col1.row()
|
|
|
|
sub.operator_context = 'INVOKE_DEFAULT'
|
|
|
|
sub.operator("wm.open_mainfile", text="Open...", icon='FILE_FOLDER')
|
|
|
|
col1.operator("wm.recover_last_session", icon='RECOVER_LAST')
|
|
|
|
|
|
|
|
col2 = split.column()
|
|
|
|
if found_recent:
|
|
|
|
col2.operator(
|
|
|
|
"wm.url_open", text="Release Notes", icon='URL',
|
|
|
|
).url = "https://www.blender.org/download/releases/%d-%d/" % bpy.app.version[:2]
|
|
|
|
col2.operator(
|
|
|
|
"wm.url_open", text="Development Fund", icon='URL'
|
2018-10-18 18:55:50 +02:00
|
|
|
).url = "https://fund.blender.org"
|
2018-09-18 17:44:14 +02:00
|
|
|
else:
|
|
|
|
col2.operator(
|
|
|
|
"wm.url_open", text="Development Fund", icon='URL'
|
2018-10-18 18:55:50 +02:00
|
|
|
).url = "https://fund.blender.org"
|
2018-09-18 17:44:14 +02:00
|
|
|
col2.operator(
|
|
|
|
"wm.url_open", text="Donate", icon='URL'
|
|
|
|
).url = "https://www.blender.org/foundation/donation-payment/"
|
|
|
|
|
|
|
|
layout.separator()
|
2018-11-28 16:36:48 +01:00
|
|
|
layout.separator()
|
2018-09-18 17:44:14 +02:00
|
|
|
|
|
|
|
|
2018-10-22 17:18:42 +02:00
|
|
|
class WM_OT_drop_blend_file(Operator):
|
|
|
|
bl_idname = "wm.drop_blend_file"
|
|
|
|
bl_label = "Handle dropped .blend file"
|
|
|
|
bl_options = {'INTERNAL'}
|
|
|
|
|
|
|
|
filepath: StringProperty()
|
|
|
|
|
2019-05-09 09:15:01 +10:00
|
|
|
def invoke(self, context, _event):
|
2018-10-22 17:18:42 +02:00
|
|
|
context.window_manager.popup_menu(self.draw_menu, title=bpy.path.basename(self.filepath), icon='QUESTION')
|
2019-03-04 22:06:23 +11:00
|
|
|
return {'FINISHED'}
|
2018-10-22 17:18:42 +02:00
|
|
|
|
2019-05-09 09:15:01 +10:00
|
|
|
def draw_menu(self, menu, _context):
|
2018-10-22 17:18:42 +02:00
|
|
|
layout = menu.layout
|
|
|
|
|
|
|
|
col = layout.column()
|
|
|
|
col.operator_context = 'EXEC_DEFAULT'
|
|
|
|
col.operator("wm.open_mainfile", text="Open", icon='FILE_FOLDER').filepath = self.filepath
|
|
|
|
|
|
|
|
layout.separator()
|
|
|
|
col = layout.column()
|
|
|
|
col.operator_context = 'INVOKE_DEFAULT'
|
|
|
|
col.operator("wm.link", text="Link...", icon='LINK_BLEND').filepath = self.filepath
|
|
|
|
col.operator("wm.append", text="Append...", icon='APPEND_BLEND').filepath = self.filepath
|
|
|
|
|
2018-12-20 13:01:40 +11:00
|
|
|
|
2017-03-18 20:03:24 +11:00
|
|
|
classes = (
|
|
|
|
WM_OT_context_collection_boolean_set,
|
|
|
|
WM_OT_context_cycle_array,
|
|
|
|
WM_OT_context_cycle_enum,
|
|
|
|
WM_OT_context_cycle_int,
|
|
|
|
WM_OT_context_menu_enum,
|
|
|
|
WM_OT_context_modal_mouse,
|
|
|
|
WM_OT_context_pie_enum,
|
|
|
|
WM_OT_context_scale_float,
|
|
|
|
WM_OT_context_scale_int,
|
|
|
|
WM_OT_context_set_boolean,
|
|
|
|
WM_OT_context_set_enum,
|
|
|
|
WM_OT_context_set_float,
|
|
|
|
WM_OT_context_set_id,
|
|
|
|
WM_OT_context_set_int,
|
|
|
|
WM_OT_context_set_string,
|
|
|
|
WM_OT_context_set_value,
|
|
|
|
WM_OT_context_toggle,
|
|
|
|
WM_OT_context_toggle_enum,
|
|
|
|
WM_OT_doc_view,
|
|
|
|
WM_OT_doc_view_manual,
|
2018-10-22 17:18:42 +02:00
|
|
|
WM_OT_drop_blend_file,
|
2017-03-18 20:03:24 +11:00
|
|
|
WM_OT_operator_cheat_sheet,
|
|
|
|
WM_OT_operator_pie_enum,
|
|
|
|
WM_OT_path_open,
|
|
|
|
WM_OT_properties_add,
|
|
|
|
WM_OT_properties_context_change,
|
|
|
|
WM_OT_properties_edit,
|
|
|
|
WM_OT_properties_remove,
|
|
|
|
WM_OT_sysinfo,
|
2018-03-01 01:26:02 +11:00
|
|
|
WM_OT_owner_disable,
|
|
|
|
WM_OT_owner_enable,
|
2017-03-18 20:03:24 +11:00
|
|
|
WM_OT_url_open,
|
2019-03-15 12:45:41 +11:00
|
|
|
WM_OT_tool_set_by_id,
|
2018-05-18 22:27:59 +02:00
|
|
|
WM_OT_toolbar,
|
2018-09-18 17:44:14 +02:00
|
|
|
WM_MT_splash,
|
2017-03-25 09:29:51 +11:00
|
|
|
)
|