more ui api changes.
[blender.git] / release / scripts / modules / rna_prop_ui.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8 compliant>
20
21 import bpy
22
23 def rna_idprop_ui_get(item, create=True):
24     try:
25         return item['_RNA_UI']
26     except:
27         if create:
28             item['_RNA_UI'] = {}
29             return item['_RNA_UI']
30         else:
31             return None
32
33
34 def rna_idprop_ui_prop_get(item, prop, create=True):
35
36     rna_ui = rna_idprop_ui_get(item, create)
37
38     if rna_ui == None:
39         return None
40
41     try:
42         return rna_ui[prop]
43     except:
44         rna_ui[prop] = {}
45         return rna_ui[prop]
46
47
48 def rna_idprop_ui_prop_clear(item, prop):
49     rna_ui = rna_idprop_ui_get(item, False)
50
51     if rna_ui == None:
52         return
53
54     try:
55         del rna_ui[prop]
56     except:
57         pass
58
59
60 def draw(layout, context, context_member, use_edit = True):
61
62     def assign_props(prop, val, key):
63         prop.path = context_member
64         prop.property = key
65
66         try:
67             prop.value = str(val)
68         except:
69             pass
70
71     rna_item = eval("context." + context_member)
72
73     items = rna_item.items()
74     items.sort()
75
76     if use_edit:
77         row = layout.row()
78         props = row.operator("wm.properties_add", text="Add")
79         props.path = context_member
80         del row
81
82     for key, val in items:
83
84         if key == '_RNA_UI':
85             continue
86
87         row = layout.row()
88         convert_to_pyobject = getattr(val, "convert_to_pyobject", None)
89
90         val_orig = val
91         if convert_to_pyobject:
92             val_draw = val = val.convert_to_pyobject()
93             val_draw = str(val_draw)
94         else:
95             val_draw = val
96
97         box = row.box()
98
99         if use_edit:
100             split = box.split(percentage=0.75)
101             row = split.row()
102         else:
103             row = box.row()
104
105         row.label(text=key)
106
107         # explicit exception for arrays
108         if convert_to_pyobject and not hasattr(val_orig, "len"):
109             row.label(text=val_draw)
110         else:
111             row.prop(rna_item, '["%s"]' % key, text="")
112
113         if use_edit:
114             row = split.row(align=True)
115             prop = row.operator("wm.properties_edit", text="edit")
116             assign_props(prop, val_draw, key)
117
118             prop = row.operator("wm.properties_remove", text="", icon='ICON_ZOOMOUT')
119             assign_props(prop, val_draw, key)
120
121
122 from bpy.props import *
123
124
125 rna_path = StringProperty(name="Property Edit",
126     description="Property path edit", maxlen=1024, default="", hidden=True)
127
128 rna_value = StringProperty(name="Property Value",
129     description="Property value edit", maxlen=1024, default="")
130
131 rna_property = StringProperty(name="Property Name",
132     description="Property name edit", maxlen=1024, default="")
133
134 rna_min = FloatProperty(name="Min", default=0.0, precision=3)
135 rna_max = FloatProperty(name="Max", default=1.0, precision=3)
136
137 class WM_OT_properties_edit(bpy.types.Operator):
138     '''Internal use (edit a property path)'''
139     bl_idname = "wm.properties_edit"
140     bl_label = "Edit Property!"
141
142     path = rna_path
143     property = rna_property
144     value = rna_value
145     min = rna_min
146     max = rna_max
147     description = StringProperty(name="Tip", default="")
148
149     # the class instance is not persistant, need to store in the class
150     # not ideal but changes as the op runs.
151     _last_prop = ['']
152
153     def execute(self, context):
154         path = self.properties.path
155         value = self.properties.value
156         prop = self.properties.property
157         prop_old = self._last_prop[0]
158
159         try:
160             value_eval = eval(value)
161         except:
162             value_eval = value
163
164         if type(value_eval) == str:
165             value_eval = '"' + value_eval + '"'
166
167         # First remove
168         item = eval("context.%s" % path)
169
170         rna_idprop_ui_prop_clear(item, prop_old)
171         exec_str = "del item['%s']" % prop_old
172         # print(exec_str)
173         exec(exec_str)
174
175
176         # Reassign
177         exec_str = "item['%s'] = %s" % (prop, value_eval)
178         # print(exec_str)
179         exec(exec_str)
180
181         prop_type = type(item[prop])
182
183         prop_ui = rna_idprop_ui_prop_get(item, prop)
184
185         if prop_type in (float, int):
186
187             prop_ui['soft_min'] = prop_ui['min'] = prop_type(self.properties.min)
188             prop_ui['soft_max'] = prop_ui['max'] = prop_type(self.properties.max)
189
190         prop_ui['description'] = self.properties.description
191
192         return ('FINISHED',)
193
194     def invoke(self, context, event):
195
196         self._last_prop[:] = [self.properties.property]
197
198         item = eval("context.%s" % self.properties.path)
199
200         # setup defaults
201         prop_ui = rna_idprop_ui_prop_get(item, self.properties.property, False) # dont create
202         if prop_ui:
203             self.properties.min = prop_ui.get("min", -1000000000)
204             self.properties.max = prop_ui.get("max",  1000000000)
205             self.properties.description = prop_ui.get("description",  "")
206
207         if 0:
208             _message= "PyConsole, press Ctrl+D to unlock the BGE"
209             import sys
210
211             # evaluate commands in current namespace
212             frame= sys._getframe()
213             namespace = frame.f_globals.copy()
214             namespace.update(frame.f_locals)
215
216             import code
217
218             # Autocomp in python, not as comprehensive as IPython
219             import rlcompleter
220
221             try: # ick, some pythons dont have this
222                 import readline
223                 readline.parse_and_bind("tab: complete")
224             except:
225                 pass
226
227             code.interact(banner=_message, local=namespace)
228
229         wm = context.manager
230         wm.invoke_props_popup(self, event)
231         return ('RUNNING_MODAL',)
232
233
234 class WM_OT_properties_add(bpy.types.Operator):
235     '''Internal use (edit a property path)'''
236     bl_idname = "wm.properties_add"
237     bl_label = "Add Property"
238
239     path = rna_path
240
241     def execute(self, context):
242         item = eval("context.%s" % self.properties.path)
243
244         def unique_name(names):
245             prop = 'prop'
246             prop_new = prop
247             i = 1
248             while prop_new in names:
249                 prop_new = prop + str(i)
250                 i+=1
251
252             return prop_new
253
254         property = unique_name(item.keys())
255
256         item[property] = 1.0
257         return ('FINISHED',)
258
259 class WM_OT_properties_remove(bpy.types.Operator):
260     '''Internal use (edit a property path)'''
261     bl_idname = "wm.properties_remove"
262     bl_label = "Add Property"
263
264     path = rna_path
265     property = rna_property
266
267     def execute(self, context):
268         item = eval("context.%s" % self.properties.path)
269         del item[self.properties.property]
270         return ('FINISHED',)
271