Sculpt Branch:
[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
24 def rna_idprop_ui_get(item, create=True):
25     try:
26         return item['_RNA_UI']
27     except:
28         if create:
29             item['_RNA_UI'] = {}
30             return item['_RNA_UI']
31         else:
32             return None
33
34
35 def rna_idprop_ui_prop_get(item, prop, create=True):
36
37     rna_ui = rna_idprop_ui_get(item, create)
38
39     if rna_ui == None:
40         return None
41
42     try:
43         return rna_ui[prop]
44     except:
45         rna_ui[prop] = {}
46         return rna_ui[prop]
47
48
49 def rna_idprop_ui_prop_clear(item, prop):
50     rna_ui = rna_idprop_ui_get(item, False)
51
52     if rna_ui == None:
53         return
54
55     try:
56         del rna_ui[prop]
57     except:
58         pass
59
60
61 def draw(layout, context, context_member, use_edit=True):
62
63     def assign_props(prop, val, key):
64         prop.path = context_member
65         prop.property = key
66
67         try:
68             prop.value = str(val)
69         except:
70             pass
71
72     rna_item = eval("context." + context_member)
73
74     # poll should really get this...
75     if not rna_item:
76         return
77
78     items = rna_item.items()
79     items.sort()
80
81     if use_edit:
82         row = layout.row()
83         props = row.operator("wm.properties_add", text="Add")
84         props.path = context_member
85         del row
86
87     for key, val in items:
88
89         if key == '_RNA_UI':
90             continue
91
92         row = layout.row()
93         convert_to_pyobject = getattr(val, "convert_to_pyobject", None)
94
95         val_orig = val
96         if convert_to_pyobject:
97             val_draw = val = val.convert_to_pyobject()
98             val_draw = str(val_draw)
99         else:
100             val_draw = val
101
102         box = row.box()
103
104         if use_edit:
105             split = box.split(percentage=0.75)
106             row = split.row()
107         else:
108             row = box.row()
109
110         row.label(text=key)
111
112         # explicit exception for arrays
113         if convert_to_pyobject and not hasattr(val_orig, "len"):
114             row.label(text=val_draw)
115         else:
116             row.prop(rna_item, '["%s"]' % key, text="")
117
118         if use_edit:
119             row = split.row(align=True)
120             prop = row.operator("wm.properties_edit", text="edit")
121             assign_props(prop, val_draw, key)
122
123             prop = row.operator("wm.properties_remove", text="", icon='ZOOMOUT')
124             assign_props(prop, val_draw, key)
125
126
127 from bpy.props import *
128
129
130 rna_path = StringProperty(name="Property Edit",
131     description="Property path edit", maxlen=1024, default="", hidden=True)
132
133 rna_value = StringProperty(name="Property Value",
134     description="Property value edit", maxlen=1024, default="")
135
136 rna_property = StringProperty(name="Property Name",
137     description="Property name edit", maxlen=1024, default="")
138
139 rna_min = FloatProperty(name="Min", default=0.0, precision=3)
140 rna_max = FloatProperty(name="Max", default=1.0, precision=3)
141
142
143 class WM_OT_properties_edit(bpy.types.Operator):
144     '''Internal use (edit a property path)'''
145     bl_idname = "wm.properties_edit"
146     bl_label = "Edit Property!"
147
148     path = rna_path
149     property = rna_property
150     value = rna_value
151     min = rna_min
152     max = rna_max
153     description = StringProperty(name="Tip", default="")
154
155     # the class instance is not persistant, need to store in the class
156     # not ideal but changes as the op runs.
157     _last_prop = ['']
158
159     def execute(self, context):
160         path = self.properties.path
161         value = self.properties.value
162         prop = self.properties.property
163         prop_old = self._last_prop[0]
164
165         try:
166             value_eval = eval(value)
167         except:
168             value_eval = value
169
170         if type(value_eval) == str:
171             value_eval = '"' + value_eval + '"'
172
173         # First remove
174         item = eval("context.%s" % path)
175
176         rna_idprop_ui_prop_clear(item, prop_old)
177         exec_str = "del item['%s']" % prop_old
178         # print(exec_str)
179         exec(exec_str)
180
181
182         # Reassign
183         exec_str = "item['%s'] = %s" % (prop, value_eval)
184         # print(exec_str)
185         exec(exec_str)
186         self._last_prop[:] = [prop]
187
188         prop_type = type(item[prop])
189
190         prop_ui = rna_idprop_ui_prop_get(item, prop)
191
192         if prop_type in (float, int):
193
194             prop_ui['soft_min'] = prop_ui['min'] = prop_type(self.properties.min)
195             prop_ui['soft_max'] = prop_ui['max'] = prop_type(self.properties.max)
196
197         prop_ui['description'] = self.properties.description
198
199         return ('FINISHED',)
200
201     def invoke(self, context, event):
202
203         self._last_prop[:] = [self.properties.property]
204
205         item = eval("context.%s" % self.properties.path)
206
207         # setup defaults
208         prop_ui = rna_idprop_ui_prop_get(item, self.properties.property, False) # dont create
209         if prop_ui:
210             self.properties.min = prop_ui.get("min", -1000000000)
211             self.properties.max = prop_ui.get("max", 1000000000)
212             self.properties.description = prop_ui.get("description", "")
213
214         wm = context.manager
215         # This crashes, TODO - fix
216         #return wm.invoke_props_popup(self, event)
217
218         wm.invoke_props_popup(self, event)
219         return ('RUNNING_MODAL',)
220
221
222
223 class WM_OT_properties_add(bpy.types.Operator):
224     '''Internal use (edit a property path)'''
225     bl_idname = "wm.properties_add"
226     bl_label = "Add Property"
227
228     path = rna_path
229
230     def execute(self, context):
231         item = eval("context.%s" % self.properties.path)
232
233         def unique_name(names):
234             prop = 'prop'
235             prop_new = prop
236             i = 1
237             while prop_new in names:
238                 prop_new = prop + str(i)
239                 i += 1
240
241             return prop_new
242
243         property = unique_name(item.keys())
244
245         item[property] = 1.0
246         return ('FINISHED',)
247
248
249 class WM_OT_properties_remove(bpy.types.Operator):
250     '''Internal use (edit a property path)'''
251     bl_idname = "wm.properties_remove"
252     bl_label = "Add Property"
253
254     path = rna_path
255     property = rna_property
256
257     def execute(self, context):
258         item = eval("context.%s" % self.properties.path)
259         del item[self.properties.property]
260         return ('FINISHED',)