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
|
2011-08-12 06:57:00 +00:00
|
|
|
from bpy.types import Menu, Operator
|
2011-08-18 12:20:10 +00:00
|
|
|
from bpy.props import (StringProperty,
|
|
|
|
BoolProperty,
|
|
|
|
IntProperty,
|
|
|
|
FloatProperty,
|
|
|
|
EnumProperty,
|
|
|
|
)
|
2011-06-27 07:51:52 +00:00
|
|
|
|
2010-08-02 22:52:55 +00:00
|
|
|
from rna_prop_ui import rna_idprop_ui_prop_get, rna_idprop_ui_prop_clear
|
2009-12-13 13:59:16 +00:00
|
|
|
|
2012-01-01 08:52:54 +00:00
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class MESH_OT_delete_edgeloop(Operator):
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Delete an edge loop by merging the faces on each side """ \
|
|
|
|
"""to a single face loop"""
|
2009-11-04 20:21:08 +00:00
|
|
|
bl_idname = "mesh.delete_edgeloop"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Delete Edge Loop"
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2012-07-19 08:04:12 +00:00
|
|
|
@classmethod
|
|
|
|
def poll(cls, context):
|
|
|
|
return bpy.ops.transform.edge_slide.poll()
|
|
|
|
|
2009-11-04 20:21:08 +00:00
|
|
|
def execute(self, context):
|
2012-11-15 06:10:09 +00:00
|
|
|
mesh = context.object.data
|
|
|
|
use_mirror_x = mesh.use_mirror_x
|
|
|
|
mesh.use_mirror_x = False
|
2010-12-05 00:08:25 +00:00
|
|
|
if 'FINISHED' in bpy.ops.transform.edge_slide(value=1.0):
|
2010-12-04 13:46:25 +00:00
|
|
|
bpy.ops.mesh.select_more()
|
|
|
|
bpy.ops.mesh.remove_doubles()
|
2012-11-15 06:10:09 +00:00
|
|
|
ret = {'FINISHED'}
|
|
|
|
else:
|
|
|
|
ret = {'CANCELLED'}
|
|
|
|
mesh.use_mirror_x = use_mirror_x
|
|
|
|
return ret
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2011-08-18 12:20:10 +00:00
|
|
|
rna_path_prop = StringProperty(
|
2011-09-15 13:20:18 +00:00
|
|
|
name="Context Attributes",
|
2012-07-07 13:51:07 +00:00
|
|
|
description="RNA context string",
|
2011-08-18 12:20:10 +00:00
|
|
|
maxlen=1024,
|
|
|
|
)
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2011-08-18 12:20:10 +00:00
|
|
|
rna_reverse_prop = BoolProperty(
|
2011-09-15 13:20:18 +00:00
|
|
|
name="Reverse",
|
|
|
|
description="Cycle backwards",
|
2011-08-18 12:20:10 +00:00
|
|
|
default=False,
|
|
|
|
)
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2011-08-18 12:20:10 +00:00
|
|
|
rna_relative_prop = BoolProperty(
|
2011-09-15 13:20:18 +00:00
|
|
|
name="Relative",
|
|
|
|
description="Apply relative to the current value (delta)",
|
2011-08-18 12:20:10 +00:00
|
|
|
default=False,
|
|
|
|
)
|
2010-01-31 14:46:28 +00: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
|
2011-08-18 15:25:18 +00:00
|
|
|
except AttributeError as e:
|
|
|
|
if str(e).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
|
|
|
#
|
|
|
|
# When we cant 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 BRUSH_OT_active_index_set(Operator):
|
2012-06-19 22:17:19 +00:00
|
|
|
"""Set active sculpt/paint brush from it's number"""
|
2011-04-01 04:22:30 +00:00
|
|
|
bl_idname = "brush.active_index_set"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Set Brush Number"
|
2010-11-10 08:11:58 +00:00
|
|
|
|
2011-08-18 12:20:10 +00:00
|
|
|
mode = StringProperty(
|
2012-02-27 18:14:56 +00:00
|
|
|
name="Mode",
|
2011-09-15 13:20:18 +00:00
|
|
|
description="Paint mode to set brush for",
|
2011-08-18 12:20:10 +00:00
|
|
|
maxlen=1024,
|
|
|
|
)
|
|
|
|
index = IntProperty(
|
2012-02-27 18:14:56 +00:00
|
|
|
name="Number",
|
2011-09-15 13:20:18 +00:00
|
|
|
description="Brush number",
|
2011-08-18 12:20:10 +00:00
|
|
|
)
|
2010-11-10 08:11:58 +00:00
|
|
|
|
2011-01-01 07:20:34 +00:00
|
|
|
_attr_dict = {"sculpt": "use_paint_sculpt",
|
2010-11-10 08:11:58 +00:00
|
|
|
"vertex_paint": "use_paint_vertex",
|
|
|
|
"weight_paint": "use_paint_weight",
|
2011-08-18 12:20:10 +00:00
|
|
|
"image_paint": "use_paint_image",
|
|
|
|
}
|
2010-11-10 08:11:58 +00:00
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
attr = self._attr_dict.get(self.mode)
|
|
|
|
if attr is None:
|
|
|
|
return {'CANCELLED'}
|
|
|
|
|
2012-01-17 17:57:20 +00:00
|
|
|
toolsettings = context.tool_settings
|
2010-11-10 08:11:58 +00:00
|
|
|
for i, brush in enumerate((cur for cur in bpy.data.brushes if getattr(cur, attr))):
|
2011-04-01 06:14:39 +00:00
|
|
|
if i == self.index:
|
2012-01-17 17:57:20 +00:00
|
|
|
getattr(toolsettings, self.mode).brush = brush
|
2010-11-10 08:11:58 +00:00
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
|
|
return {'CANCELLED'}
|
|
|
|
|
2011-01-01 07:20:34 +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
|
|
|
|
2010-06-14 03:52:10 +00:00
|
|
|
data_path = rna_path_prop
|
2011-08-18 12:20:10 +00:00
|
|
|
value = BoolProperty(
|
2011-09-15 13:20:18 +00:00
|
|
|
name="Value",
|
|
|
|
description="Assignment value",
|
2011-08-18 12:20:10 +00:00
|
|
|
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
|
|
|
|
2010-06-14 03:52:10 +00:00
|
|
|
data_path = rna_path_prop
|
2011-08-18 12:20:10 +00:00
|
|
|
value = IntProperty(
|
2011-09-15 13:20:18 +00:00
|
|
|
name="Value",
|
|
|
|
description="Assign value",
|
2011-08-18 12:20:10 +00:00
|
|
|
default=0,
|
|
|
|
)
|
2010-01-04 08:24:24 +00: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_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"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Context Set"
|
2011-06-06 11:56:54 +00:00
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2010-03-08 23:34:38 +00:00
|
|
|
|
2010-06-14 03:52:10 +00:00
|
|
|
data_path = rna_path_prop
|
2011-08-18 12:20:10 +00:00
|
|
|
value = FloatProperty(
|
2011-09-15 13:20:18 +00:00
|
|
|
name="Value",
|
|
|
|
description="Assign value",
|
2011-08-18 12:20:10 +00:00
|
|
|
default=1.0,
|
|
|
|
)
|
|
|
|
always_step = BoolProperty(
|
2011-09-15 13:20:18 +00:00
|
|
|
name="Always Step",
|
2011-09-19 14:00:42 +00:00
|
|
|
description="Always adjust the value by a minimum of 1 when 'value' is not 1.0",
|
2011-08-18 12:20:10 +00:00
|
|
|
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
|
|
|
|
2010-06-14 03:52:10 +00:00
|
|
|
data_path = rna_path_prop
|
2011-08-18 12:20:10 +00:00
|
|
|
value = FloatProperty(
|
2011-09-15 13:20:18 +00:00
|
|
|
name="Value",
|
|
|
|
description="Assignment value",
|
2011-08-18 12:20:10 +00:00
|
|
|
default=0.0,
|
|
|
|
)
|
2010-01-04 08:24:24 +00: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
|
|
|
|
2010-06-14 03:52:10 +00:00
|
|
|
data_path = rna_path_prop
|
2011-08-18 12:20:10 +00:00
|
|
|
value = StringProperty(
|
2011-09-15 13:20:18 +00:00
|
|
|
name="Value",
|
|
|
|
description="Assign value",
|
2011-08-18 12:20:10 +00:00
|
|
|
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
|
|
|
|
2010-06-14 03:52:10 +00:00
|
|
|
data_path = rna_path_prop
|
2011-08-18 12:20:10 +00:00
|
|
|
value = StringProperty(
|
2011-09-15 13:20:18 +00:00
|
|
|
name="Value",
|
|
|
|
description="Assignment value (as a string)",
|
2011-08-18 12:20:10 +00:00
|
|
|
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
|
|
|
|
2010-06-14 03:52:10 +00:00
|
|
|
data_path = rna_path_prop
|
2011-08-18 12:20:10 +00:00
|
|
|
value = StringProperty(
|
2011-09-15 13:20:18 +00:00
|
|
|
name="Value",
|
|
|
|
description="Assignment value (as a string)",
|
2011-08-18 12:20:10 +00:00
|
|
|
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
|
|
|
|
2010-06-14 03:52:10 +00: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
|
|
|
|
2010-06-14 03:52:10 +00:00
|
|
|
data_path = rna_path_prop
|
2011-08-18 12:20:10 +00:00
|
|
|
value_1 = StringProperty(
|
2011-09-15 13:20:18 +00:00
|
|
|
name="Value",
|
|
|
|
description="Toggle enum",
|
2011-08-18 12:20:10 +00:00
|
|
|
maxlen=1024,
|
|
|
|
)
|
|
|
|
value_2 = StringProperty(
|
2011-09-15 13:20:18 +00:00
|
|
|
name="Value",
|
|
|
|
description="Toggle enum",
|
2011-08-18 12:20:10 +00:00
|
|
|
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
|
|
|
|
2011-08-18 15:25:18 +00:00
|
|
|
exec("context.%s = ('%s', '%s')[context.%s != '%s']" %
|
|
|
|
(data_path, self.value_1,
|
|
|
|
self.value_2, data_path,
|
2011-08-18 12:20:10 +00:00
|
|
|
self.value_2,
|
|
|
|
))
|
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, """ \
|
|
|
|
"""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
|
|
|
|
2010-06-14 03:52:10 +00:00
|
|
|
data_path = rna_path_prop
|
2009-11-04 20:21:08 +00:00
|
|
|
reverse = rna_reverse_prop
|
|
|
|
|
|
|
|
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
|
|
|
|
2010-06-14 03:52:10 +00:00
|
|
|
if value != eval("context.%s" % data_path):
|
2012-02-08 04:37:37 +00:00
|
|
|
# relies on rna clamping integers out of the range
|
2010-09-09 18:03:57 +00:00
|
|
|
if self.reverse:
|
2010-10-03 01:44:00 +00:00
|
|
|
value = (1 << 31) - 1
|
2009-11-04 20:21:08 +00:00
|
|
|
else:
|
2010-10-03 01:44:00 +00:00
|
|
|
value = -1 << 31
|
2010-01-03 02:24:53 +00:00
|
|
|
|
2011-08-18 15:25:18 +00: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
|
|
|
|
2010-06-14 03:52:10 +00:00
|
|
|
data_path = rna_path_prop
|
2009-11-04 20:21:08 +00:00
|
|
|
reverse = rna_reverse_prop
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
# Have the info we need, advance to the next item
|
2010-09-09 18:03:57 +00:00
|
|
|
if self.reverse:
|
2009-11-04 20:21:08 +00:00
|
|
|
if orig_index == 0:
|
|
|
|
advance_enum = enums[-1]
|
|
|
|
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:
|
|
|
|
advance_enum = enums[0]
|
|
|
|
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 """ \
|
|
|
|
"""(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
|
|
|
|
|
|
|
data_path = rna_path_prop
|
|
|
|
reverse = rna_reverse_prop
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_MT_context_menu_enum(Menu):
|
2011-02-15 18:12:41 +00:00
|
|
|
bl_label = ""
|
|
|
|
data_path = "" # BAD DESIGN, set from operator below.
|
2011-02-16 02:25:03 +00:00
|
|
|
|
2011-02-15 18:12:41 +00:00
|
|
|
def draw(self, context):
|
|
|
|
data_path = self.data_path
|
|
|
|
value = context_path_validate(bpy.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)
|
2011-02-16 02:25:03 +00:00
|
|
|
|
2011-05-01 07:39:21 +00:00
|
|
|
values = [(i.name, i.identifier) for i in value_base.bl_rna.properties[prop_string].enum_items]
|
2011-02-15 18:12:41 +00:00
|
|
|
|
|
|
|
for name, identifier in values:
|
2012-01-01 13:09:58 +00:00
|
|
|
props = self.layout.operator("wm.context_set_enum", text=name)
|
|
|
|
props.data_path = data_path
|
|
|
|
props.value = identifier
|
2011-02-15 18:12:41 +00:00
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_context_menu_enum(Operator):
|
2011-02-15 18:12:41 +00:00
|
|
|
bl_idname = "wm.context_menu_enum"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Context Enum Menu"
|
2011-06-06 11:56:54 +00:00
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2011-02-15 18:12:41 +00:00
|
|
|
data_path = rna_path_prop
|
2011-02-16 02:25:03 +00:00
|
|
|
|
2011-02-15 18:12:41 +00:00
|
|
|
def execute(self, context):
|
|
|
|
data_path = self.data_path
|
|
|
|
WM_MT_context_menu_enum.data_path = data_path
|
|
|
|
bpy.ops.wm.call_menu(name="WM_MT_context_menu_enum")
|
|
|
|
return {'PASS_THROUGH'}
|
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_context_set_id(Operator):
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Toggle a context value"""
|
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
|
|
|
|
|
|
|
data_path = rna_path_prop
|
2011-08-18 12:20:10 +00:00
|
|
|
value = StringProperty(
|
2011-09-15 13:20:18 +00:00
|
|
|
name="Value",
|
|
|
|
description="Assign value",
|
2011-08-18 12:20:10 +00:00
|
|
|
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(
|
2011-09-15 13:20:18 +00:00
|
|
|
name="Doc ID",
|
2011-08-18 12:20:10 +00:00
|
|
|
maxlen=1024,
|
|
|
|
options={'HIDDEN'},
|
|
|
|
)
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2011-08-18 12:20:10 +00:00
|
|
|
doc_new = StringProperty(
|
2011-09-15 13:20:18 +00:00
|
|
|
name="Edit Description",
|
2011-08-18 12:20:10 +00:00
|
|
|
maxlen=1024,
|
|
|
|
)
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2011-06-27 07:51:52 +00:00
|
|
|
data_path_iter = StringProperty(
|
2011-09-19 14:00:42 +00: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(
|
|
|
|
description="The data path from each iterable to the value (int or float)")
|
|
|
|
|
|
|
|
|
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'}
|
|
|
|
|
|
|
|
data_path_iter = data_path_iter
|
|
|
|
data_path_item = data_path_item
|
|
|
|
|
2011-08-18 12:20:10 +00:00
|
|
|
type = EnumProperty(
|
|
|
|
name="Type",
|
|
|
|
items=(('TOGGLE', "Toggle", ""),
|
|
|
|
('ENABLE', "Enable", ""),
|
|
|
|
('DISABLE', "Disable", ""),
|
|
|
|
),
|
|
|
|
)
|
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"
|
2011-08-18 16:01:11 +00:00
|
|
|
bl_options = {'GRAB_POINTER', 'BLOCKING', 'UNDO', 'INTERNAL'}
|
2010-02-22 23:32:58 +00:00
|
|
|
|
2011-06-27 07:51:52 +00:00
|
|
|
data_path_iter = data_path_iter
|
|
|
|
data_path_item = data_path_item
|
2012-02-25 19:49:23 +00:00
|
|
|
header_text = StringProperty(
|
|
|
|
name="Header Text",
|
|
|
|
description="Text to display in header during scale",
|
|
|
|
)
|
2010-02-22 23:32:58 +00:00
|
|
|
|
2011-08-18 12:20:10 +00:00
|
|
|
input_scale = FloatProperty(
|
2011-09-15 13:20:18 +00:00
|
|
|
description="Scale the mouse movement by this value before applying the delta",
|
2011-08-18 12:20:10 +00:00
|
|
|
default=0.01,
|
|
|
|
)
|
|
|
|
invert = BoolProperty(
|
2011-09-15 13:20:18 +00:00
|
|
|
description="Invert the mouse input",
|
2011-08-18 12:20:10 +00:00
|
|
|
default=False,
|
|
|
|
)
|
2010-02-21 14:05:02 +00:00
|
|
|
initial_x = IntProperty(options={'HIDDEN'})
|
|
|
|
|
|
|
|
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)"
|
|
|
|
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()
|
2012-02-25 19:49:23 +00:00
|
|
|
context.area.header_text_set()
|
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()
|
2012-02-25 19:49:23 +00:00
|
|
|
context.area.header_text_set()
|
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):
|
2012-02-08 04:37:37 +00:00
|
|
|
"Open a website in the web-browser"
|
2010-03-14 20:07:15 +00:00
|
|
|
bl_idname = "wm.url_open"
|
|
|
|
bl_label = ""
|
|
|
|
|
2011-08-18 12:20:10 +00:00
|
|
|
url = StringProperty(
|
|
|
|
name="URL",
|
|
|
|
description="URL to open",
|
|
|
|
)
|
2010-03-14 20:07:15 +00:00
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
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):
|
2010-07-04 10:02:30 +00:00
|
|
|
"Open a path in a file browser"
|
|
|
|
bl_idname = "wm.path_open"
|
|
|
|
bl_label = ""
|
|
|
|
|
2011-08-18 12:20:10 +00:00
|
|
|
filepath = StringProperty(
|
|
|
|
subtype='FILE_PATH',
|
2012-05-12 21:11:48 +00:00
|
|
|
options={'SKIP_SAVE'},
|
2011-08-18 12:20:10 +00:00
|
|
|
)
|
2010-07-04 10:02:30 +00:00
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
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":
|
|
|
|
subprocess.Popen(["open", filepath])
|
2010-07-04 10:02:30 +00:00
|
|
|
else:
|
|
|
|
try:
|
2011-12-22 03:56:21 +00:00
|
|
|
subprocess.Popen(["xdg-open", filepath])
|
2010-07-04 10:02:30 +00:00
|
|
|
except OSError:
|
|
|
|
# xdg-open *should* be supported by recent Gnome, KDE, Xfce
|
|
|
|
pass
|
|
|
|
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
|
|
|
2012-06-01 20:38:40 +00:00
|
|
|
def _wm_doc_get_id(doc_id, do_url=True, url_prefix=""):
|
|
|
|
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
|
|
|
|
|
|
|
|
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))
|
|
|
|
else:
|
|
|
|
rna = "bpy.ops.%s.%s" % (class_name, class_prop)
|
|
|
|
else:
|
|
|
|
|
|
|
|
# detect if this is a inherited member and use that name instead
|
|
|
|
rna_parent = getattr(bpy.types, class_name).bl_rna
|
|
|
|
rna_prop = rna_parent.properties[class_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
|
|
|
|
|
|
|
|
if do_url:
|
|
|
|
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))
|
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"
|
|
|
|
|
|
|
|
doc_id = doc_id
|
|
|
|
|
|
|
|
@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)
|
2012-06-03 11:01:18 +00:00
|
|
|
from fnmatch import fnmatch
|
2012-06-01 20:38:40 +00:00
|
|
|
for pattern, url_suffix in url_mapping:
|
2012-06-03 11:01:18 +00:00
|
|
|
if fnmatch(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
|
|
|
url = None
|
|
|
|
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
|
|
|
|
break
|
2012-09-13 00:26:01 +00:00
|
|
|
return url
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
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:
|
|
|
|
self.report({'WARNING'}, "No reference available %r, "
|
|
|
|
"Update info in 'rna_wiki_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):
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Load online reference docs"""
|
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
|
|
|
|
|
|
|
doc_id = doc_id
|
2011-04-01 15:13:58 +00:00
|
|
|
if bpy.app.version_cycle == "release":
|
2011-08-18 12:20:10 +00:00
|
|
|
_prefix = ("http://www.blender.org/documentation/blender_python_api_%s%s_release" %
|
|
|
|
("_".join(str(v) for v in bpy.app.version[:2]), bpy.app.version_char))
|
2011-04-01 15:13:58 +00:00
|
|
|
else:
|
2011-08-18 12:20:10 +00:00
|
|
|
_prefix = ("http://www.blender.org/documentation/blender_python_api_%s" %
|
|
|
|
"_".join(str(v) for v in bpy.app.version))
|
2009-11-04 20:21:08 +00: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-12 06:57:00 +00:00
|
|
|
class WM_OT_doc_edit(Operator):
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Load online reference docs"""
|
2009-11-04 20:21:08 +00:00
|
|
|
bl_idname = "wm.doc_edit"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Edit Documentation"
|
2009-11-04 20:21:08 +00:00
|
|
|
|
|
|
|
doc_id = doc_id
|
|
|
|
doc_new = doc_new
|
|
|
|
|
|
|
|
_url = "http://www.mindrones.com/blender/svn/xmlrpc.php"
|
|
|
|
|
|
|
|
def _send_xmlrpc(self, data_dict):
|
|
|
|
print("sending data:", data_dict)
|
|
|
|
|
|
|
|
import xmlrpc.client
|
2011-12-22 03:56:21 +00:00
|
|
|
user = "blenderuser"
|
|
|
|
pwd = "blender>user"
|
2009-11-04 20:21:08 +00:00
|
|
|
|
|
|
|
docblog = xmlrpc.client.ServerProxy(self._url)
|
|
|
|
docblog.metaWeblog.newPost(1, user, pwd, data_dict, 1)
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
2010-09-09 18:03:57 +00:00
|
|
|
doc_id = self.doc_id
|
|
|
|
doc_new = self.doc_new
|
2009-12-13 13:59:16 +00:00
|
|
|
|
|
|
|
class_name, class_prop = doc_id.split('.')
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2009-12-13 13:59:16 +00:00
|
|
|
if not doc_new:
|
2010-03-20 19:22:34 +00:00
|
|
|
self.report({'ERROR'}, "No input given for '%s'" % doc_id)
|
|
|
|
return {'CANCELLED'}
|
2009-11-04 20:21:08 +00:00
|
|
|
|
|
|
|
# check if this is an operator
|
|
|
|
op_name = class_name.upper() + '_OT_' + class_prop
|
|
|
|
op_class = getattr(bpy.types, op_name, None)
|
|
|
|
|
|
|
|
# Upload this to the web server
|
|
|
|
upload = {}
|
|
|
|
|
|
|
|
if op_class:
|
|
|
|
rna = op_class.bl_rna
|
|
|
|
doc_orig = rna.description
|
2009-12-13 13:59:16 +00:00
|
|
|
if doc_orig == doc_new:
|
2009-12-24 21:17:14 +00:00
|
|
|
return {'RUNNING_MODAL'}
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2009-12-13 13:59:16 +00:00
|
|
|
print("op - old:'%s' -> new:'%s'" % (doc_orig, doc_new))
|
|
|
|
upload["title"] = 'OPERATOR %s:%s' % (doc_id, doc_orig)
|
2009-11-04 20:21:08 +00:00
|
|
|
else:
|
|
|
|
rna = getattr(bpy.types, class_name).bl_rna
|
|
|
|
doc_orig = rna.properties[class_prop].description
|
2009-12-13 13:59:16 +00:00
|
|
|
if doc_orig == doc_new:
|
2009-12-24 21:17:14 +00:00
|
|
|
return {'RUNNING_MODAL'}
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2009-12-13 13:59:16 +00:00
|
|
|
print("rna - old:'%s' -> new:'%s'" % (doc_orig, doc_new))
|
|
|
|
upload["title"] = 'RNA %s:%s' % (doc_id, doc_orig)
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2009-12-13 13:59:16 +00:00
|
|
|
upload["description"] = doc_new
|
2009-11-04 20:21:08 +00:00
|
|
|
|
|
|
|
self._send_xmlrpc(upload)
|
|
|
|
|
2009-12-24 19:50:43 +00:00
|
|
|
return {'FINISHED'}
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2010-03-20 19:22:34 +00:00
|
|
|
def draw(self, context):
|
|
|
|
layout = self.layout
|
2011-09-15 13:20:18 +00:00
|
|
|
layout.label(text="Descriptor ID: '%s'" % self.doc_id)
|
2010-10-27 22:15:55 +00:00
|
|
|
layout.prop(self, "doc_new", text="")
|
2010-03-20 19:22:34 +00:00
|
|
|
|
2009-11-04 20:21:08 +00:00
|
|
|
def invoke(self, context, event):
|
2010-09-02 04:53:05 +00:00
|
|
|
wm = context.window_manager
|
2010-03-28 10:52:24 +00:00
|
|
|
return wm.invoke_props_dialog(self, width=600)
|
2009-11-04 20:21:08 +00:00
|
|
|
|
|
|
|
|
2011-08-18 12:20:10 +00:00
|
|
|
rna_path = StringProperty(
|
2011-09-15 13:20:18 +00:00
|
|
|
name="Property Edit",
|
|
|
|
description="Property data_path edit",
|
2011-08-18 12:20:10 +00:00
|
|
|
maxlen=1024,
|
|
|
|
options={'HIDDEN'},
|
|
|
|
)
|
|
|
|
|
|
|
|
rna_value = StringProperty(
|
2011-09-15 13:20:18 +00:00
|
|
|
name="Property Value",
|
|
|
|
description="Property value edit",
|
2011-08-18 12:20:10 +00:00
|
|
|
maxlen=1024,
|
|
|
|
)
|
2010-08-02 02:55:12 +00:00
|
|
|
|
2011-08-18 12:20:10 +00:00
|
|
|
rna_property = StringProperty(
|
2011-09-15 13:20:18 +00:00
|
|
|
name="Property Name",
|
|
|
|
description="Property name edit",
|
2011-08-18 12:20:10 +00:00
|
|
|
maxlen=1024,
|
|
|
|
)
|
2010-08-02 02:55:12 +00:00
|
|
|
|
2011-08-18 12:20:10 +00:00
|
|
|
rna_min = FloatProperty(
|
2011-09-15 13:20:18 +00:00
|
|
|
name="Min",
|
2011-08-18 12:20:10 +00:00
|
|
|
default=0.0,
|
|
|
|
precision=3,
|
|
|
|
)
|
2010-08-02 02:55:12 +00:00
|
|
|
|
2011-08-18 12:20:10 +00:00
|
|
|
rna_max = FloatProperty(
|
|
|
|
name="Max",
|
|
|
|
default=1.0,
|
|
|
|
precision=3,
|
|
|
|
)
|
2010-08-02 02:55:12 +00:00
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_properties_edit(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_edit"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Edit Property"
|
2011-01-01 07:20:34 +00:00
|
|
|
bl_options = {'REGISTER'} # only because invoke_props_popup requires.
|
2010-08-02 02:55:12 +00:00
|
|
|
|
|
|
|
data_path = rna_path
|
|
|
|
property = rna_property
|
|
|
|
value = rna_value
|
|
|
|
min = rna_min
|
|
|
|
max = rna_max
|
2011-08-18 12:20:10 +00:00
|
|
|
description = StringProperty(
|
2012-10-04 18:21:34 +00:00
|
|
|
name="Tooltip",
|
2011-08-18 12:20:10 +00:00
|
|
|
)
|
2010-08-02 02:55:12 +00:00
|
|
|
|
|
|
|
def execute(self, context):
|
2010-09-09 18:03:57 +00:00
|
|
|
data_path = self.data_path
|
|
|
|
value = self.value
|
|
|
|
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
|
|
|
|
|
|
|
try:
|
|
|
|
value_eval = eval(value)
|
2012-12-08 09:43:02 +00:00
|
|
|
# assert else None -> None, not "None", see [#33431]
|
|
|
|
assert(type(value_eval) in {str, float, int, bool, tuple, list})
|
2010-08-02 02:55:12 +00:00
|
|
|
except:
|
|
|
|
value_eval = value
|
|
|
|
|
|
|
|
# First remove
|
|
|
|
item = eval("context.%s" % data_path)
|
|
|
|
|
|
|
|
rna_idprop_ui_prop_clear(item, prop_old)
|
|
|
|
exec_str = "del item['%s']" % prop_old
|
|
|
|
# print(exec_str)
|
|
|
|
exec(exec_str)
|
|
|
|
|
|
|
|
# Reassign
|
|
|
|
exec_str = "item['%s'] = %s" % (prop, repr(value_eval))
|
|
|
|
# print(exec_str)
|
|
|
|
exec(exec_str)
|
|
|
|
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}:
|
2011-12-22 03:56:21 +00:00
|
|
|
prop_ui["soft_min"] = prop_ui["min"] = prop_type(self.min)
|
|
|
|
prop_ui["soft_max"] = prop_ui["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
|
|
|
|
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'}
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
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
|
|
|
|
|
|
|
# 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
|
|
|
|
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
|
|
|
|
2010-03-14 23:26:17 +00:00
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_properties_add(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_add"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Add Property"
|
2012-03-30 03:33:42 +00:00
|
|
|
bl_options = {'UNDO'}
|
2010-02-14 11:21:21 +00:00
|
|
|
|
2010-08-02 02:55:12 +00: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):
|
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
|
|
|
|
|
2012-10-26 10:54:02 +00:00
|
|
|
prop = unique_name(item.keys())
|
|
|
|
|
|
|
|
item[prop] = 1.0
|
|
|
|
|
|
|
|
# not essential, but without this we get [#31661]
|
|
|
|
prop_ui = rna_idprop_ui_prop_get(item, prop)
|
|
|
|
prop_ui["soft_min"] = prop_ui["min"] = 0.0
|
|
|
|
prop_ui["soft_max"] = prop_ui["max"] = 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):
|
2011-04-25 13:47:15 +00:00
|
|
|
"Change the context tab in a Properties Window"
|
|
|
|
bl_idname = "wm.properties_context_change"
|
|
|
|
bl_label = ""
|
|
|
|
|
2011-08-18 12:20:10 +00:00
|
|
|
context = StringProperty(
|
2011-09-15 13:20:18 +00:00
|
|
|
name="Context",
|
2012-05-21 16:52:04 +00:00
|
|
|
maxlen=64,
|
2011-08-18 12:20:10 +00:00
|
|
|
)
|
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"
|
2012-03-30 03:33:42 +00:00
|
|
|
bl_options = {'UNDO'}
|
2010-08-02 02:55:12 +00:00
|
|
|
|
|
|
|
data_path = rna_path
|
|
|
|
property = rna_property
|
|
|
|
|
|
|
|
def execute(self, context):
|
2011-08-18 15:25:18 +00:00
|
|
|
data_path = self.data_path
|
|
|
|
item = eval("context.%s" % data_path)
|
2010-09-09 18:03:57 +00:00
|
|
|
del item[self.property]
|
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_keyconfig_activate(Operator):
|
2010-09-14 16:45:24 +00:00
|
|
|
bl_idname = "wm.keyconfig_activate"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Activate Keyconfig"
|
2010-09-14 16:45:24 +00:00
|
|
|
|
2011-08-18 12:20:10 +00:00
|
|
|
filepath = StringProperty(
|
2011-12-11 21:23:29 +00:00
|
|
|
subtype='FILE_PATH',
|
2011-08-18 12:20:10 +00:00
|
|
|
)
|
2010-09-14 16:45:24 +00:00
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
bpy.utils.keyconfig_set(self.filepath)
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
2011-04-01 02:41:15 +00:00
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_appconfig_default(Operator):
|
2011-03-27 21:45:37 +00:00
|
|
|
bl_idname = "wm.appconfig_default"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Default Application Configuration"
|
2011-03-27 21:45:37 +00:00
|
|
|
|
|
|
|
def execute(self, context):
|
2011-09-23 13:47:29 +00:00
|
|
|
import os
|
2011-03-27 21:45:37 +00:00
|
|
|
|
|
|
|
context.window_manager.keyconfigs.active = context.window_manager.keyconfigs.default
|
|
|
|
|
2011-04-01 02:41:15 +00:00
|
|
|
filepath = os.path.join(bpy.utils.preset_paths("interaction")[0], "blender.py")
|
|
|
|
|
2011-03-27 21:45:37 +00:00
|
|
|
if os.path.exists(filepath):
|
2011-04-01 02:41:15 +00:00
|
|
|
bpy.ops.script.execute_preset(filepath=filepath, menu_idname="USERPREF_MT_interaction_presets")
|
|
|
|
|
2011-03-27 21:45:37 +00:00
|
|
|
return {'FINISHED'}
|
|
|
|
|
2011-04-01 02:41:15 +00:00
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_appconfig_activate(Operator):
|
2011-03-27 21:45:37 +00:00
|
|
|
bl_idname = "wm.appconfig_activate"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Activate Application Configuration"
|
2011-03-27 21:45:37 +00:00
|
|
|
|
2011-08-18 12:20:10 +00:00
|
|
|
filepath = StringProperty(
|
2011-12-11 21:23:29 +00:00
|
|
|
subtype='FILE_PATH',
|
2011-08-18 12:20:10 +00:00
|
|
|
)
|
2011-03-27 21:45:37 +00:00
|
|
|
|
|
|
|
def execute(self, context):
|
2011-09-23 13:47:29 +00:00
|
|
|
import os
|
2011-03-27 21:45:37 +00:00
|
|
|
bpy.utils.keyconfig_set(self.filepath)
|
2011-04-01 02:41:15 +00:00
|
|
|
|
2011-03-27 21:45:37 +00:00
|
|
|
filepath = self.filepath.replace("keyconfig", "interaction")
|
2011-04-01 02:41:15 +00:00
|
|
|
|
2011-03-27 21:45:37 +00:00
|
|
|
if os.path.exists(filepath):
|
2011-04-01 02:41:15 +00:00
|
|
|
bpy.ops.script.execute_preset(filepath=filepath, menu_idname="USERPREF_MT_interaction_presets")
|
|
|
|
|
2011-03-27 21:45:37 +00:00
|
|
|
return {'FINISHED'}
|
2011-01-01 07:20:34 +00:00
|
|
|
|
2011-04-01 02:41:15 +00:00
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_sysinfo(Operator):
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Generate System Info"""
|
2010-10-16 17:26:40 +00:00
|
|
|
bl_idname = "wm.sysinfo"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "System Info"
|
2010-10-16 17:26:40 +00:00
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
import sys_info
|
|
|
|
sys_info.write_sysinfo(self)
|
|
|
|
return {'FINISHED'}
|
2011-03-23 13:04:35 +00:00
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_copy_prev_settings(Operator):
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Copy settings from previous version"""
|
2011-04-11 15:13:06 +00:00
|
|
|
bl_idname = "wm.copy_prev_settings"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Copy Previous Settings"
|
2011-04-11 15:13:06 +00:00
|
|
|
|
|
|
|
def execute(self, context):
|
2011-09-23 13:47:29 +00:00
|
|
|
import os
|
2011-04-11 15:13:06 +00:00
|
|
|
import shutil
|
|
|
|
ver = bpy.app.version
|
2011-04-12 04:23:38 +00:00
|
|
|
ver_old = ((ver[0] * 100) + ver[1]) - 1
|
|
|
|
path_src = bpy.utils.resource_path('USER', ver_old // 100, ver_old % 100)
|
|
|
|
path_dst = bpy.utils.resource_path('USER')
|
|
|
|
|
|
|
|
if os.path.isdir(path_dst):
|
|
|
|
self.report({'ERROR'}, "Target path %r exists" % path_dst)
|
|
|
|
elif not os.path.isdir(path_src):
|
|
|
|
self.report({'ERROR'}, "Source path %r exists" % path_src)
|
|
|
|
else:
|
2011-10-17 16:36:03 +00:00
|
|
|
shutil.copytree(path_src, path_dst, symlinks=True)
|
Windows installer and Path changes, fixing various issues:
* Windows installer not working for non-admin users and multiple users
* Addon scripts not installing next to user configuration
* Portable install not being taken into account in all places
The main problem was the windows installer was installing system scripts in
AppData next to the user configuration directory, which is not shared between
users. Now these are installed in ProgramFiles, and only addon scripts added
by the users go to AppData.
On all platforms, addon scripts were sometimes getting installed between
system scripts, because the scripts folder in the executable directory was
given precedence over the user configuration folder, that is no longer done
now. So addons now behave like user configuration, they are preserved even
if you download a newer build of the same blender version.
If you have an installation of 2.57 on windows, the addon install location
will not change until we do the version bump to 2.58, to avoid conflicts with
the existing the installed 2.57 version.
The old behavior of giving precedence to the local folder was done to support
portable install, where all configuration is written to the local folder. This
is now implemented differently: if and only if a "config" folder exists in the
local folder, portable install will be assumed, and files will only be written
to that local folder.
2011-05-27 09:57:53 +00:00
|
|
|
|
|
|
|
# in 2.57 and earlier windows installers, system scripts were copied
|
|
|
|
# into the configuration directory, don't want to copy those
|
2011-12-22 03:56:21 +00:00
|
|
|
system_script = os.path.join(path_dst, "scripts/modules/bpy_types.py")
|
Windows installer and Path changes, fixing various issues:
* Windows installer not working for non-admin users and multiple users
* Addon scripts not installing next to user configuration
* Portable install not being taken into account in all places
The main problem was the windows installer was installing system scripts in
AppData next to the user configuration directory, which is not shared between
users. Now these are installed in ProgramFiles, and only addon scripts added
by the users go to AppData.
On all platforms, addon scripts were sometimes getting installed between
system scripts, because the scripts folder in the executable directory was
given precedence over the user configuration folder, that is no longer done
now. So addons now behave like user configuration, they are preserved even
if you download a newer build of the same blender version.
If you have an installation of 2.57 on windows, the addon install location
will not change until we do the version bump to 2.58, to avoid conflicts with
the existing the installed 2.57 version.
The old behavior of giving precedence to the local folder was done to support
portable install, where all configuration is written to the local folder. This
is now implemented differently: if and only if a "config" folder exists in the
local folder, portable install will be assumed, and files will only be written
to that local folder.
2011-05-27 09:57:53 +00:00
|
|
|
if os.path.isfile(system_script):
|
2011-12-22 03:56:21 +00:00
|
|
|
shutil.rmtree(os.path.join(path_dst, "scripts"))
|
|
|
|
shutil.rmtree(os.path.join(path_dst, "plugins"))
|
Windows installer and Path changes, fixing various issues:
* Windows installer not working for non-admin users and multiple users
* Addon scripts not installing next to user configuration
* Portable install not being taken into account in all places
The main problem was the windows installer was installing system scripts in
AppData next to the user configuration directory, which is not shared between
users. Now these are installed in ProgramFiles, and only addon scripts added
by the users go to AppData.
On all platforms, addon scripts were sometimes getting installed between
system scripts, because the scripts folder in the executable directory was
given precedence over the user configuration folder, that is no longer done
now. So addons now behave like user configuration, they are preserved even
if you download a newer build of the same blender version.
If you have an installation of 2.57 on windows, the addon install location
will not change until we do the version bump to 2.58, to avoid conflicts with
the existing the installed 2.57 version.
The old behavior of giving precedence to the local folder was done to support
portable install, where all configuration is written to the local folder. This
is now implemented differently: if and only if a "config" folder exists in the
local folder, portable install will be assumed, and files will only be written
to that local folder.
2011-05-27 09:57:53 +00:00
|
|
|
|
2011-10-17 06:58:07 +00:00
|
|
|
# don't loose users work if they open the splash later.
|
2011-04-12 04:23:38 +00:00
|
|
|
if bpy.data.is_saved is bpy.data.is_dirty is False:
|
|
|
|
bpy.ops.wm.read_homefile()
|
|
|
|
else:
|
2011-09-19 14:00:42 +00:00
|
|
|
self.report({'INFO'}, "Reload Start-Up file to restore settings")
|
2011-04-12 04:23:38 +00:00
|
|
|
return {'FINISHED'}
|
2011-04-11 15:13:06 +00:00
|
|
|
|
2011-04-12 04:23:38 +00:00
|
|
|
return {'CANCELLED'}
|
2011-09-22 19:50:41 +00:00
|
|
|
|
2012-01-01 08:52:54 +00:00
|
|
|
|
2012-01-14 06:30:27 +00:00
|
|
|
class WM_OT_blenderplayer_start(Operator):
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Launch the blender-player with the current blend-file"""
|
Cucumber, first batch of merge - UI changes and custom exit key
---------------------------------------------------------------
This was a test drive to see how painful the merge will be.
Next batches are:
- use desktop option for fullscreen
- multisampling option
- bullet collision mask
- python
- storage (vbo, dl, ...)
- lighting
[lighting still needs review]
[python could use review, although it should be straightforward]
[storage should be tested more I think]
Merged /branches/soc-2011-cucumber:r
36991,37059,37157,37416,37497-37499,37501,37522,39036,40593
36991:
==UI==
* Made some options available in Blender Game that were only available in Blender Render (camera resolution, animation fps)
* Created a panel for the embedded player
* Renamed the FPS option for the standalone player to Refresh Rate
* Moved framing options to display
* Made a button to launch the blender player from within blender (only tested on windows for now)
37059:
==UI==
* Added the option to change the exit key for the BGE. The UI currently just sets a number, and this feature most likely does not work for blenderplayer yet. More work on this to come.
* Removed the physics settings from the scene panel for the BGE.
* Added an Add menu in the logic brick header.
37157:
Making the bake options available in Blender Game
37416:
Making the exit key UI element accept key presses instead of numbers. It still does not work for the Blenderplayer, and it does not limit the input to key presses (other events don't work for exiting)
37497:
Some more work on getting the exit key to work in the Blenderplayer.
Input is now restricted to keyboard events only for the exit key UI.
37498:
Some clean up from the last commit.
The exit key setting affects the Blenderplayer now.
37499:
Cleaning up some duplicate code. Now the reverseTranslateTable for converting blender key codes to ketsji key codes is only defined in BL_BlenderDataConverter.
37501:
Centralizing the exit key methods to the keyboard devices. This should make it easier to get exit key control to the python API.
[37517: committed previously]
37522:
Moved control of the exit key away from the keyboard devices, and moved it to ketsjiengine.
Added setExitKey and getExitKey to the python API
39036:
A couple of the doversions were in the wrong spot. This should fix some issues with the exit key not being set.
[not committed entirely, see below]]
40552: space_logic.py (* fixed an error in space_logic.py *)
40593:
launch blenderplayer from ui not working in OSX fix - by Daniel Stokes and me
########################################################
code left behind (to be included in next commit):
########################################################
{
/* Initialize default values for collision masks */
Object *ob;
for(ob=main->object.first; ob; ob=ob->id.next)
ob->col_group = ob->col_mask = 1;
}
2011-12-20 03:11:56 +00:00
|
|
|
bl_idname = "wm.blenderplayer_start"
|
2012-04-16 02:28:18 +00:00
|
|
|
bl_label = "Start Game In Player"
|
2012-01-01 08:52:54 +00:00
|
|
|
|
Cucumber, first batch of merge - UI changes and custom exit key
---------------------------------------------------------------
This was a test drive to see how painful the merge will be.
Next batches are:
- use desktop option for fullscreen
- multisampling option
- bullet collision mask
- python
- storage (vbo, dl, ...)
- lighting
[lighting still needs review]
[python could use review, although it should be straightforward]
[storage should be tested more I think]
Merged /branches/soc-2011-cucumber:r
36991,37059,37157,37416,37497-37499,37501,37522,39036,40593
36991:
==UI==
* Made some options available in Blender Game that were only available in Blender Render (camera resolution, animation fps)
* Created a panel for the embedded player
* Renamed the FPS option for the standalone player to Refresh Rate
* Moved framing options to display
* Made a button to launch the blender player from within blender (only tested on windows for now)
37059:
==UI==
* Added the option to change the exit key for the BGE. The UI currently just sets a number, and this feature most likely does not work for blenderplayer yet. More work on this to come.
* Removed the physics settings from the scene panel for the BGE.
* Added an Add menu in the logic brick header.
37157:
Making the bake options available in Blender Game
37416:
Making the exit key UI element accept key presses instead of numbers. It still does not work for the Blenderplayer, and it does not limit the input to key presses (other events don't work for exiting)
37497:
Some more work on getting the exit key to work in the Blenderplayer.
Input is now restricted to keyboard events only for the exit key UI.
37498:
Some clean up from the last commit.
The exit key setting affects the Blenderplayer now.
37499:
Cleaning up some duplicate code. Now the reverseTranslateTable for converting blender key codes to ketsji key codes is only defined in BL_BlenderDataConverter.
37501:
Centralizing the exit key methods to the keyboard devices. This should make it easier to get exit key control to the python API.
[37517: committed previously]
37522:
Moved control of the exit key away from the keyboard devices, and moved it to ketsjiengine.
Added setExitKey and getExitKey to the python API
39036:
A couple of the doversions were in the wrong spot. This should fix some issues with the exit key not being set.
[not committed entirely, see below]]
40552: space_logic.py (* fixed an error in space_logic.py *)
40593:
launch blenderplayer from ui not working in OSX fix - by Daniel Stokes and me
########################################################
code left behind (to be included in next commit):
########################################################
{
/* Initialize default values for collision masks */
Object *ob;
for(ob=main->object.first; ob; ob=ob->id.next)
ob->col_group = ob->col_mask = 1;
}
2011-12-20 03:11:56 +00:00
|
|
|
def execute(self, context):
|
2012-04-06 08:04:31 +00:00
|
|
|
import os
|
Cucumber, first batch of merge - UI changes and custom exit key
---------------------------------------------------------------
This was a test drive to see how painful the merge will be.
Next batches are:
- use desktop option for fullscreen
- multisampling option
- bullet collision mask
- python
- storage (vbo, dl, ...)
- lighting
[lighting still needs review]
[python could use review, although it should be straightforward]
[storage should be tested more I think]
Merged /branches/soc-2011-cucumber:r
36991,37059,37157,37416,37497-37499,37501,37522,39036,40593
36991:
==UI==
* Made some options available in Blender Game that were only available in Blender Render (camera resolution, animation fps)
* Created a panel for the embedded player
* Renamed the FPS option for the standalone player to Refresh Rate
* Moved framing options to display
* Made a button to launch the blender player from within blender (only tested on windows for now)
37059:
==UI==
* Added the option to change the exit key for the BGE. The UI currently just sets a number, and this feature most likely does not work for blenderplayer yet. More work on this to come.
* Removed the physics settings from the scene panel for the BGE.
* Added an Add menu in the logic brick header.
37157:
Making the bake options available in Blender Game
37416:
Making the exit key UI element accept key presses instead of numbers. It still does not work for the Blenderplayer, and it does not limit the input to key presses (other events don't work for exiting)
37497:
Some more work on getting the exit key to work in the Blenderplayer.
Input is now restricted to keyboard events only for the exit key UI.
37498:
Some clean up from the last commit.
The exit key setting affects the Blenderplayer now.
37499:
Cleaning up some duplicate code. Now the reverseTranslateTable for converting blender key codes to ketsji key codes is only defined in BL_BlenderDataConverter.
37501:
Centralizing the exit key methods to the keyboard devices. This should make it easier to get exit key control to the python API.
[37517: committed previously]
37522:
Moved control of the exit key away from the keyboard devices, and moved it to ketsjiengine.
Added setExitKey and getExitKey to the python API
39036:
A couple of the doversions were in the wrong spot. This should fix some issues with the exit key not being set.
[not committed entirely, see below]]
40552: space_logic.py (* fixed an error in space_logic.py *)
40593:
launch blenderplayer from ui not working in OSX fix - by Daniel Stokes and me
########################################################
code left behind (to be included in next commit):
########################################################
{
/* Initialize default values for collision masks */
Object *ob;
for(ob=main->object.first; ob; ob=ob->id.next)
ob->col_group = ob->col_mask = 1;
}
2011-12-20 03:11:56 +00:00
|
|
|
import sys
|
2012-04-06 08:04:31 +00:00
|
|
|
import subprocess
|
|
|
|
|
|
|
|
# these remain the same every execution
|
|
|
|
blender_bin_path = bpy.app.binary_path
|
|
|
|
blender_bin_dir = os.path.dirname(blender_bin_path)
|
|
|
|
ext = os.path.splitext(blender_bin_path)[-1]
|
|
|
|
player_path = os.path.join(blender_bin_dir, "blenderplayer" + ext)
|
|
|
|
# done static vars
|
Cucumber, first batch of merge - UI changes and custom exit key
---------------------------------------------------------------
This was a test drive to see how painful the merge will be.
Next batches are:
- use desktop option for fullscreen
- multisampling option
- bullet collision mask
- python
- storage (vbo, dl, ...)
- lighting
[lighting still needs review]
[python could use review, although it should be straightforward]
[storage should be tested more I think]
Merged /branches/soc-2011-cucumber:r
36991,37059,37157,37416,37497-37499,37501,37522,39036,40593
36991:
==UI==
* Made some options available in Blender Game that were only available in Blender Render (camera resolution, animation fps)
* Created a panel for the embedded player
* Renamed the FPS option for the standalone player to Refresh Rate
* Moved framing options to display
* Made a button to launch the blender player from within blender (only tested on windows for now)
37059:
==UI==
* Added the option to change the exit key for the BGE. The UI currently just sets a number, and this feature most likely does not work for blenderplayer yet. More work on this to come.
* Removed the physics settings from the scene panel for the BGE.
* Added an Add menu in the logic brick header.
37157:
Making the bake options available in Blender Game
37416:
Making the exit key UI element accept key presses instead of numbers. It still does not work for the Blenderplayer, and it does not limit the input to key presses (other events don't work for exiting)
37497:
Some more work on getting the exit key to work in the Blenderplayer.
Input is now restricted to keyboard events only for the exit key UI.
37498:
Some clean up from the last commit.
The exit key setting affects the Blenderplayer now.
37499:
Cleaning up some duplicate code. Now the reverseTranslateTable for converting blender key codes to ketsji key codes is only defined in BL_BlenderDataConverter.
37501:
Centralizing the exit key methods to the keyboard devices. This should make it easier to get exit key control to the python API.
[37517: committed previously]
37522:
Moved control of the exit key away from the keyboard devices, and moved it to ketsjiengine.
Added setExitKey and getExitKey to the python API
39036:
A couple of the doversions were in the wrong spot. This should fix some issues with the exit key not being set.
[not committed entirely, see below]]
40552: space_logic.py (* fixed an error in space_logic.py *)
40593:
launch blenderplayer from ui not working in OSX fix - by Daniel Stokes and me
########################################################
code left behind (to be included in next commit):
########################################################
{
/* Initialize default values for collision masks */
Object *ob;
for(ob=main->object.first; ob; ob=ob->id.next)
ob->col_group = ob->col_mask = 1;
}
2011-12-20 03:11:56 +00:00
|
|
|
|
2011-12-22 03:56:21 +00:00
|
|
|
if sys.platform == "darwin":
|
2012-04-06 08:04:31 +00:00
|
|
|
player_path = os.path.join(blender_bin_dir, "../../../blenderplayer.app/Contents/MacOS/blenderplayer")
|
2012-01-01 08:52:54 +00:00
|
|
|
|
2012-04-16 02:28:18 +00:00
|
|
|
if not os.path.exists(player_path):
|
|
|
|
self.report({'ERROR'}, "Player path: %r not found" % player_path)
|
|
|
|
return {'CANCELLED'}
|
|
|
|
|
2012-04-06 08:04:31 +00:00
|
|
|
filepath = os.path.join(bpy.app.tempdir, "game.blend")
|
2012-10-31 17:03:31 +00:00
|
|
|
bpy.ops.wm.save_as_mainfile('EXEC_DEFAULT', filepath=filepath, copy=True)
|
2012-04-06 08:04:31 +00:00
|
|
|
subprocess.call([player_path, filepath])
|
Cucumber, first batch of merge - UI changes and custom exit key
---------------------------------------------------------------
This was a test drive to see how painful the merge will be.
Next batches are:
- use desktop option for fullscreen
- multisampling option
- bullet collision mask
- python
- storage (vbo, dl, ...)
- lighting
[lighting still needs review]
[python could use review, although it should be straightforward]
[storage should be tested more I think]
Merged /branches/soc-2011-cucumber:r
36991,37059,37157,37416,37497-37499,37501,37522,39036,40593
36991:
==UI==
* Made some options available in Blender Game that were only available in Blender Render (camera resolution, animation fps)
* Created a panel for the embedded player
* Renamed the FPS option for the standalone player to Refresh Rate
* Moved framing options to display
* Made a button to launch the blender player from within blender (only tested on windows for now)
37059:
==UI==
* Added the option to change the exit key for the BGE. The UI currently just sets a number, and this feature most likely does not work for blenderplayer yet. More work on this to come.
* Removed the physics settings from the scene panel for the BGE.
* Added an Add menu in the logic brick header.
37157:
Making the bake options available in Blender Game
37416:
Making the exit key UI element accept key presses instead of numbers. It still does not work for the Blenderplayer, and it does not limit the input to key presses (other events don't work for exiting)
37497:
Some more work on getting the exit key to work in the Blenderplayer.
Input is now restricted to keyboard events only for the exit key UI.
37498:
Some clean up from the last commit.
The exit key setting affects the Blenderplayer now.
37499:
Cleaning up some duplicate code. Now the reverseTranslateTable for converting blender key codes to ketsji key codes is only defined in BL_BlenderDataConverter.
37501:
Centralizing the exit key methods to the keyboard devices. This should make it easier to get exit key control to the python API.
[37517: committed previously]
37522:
Moved control of the exit key away from the keyboard devices, and moved it to ketsjiengine.
Added setExitKey and getExitKey to the python API
39036:
A couple of the doversions were in the wrong spot. This should fix some issues with the exit key not being set.
[not committed entirely, see below]]
40552: space_logic.py (* fixed an error in space_logic.py *)
40593:
launch blenderplayer from ui not working in OSX fix - by Daniel Stokes and me
########################################################
code left behind (to be included in next commit):
########################################################
{
/* Initialize default values for collision masks */
Object *ob;
for(ob=main->object.first; ob; ob=ob->id.next)
ob->col_group = ob->col_mask = 1;
}
2011-12-20 03:11:56 +00:00
|
|
|
return {'FINISHED'}
|
2011-09-26 15:39:15 +00:00
|
|
|
|
2012-01-01 08:52:54 +00:00
|
|
|
|
2011-09-22 19:50:41 +00:00
|
|
|
class WM_OT_keyconfig_test(Operator):
|
2012-02-08 04:37:37 +00:00
|
|
|
"Test key-config for conflicts"
|
2011-09-22 19:50:41 +00:00
|
|
|
bl_idname = "wm.keyconfig_test"
|
|
|
|
bl_label = "Test Key Configuration for Conflicts"
|
|
|
|
|
|
|
|
def execute(self, context):
|
2011-09-26 11:22:07 +00:00
|
|
|
from bpy_extras import keyconfig_utils
|
|
|
|
|
2011-09-22 19:50:41 +00:00
|
|
|
wm = context.window_manager
|
|
|
|
kc = wm.keyconfigs.default
|
|
|
|
|
2011-09-26 11:22:07 +00:00
|
|
|
if keyconfig_utils.keyconfig_test(kc):
|
2011-09-22 19:50:41 +00:00
|
|
|
print("CONFLICT")
|
|
|
|
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
|
|
|
|
|
|
class WM_OT_keyconfig_import(Operator):
|
|
|
|
"Import key configuration from a python script"
|
|
|
|
bl_idname = "wm.keyconfig_import"
|
|
|
|
bl_label = "Import Key Configuration..."
|
|
|
|
|
|
|
|
filepath = StringProperty(
|
2011-12-11 21:23:29 +00:00
|
|
|
subtype='FILE_PATH',
|
2011-09-22 19:50:41 +00:00
|
|
|
default="keymap.py",
|
|
|
|
)
|
|
|
|
filter_folder = BoolProperty(
|
|
|
|
name="Filter folders",
|
|
|
|
default=True,
|
|
|
|
options={'HIDDEN'},
|
|
|
|
)
|
|
|
|
filter_text = BoolProperty(
|
|
|
|
name="Filter text",
|
|
|
|
default=True,
|
|
|
|
options={'HIDDEN'},
|
|
|
|
)
|
|
|
|
filter_python = BoolProperty(
|
|
|
|
name="Filter python",
|
|
|
|
default=True,
|
|
|
|
options={'HIDDEN'},
|
|
|
|
)
|
|
|
|
keep_original = BoolProperty(
|
|
|
|
name="Keep original",
|
|
|
|
description="Keep original file after copying to configuration folder",
|
|
|
|
default=True,
|
|
|
|
)
|
|
|
|
|
|
|
|
def execute(self, context):
|
2011-09-26 09:31:24 +00:00
|
|
|
import os
|
2011-09-22 19:50:41 +00:00
|
|
|
from os.path import basename
|
|
|
|
import shutil
|
|
|
|
|
|
|
|
if not self.filepath:
|
|
|
|
self.report({'ERROR'}, "Filepath not set")
|
|
|
|
return {'CANCELLED'}
|
|
|
|
|
|
|
|
config_name = basename(self.filepath)
|
|
|
|
|
|
|
|
path = bpy.utils.user_resource('SCRIPTS', os.path.join("presets", "keyconfig"), create=True)
|
|
|
|
path = os.path.join(path, config_name)
|
|
|
|
|
|
|
|
try:
|
|
|
|
if self.keep_original:
|
|
|
|
shutil.copy(self.filepath, path)
|
|
|
|
else:
|
|
|
|
shutil.move(self.filepath, path)
|
|
|
|
except Exception as e:
|
|
|
|
self.report({'ERROR'}, "Installing keymap failed: %s" % e)
|
|
|
|
return {'CANCELLED'}
|
|
|
|
|
|
|
|
# sneaky way to check we're actually running the code.
|
|
|
|
bpy.utils.keyconfig_set(path)
|
|
|
|
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
|
|
|
wm = context.window_manager
|
|
|
|
wm.fileselect_add(self)
|
|
|
|
return {'RUNNING_MODAL'}
|
|
|
|
|
|
|
|
# This operator is also used by interaction presets saving - AddPresetBase
|
|
|
|
|
|
|
|
|
|
|
|
class WM_OT_keyconfig_export(Operator):
|
|
|
|
"Export key configuration to a python script"
|
|
|
|
bl_idname = "wm.keyconfig_export"
|
|
|
|
bl_label = "Export Key Configuration..."
|
|
|
|
|
|
|
|
filepath = StringProperty(
|
2011-12-11 21:23:29 +00:00
|
|
|
subtype='FILE_PATH',
|
2011-09-22 19:50:41 +00:00
|
|
|
default="keymap.py",
|
|
|
|
)
|
|
|
|
filter_folder = BoolProperty(
|
|
|
|
name="Filter folders",
|
|
|
|
default=True,
|
|
|
|
options={'HIDDEN'},
|
|
|
|
)
|
|
|
|
filter_text = BoolProperty(
|
|
|
|
name="Filter text",
|
|
|
|
default=True,
|
|
|
|
options={'HIDDEN'},
|
|
|
|
)
|
|
|
|
filter_python = BoolProperty(
|
|
|
|
name="Filter python",
|
|
|
|
default=True,
|
|
|
|
options={'HIDDEN'},
|
|
|
|
)
|
|
|
|
|
|
|
|
def execute(self, context):
|
2011-09-26 11:22:07 +00:00
|
|
|
from bpy_extras import keyconfig_utils
|
|
|
|
|
2011-09-22 19:50:41 +00:00
|
|
|
if not self.filepath:
|
|
|
|
raise Exception("Filepath not set")
|
|
|
|
|
2012-07-04 21:41:05 +00:00
|
|
|
if not self.filepath.endswith(".py"):
|
|
|
|
self.filepath += ".py"
|
2011-09-22 19:50:41 +00:00
|
|
|
|
|
|
|
wm = context.window_manager
|
|
|
|
|
2011-09-26 11:22:07 +00:00
|
|
|
keyconfig_utils.keyconfig_export(wm,
|
|
|
|
wm.keyconfigs.active,
|
|
|
|
self.filepath,
|
|
|
|
)
|
2011-09-22 19:50:41 +00:00
|
|
|
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
|
|
|
wm = context.window_manager
|
|
|
|
wm.fileselect_add(self)
|
|
|
|
return {'RUNNING_MODAL'}
|
|
|
|
|
|
|
|
|
|
|
|
class WM_OT_keymap_restore(Operator):
|
|
|
|
"Restore key map(s)"
|
|
|
|
bl_idname = "wm.keymap_restore"
|
|
|
|
bl_label = "Restore Key Map(s)"
|
|
|
|
|
|
|
|
all = BoolProperty(
|
|
|
|
name="All Keymaps",
|
|
|
|
description="Restore all keymaps to default",
|
|
|
|
)
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
wm = context.window_manager
|
|
|
|
|
|
|
|
if self.all:
|
|
|
|
for km in wm.keyconfigs.user.keymaps:
|
|
|
|
km.restore_to_default()
|
|
|
|
else:
|
|
|
|
km = context.keymap
|
|
|
|
km.restore_to_default()
|
|
|
|
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
|
|
|
|
|
|
class WM_OT_keyitem_restore(Operator):
|
|
|
|
"Restore key map item"
|
|
|
|
bl_idname = "wm.keyitem_restore"
|
|
|
|
bl_label = "Restore Key Map Item"
|
|
|
|
|
|
|
|
item_id = IntProperty(
|
|
|
|
name="Item Identifier",
|
|
|
|
description="Identifier of the item to remove",
|
|
|
|
)
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def poll(cls, context):
|
|
|
|
keymap = getattr(context, "keymap", None)
|
|
|
|
return keymap
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
km = context.keymap
|
|
|
|
kmi = km.keymap_items.from_id(self.item_id)
|
|
|
|
|
|
|
|
if (not kmi.is_user_defined) and kmi.is_user_modified:
|
|
|
|
km.restore_item_to_default(kmi)
|
|
|
|
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
|
|
|
|
|
|
class WM_OT_keyitem_add(Operator):
|
|
|
|
"Add key map item"
|
|
|
|
bl_idname = "wm.keyitem_add"
|
|
|
|
bl_label = "Add Key Map Item"
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
km = context.keymap
|
|
|
|
|
|
|
|
if km.is_modal:
|
2011-10-31 01:50:04 +00:00
|
|
|
km.keymap_items.new_modal("", 'A', 'PRESS')
|
2011-09-22 19:50:41 +00:00
|
|
|
else:
|
2011-10-31 01:50:04 +00:00
|
|
|
km.keymap_items.new("none", 'A', 'PRESS')
|
2011-09-22 19:50:41 +00:00
|
|
|
|
|
|
|
# clear filter and expand keymap so we can see the newly added item
|
|
|
|
if context.space_data.filter_text != "":
|
|
|
|
context.space_data.filter_text = ""
|
|
|
|
km.show_expanded_items = True
|
|
|
|
km.show_expanded_children = True
|
|
|
|
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
|
|
|
|
|
|
class WM_OT_keyitem_remove(Operator):
|
|
|
|
"Remove key map item"
|
|
|
|
bl_idname = "wm.keyitem_remove"
|
|
|
|
bl_label = "Remove Key Map Item"
|
|
|
|
|
|
|
|
item_id = IntProperty(
|
|
|
|
name="Item Identifier",
|
|
|
|
description="Identifier of the item to remove",
|
|
|
|
)
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def poll(cls, context):
|
|
|
|
return hasattr(context, "keymap")
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
km = context.keymap
|
|
|
|
kmi = km.keymap_items.from_id(self.item_id)
|
|
|
|
km.keymap_items.remove(kmi)
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
|
|
|
|
|
|
class WM_OT_keyconfig_remove(Operator):
|
|
|
|
"Remove key config"
|
|
|
|
bl_idname = "wm.keyconfig_remove"
|
|
|
|
bl_label = "Remove Key Config"
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def poll(cls, context):
|
|
|
|
wm = context.window_manager
|
|
|
|
keyconf = wm.keyconfigs.active
|
|
|
|
return keyconf and keyconf.is_user_defined
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
wm = context.window_manager
|
|
|
|
keyconfig = wm.keyconfigs.active
|
|
|
|
wm.keyconfigs.remove(keyconfig)
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
|
|
|
|
|
|
class WM_OT_operator_cheat_sheet(Operator):
|
|
|
|
bl_idname = "wm.operator_cheat_sheet"
|
|
|
|
bl_label = "Operator Cheat Sheet"
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
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
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
# Addon Operators
|
2011-09-26 15:39:15 +00:00
|
|
|
|
2011-09-22 19:50:41 +00:00
|
|
|
class WM_OT_addon_enable(Operator):
|
|
|
|
"Enable an addon"
|
|
|
|
bl_idname = "wm.addon_enable"
|
2011-10-23 17:52:20 +00:00
|
|
|
bl_label = "Enable Addon"
|
2011-09-22 19:50:41 +00:00
|
|
|
|
|
|
|
module = StringProperty(
|
|
|
|
name="Module",
|
|
|
|
description="Module name of the addon to enable",
|
|
|
|
)
|
|
|
|
|
|
|
|
def execute(self, context):
|
2011-09-23 13:47:29 +00:00
|
|
|
import addon_utils
|
|
|
|
|
2011-09-22 19:50:41 +00:00
|
|
|
mod = addon_utils.enable(self.module)
|
|
|
|
|
|
|
|
if mod:
|
|
|
|
info = addon_utils.module_bl_info(mod)
|
|
|
|
|
|
|
|
info_ver = info.get("blender", (0, 0, 0))
|
|
|
|
|
|
|
|
if info_ver > bpy.app.version:
|
|
|
|
self.report({'WARNING'}, ("This script was written Blender "
|
|
|
|
"version %d.%d.%d and might not "
|
|
|
|
"function (correctly), "
|
|
|
|
"though it is enabled") %
|
2012-10-08 08:28:05 +00:00
|
|
|
info_ver)
|
2011-09-22 19:50:41 +00:00
|
|
|
return {'FINISHED'}
|
|
|
|
else:
|
|
|
|
return {'CANCELLED'}
|
|
|
|
|
|
|
|
|
|
|
|
class WM_OT_addon_disable(Operator):
|
|
|
|
"Disable an addon"
|
|
|
|
bl_idname = "wm.addon_disable"
|
2011-10-23 17:52:20 +00:00
|
|
|
bl_label = "Disable Addon"
|
2011-09-22 19:50:41 +00:00
|
|
|
|
|
|
|
module = StringProperty(
|
|
|
|
name="Module",
|
|
|
|
description="Module name of the addon to disable",
|
|
|
|
)
|
|
|
|
|
|
|
|
def execute(self, context):
|
2011-09-23 13:47:29 +00:00
|
|
|
import addon_utils
|
|
|
|
|
2011-09-22 19:50:41 +00:00
|
|
|
addon_utils.disable(self.module)
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
2012-06-19 22:17:19 +00:00
|
|
|
|
2012-05-18 12:49:27 +00:00
|
|
|
class WM_OT_theme_install(Operator):
|
2012-12-15 16:27:12 +00:00
|
|
|
"Load and apply a Blender XML theme file"
|
2012-05-18 12:49:27 +00:00
|
|
|
bl_idname = "wm.theme_install"
|
2012-06-19 22:17:19 +00:00
|
|
|
bl_label = "Install Theme..."
|
2012-05-18 12:49:27 +00:00
|
|
|
|
|
|
|
overwrite = BoolProperty(
|
|
|
|
name="Overwrite",
|
|
|
|
description="Remove existing theme file if exists",
|
|
|
|
default=True,
|
|
|
|
)
|
|
|
|
filepath = StringProperty(
|
|
|
|
subtype='FILE_PATH',
|
|
|
|
)
|
|
|
|
filter_folder = BoolProperty(
|
|
|
|
name="Filter folders",
|
|
|
|
default=True,
|
|
|
|
options={'HIDDEN'},
|
|
|
|
)
|
|
|
|
filter_glob = StringProperty(
|
|
|
|
default="*.xml",
|
|
|
|
options={'HIDDEN'},
|
|
|
|
)
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
import os
|
|
|
|
import shutil
|
|
|
|
import traceback
|
2012-06-19 22:17:19 +00:00
|
|
|
|
2012-05-18 12:49:27 +00:00
|
|
|
xmlfile = self.filepath
|
|
|
|
|
2012-06-19 22:17:19 +00:00
|
|
|
path_themes = bpy.utils.user_resource('SCRIPTS', "presets/interface_theme", create=True)
|
2012-05-18 12:49:27 +00:00
|
|
|
|
|
|
|
if not path_themes:
|
|
|
|
self.report({'ERROR'}, "Failed to get themes path")
|
|
|
|
return {'CANCELLED'}
|
|
|
|
|
|
|
|
path_dest = os.path.join(path_themes, os.path.basename(xmlfile))
|
|
|
|
|
|
|
|
if not self.overwrite:
|
|
|
|
if os.path.exists(path_dest):
|
|
|
|
self.report({'WARNING'}, "File already installed to %r\n" % path_dest)
|
|
|
|
return {'CANCELLED'}
|
|
|
|
|
|
|
|
try:
|
|
|
|
shutil.copyfile(xmlfile, path_dest)
|
2012-06-19 22:17:19 +00:00
|
|
|
bpy.ops.script.execute_preset(filepath=path_dest, menu_idname="USERPREF_MT_interface_theme_presets")
|
2012-05-18 12:49:27 +00:00
|
|
|
|
|
|
|
except:
|
|
|
|
traceback.print_exc()
|
|
|
|
return {'CANCELLED'}
|
|
|
|
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
|
|
|
wm = context.window_manager
|
|
|
|
wm.fileselect_add(self)
|
|
|
|
return {'RUNNING_MODAL'}
|
|
|
|
|
2011-09-22 19:50:41 +00:00
|
|
|
|
|
|
|
class WM_OT_addon_install(Operator):
|
|
|
|
"Install an addon"
|
|
|
|
bl_idname = "wm.addon_install"
|
2012-10-10 08:04:04 +00:00
|
|
|
bl_label = "Install from File..."
|
2011-09-22 19:50:41 +00:00
|
|
|
|
|
|
|
overwrite = BoolProperty(
|
|
|
|
name="Overwrite",
|
|
|
|
description="Remove existing addons with the same ID",
|
|
|
|
default=True,
|
|
|
|
)
|
|
|
|
target = EnumProperty(
|
|
|
|
name="Target Path",
|
|
|
|
items=(('DEFAULT', "Default", ""),
|
|
|
|
('PREFS', "User Prefs", "")),
|
|
|
|
)
|
|
|
|
|
|
|
|
filepath = StringProperty(
|
2011-12-11 21:23:29 +00:00
|
|
|
subtype='FILE_PATH',
|
2011-09-22 19:50:41 +00:00
|
|
|
)
|
|
|
|
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'},
|
|
|
|
)
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def _module_remove(path_addons, module):
|
2011-09-26 06:29:58 +00:00
|
|
|
import os
|
2011-09-22 19:50:41 +00:00
|
|
|
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):
|
2011-09-23 13:47:29 +00:00
|
|
|
import addon_utils
|
2011-09-22 19:50:41 +00:00
|
|
|
import traceback
|
|
|
|
import zipfile
|
|
|
|
import shutil
|
2011-09-26 06:29:58 +00:00
|
|
|
import os
|
2011-09-22 19:50:41 +00:00
|
|
|
|
|
|
|
pyfile = self.filepath
|
|
|
|
|
|
|
|
if self.target == 'DEFAULT':
|
2011-10-17 06:58:07 +00:00
|
|
|
# don't use bpy.utils.script_paths("addons") because we may not be able to write to it.
|
2011-09-22 19:50:41 +00:00
|
|
|
path_addons = bpy.utils.user_resource('SCRIPTS', "addons", create=True)
|
|
|
|
else:
|
|
|
|
path_addons = bpy.context.user_preferences.filepaths.script_directory
|
|
|
|
if path_addons:
|
|
|
|
path_addons = os.path.join(path_addons, "addons")
|
|
|
|
|
|
|
|
if not path_addons:
|
|
|
|
self.report({'ERROR'}, "Failed to get addons path")
|
|
|
|
return {'CANCELLED'}
|
|
|
|
|
|
|
|
# create dir is if missing.
|
|
|
|
if not os.path.exists(path_addons):
|
|
|
|
os.makedirs(path_addons)
|
|
|
|
|
|
|
|
# Check if we are installing from a target path,
|
|
|
|
# doing so causes 2+ addons of same name or when the same from/to
|
|
|
|
# location is used, removal of the file!
|
|
|
|
addon_path = ""
|
|
|
|
pyfile_dir = os.path.dirname(pyfile)
|
|
|
|
for addon_path in addon_utils.paths():
|
|
|
|
if os.path.samefile(pyfile_dir, addon_path):
|
|
|
|
self.report({'ERROR'}, "Source file is in the addon search path: %r" % addon_path)
|
|
|
|
return {'CANCELLED'}
|
|
|
|
del addon_path
|
|
|
|
del pyfile_dir
|
|
|
|
# done checking for exceptional case
|
|
|
|
|
2011-09-23 13:47:29 +00:00
|
|
|
addons_old = {mod.__name__ for mod in addon_utils.modules(addon_utils.addons_fake_modules)}
|
2011-09-22 19:50:41 +00:00
|
|
|
|
|
|
|
#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():
|
|
|
|
WM_OT_addon_install._module_remove(path_addons, f)
|
|
|
|
else:
|
|
|
|
for f in file_to_extract.namelist():
|
|
|
|
path_dest = os.path.join(path_addons, os.path.basename(f))
|
|
|
|
if os.path.exists(path_dest):
|
|
|
|
self.report({'WARNING'}, "File already installed to %r\n" % path_dest)
|
|
|
|
return {'CANCELLED'}
|
|
|
|
|
|
|
|
try: # extract the file to "addons"
|
|
|
|
file_to_extract.extractall(path_addons)
|
|
|
|
except:
|
|
|
|
traceback.print_exc()
|
|
|
|
return {'CANCELLED'}
|
|
|
|
|
|
|
|
else:
|
|
|
|
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))
|
|
|
|
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'}
|
|
|
|
|
2011-09-23 13:47:29 +00:00
|
|
|
addons_new = {mod.__name__ for mod in addon_utils.modules(addon_utils.addons_fake_modules)} - addons_old
|
2011-09-22 19:50:41 +00:00
|
|
|
addons_new.discard("modules")
|
|
|
|
|
|
|
|
# disable any addons we may have enabled previously and removed.
|
2011-10-17 06:58:07 +00:00
|
|
|
# this is unlikely but do just in case. bug [#23978]
|
2011-09-22 19:50:41 +00:00
|
|
|
for new_addon in addons_new:
|
|
|
|
addon_utils.disable(new_addon)
|
|
|
|
|
|
|
|
# possible the zip contains multiple addons, we could disallow this
|
|
|
|
# but for now just use the first
|
2011-09-23 13:47:29 +00:00
|
|
|
for mod in addon_utils.modules(addon_utils.addons_fake_modules):
|
2011-09-22 19:50:41 +00:00
|
|
|
if mod.__name__ in addons_new:
|
|
|
|
info = addon_utils.module_bl_info(mod)
|
|
|
|
|
|
|
|
# show the newly installed addon.
|
|
|
|
context.window_manager.addon_filter = 'All'
|
|
|
|
context.window_manager.addon_search = info["name"]
|
|
|
|
break
|
|
|
|
|
2011-10-17 06:58:07 +00:00
|
|
|
# in case a new module path was created to install this addon.
|
2011-09-22 19:50:41 +00:00
|
|
|
bpy.utils.refresh_script_paths()
|
|
|
|
|
2012-10-12 03:24:47 +00:00
|
|
|
# print message
|
2012-10-19 10:37:24 +00:00
|
|
|
msg = "Modules Installed from %r into %r (%s)" % (pyfile, path_addons, ", ".join(sorted(addons_new)))
|
2012-10-12 03:24:47 +00:00
|
|
|
print(msg)
|
2012-10-19 08:51:31 +00:00
|
|
|
self.report({'INFO'}, msg)
|
2012-10-12 03:24:47 +00:00
|
|
|
|
2011-09-22 19:50:41 +00:00
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
|
|
|
wm = context.window_manager
|
|
|
|
wm.fileselect_add(self)
|
|
|
|
return {'RUNNING_MODAL'}
|
|
|
|
|
|
|
|
|
|
|
|
class WM_OT_addon_remove(Operator):
|
|
|
|
"Disable an addon"
|
|
|
|
bl_idname = "wm.addon_remove"
|
2011-10-23 17:52:20 +00:00
|
|
|
bl_label = "Remove Addon"
|
2011-09-22 19:50:41 +00:00
|
|
|
|
|
|
|
module = StringProperty(
|
|
|
|
name="Module",
|
|
|
|
description="Module name of the addon to remove",
|
|
|
|
)
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def path_from_addon(module):
|
2011-09-26 09:31:24 +00:00
|
|
|
import os
|
2011-09-23 13:47:29 +00:00
|
|
|
import addon_utils
|
|
|
|
|
|
|
|
for mod in addon_utils.modules(addon_utils.addons_fake_modules):
|
2011-09-22 19:50:41 +00:00
|
|
|
if mod.__name__ == module:
|
|
|
|
filepath = mod.__file__
|
|
|
|
if os.path.exists(filepath):
|
|
|
|
if os.path.splitext(os.path.basename(filepath))[0] == "__init__":
|
|
|
|
return os.path.dirname(filepath), True
|
|
|
|
else:
|
|
|
|
return filepath, False
|
|
|
|
return None, False
|
|
|
|
|
|
|
|
def execute(self, context):
|
2011-09-23 13:47:29 +00:00
|
|
|
import addon_utils
|
2011-09-26 11:22:07 +00:00
|
|
|
import os
|
2011-09-23 13:47:29 +00:00
|
|
|
|
2011-09-22 19:50:41 +00:00
|
|
|
path, isdir = WM_OT_addon_remove.path_from_addon(self.module)
|
|
|
|
if path is None:
|
2012-05-26 09:55:00 +00:00
|
|
|
self.report({'WARNING'}, "Addon path %r could not be found" % path)
|
2011-09-22 19:50:41 +00:00
|
|
|
return {'CANCELLED'}
|
|
|
|
|
2011-10-17 06:58:07 +00:00
|
|
|
# in case its enabled
|
2011-09-22 19:50:41 +00:00
|
|
|
addon_utils.disable(self.module)
|
|
|
|
|
|
|
|
import shutil
|
|
|
|
if isdir:
|
|
|
|
shutil.rmtree(path)
|
|
|
|
else:
|
|
|
|
os.remove(path)
|
|
|
|
|
|
|
|
context.area.tag_redraw()
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
|
|
# lame confirmation check
|
|
|
|
def draw(self, context):
|
|
|
|
self.layout.label(text="Remove Addon: %r?" % self.module)
|
|
|
|
path, isdir = WM_OT_addon_remove.path_from_addon(self.module)
|
|
|
|
self.layout.label(text="Path: %r" % path)
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
|
|
|
wm = context.window_manager
|
|
|
|
return wm.invoke_props_dialog(self, width=600)
|
|
|
|
|
|
|
|
|
|
|
|
class WM_OT_addon_expand(Operator):
|
2011-10-23 17:52:20 +00:00
|
|
|
"Display more information on this addon"
|
2011-09-22 19:50:41 +00:00
|
|
|
bl_idname = "wm.addon_expand"
|
|
|
|
bl_label = ""
|
|
|
|
|
|
|
|
module = StringProperty(
|
|
|
|
name="Module",
|
|
|
|
description="Module name of the addon to expand",
|
|
|
|
)
|
|
|
|
|
|
|
|
def execute(self, context):
|
2011-09-23 13:47:29 +00:00
|
|
|
import addon_utils
|
|
|
|
|
2011-09-22 19:50:41 +00:00
|
|
|
module_name = self.module
|
|
|
|
|
|
|
|
# unlikely to fail, module should have already been imported
|
|
|
|
try:
|
|
|
|
# mod = __import__(module_name)
|
2011-09-23 13:47:29 +00:00
|
|
|
mod = addon_utils.addons_fake_modules.get(module_name)
|
2011-09-22 19:50:41 +00:00
|
|
|
except:
|
|
|
|
import traceback
|
|
|
|
traceback.print_exc()
|
|
|
|
return {'CANCELLED'}
|
|
|
|
|
|
|
|
info = addon_utils.module_bl_info(mod)
|
|
|
|
info["show_expanded"] = not info["show_expanded"]
|
|
|
|
return {'FINISHED'}
|