ID properties that are displayed via RNA can now define their own UI settings,
[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 EVIL_PROP = "act_property"
24 EVIL_PROP_PATH = EVIL_PROP + '_path'
25 EVIL_PROP_VALUE = EVIL_PROP + '_value'
26 EVIL_PROP_PROP = EVIL_PROP + '_prop'
27 EVIL_PROP_PROP_ORIG = EVIL_PROP + '_prop_orig'
28
29 # nasty!, use a scene property to store the active edit item
30 def evil_prop_init():
31     Scene = bpy.types.Scene
32     if EVIL_PROP_PROP_ORIG not in Scene.bl_rna.properties:
33         Scene.StringProperty(attr=EVIL_PROP_PATH)
34         Scene.StringProperty(attr=EVIL_PROP_VALUE)
35         Scene.StringProperty(attr=EVIL_PROP_PROP)
36         Scene.StringProperty(attr=EVIL_PROP_PROP_ORIG)
37
38
39 def draw(layout, context, context_member, use_edit = True):
40     
41     def assign_props(prop, val, key):
42         prop.path = context_member
43         prop.property = key
44         
45         try:
46             prop.value = str(val)
47         except:
48             pass
49     
50     rna_item = eval("context." + context_member)
51     
52     evil_prop_init()
53
54     scene = context.scene
55     
56     global_path = getattr(scene, EVIL_PROP_PATH)
57     global_value = getattr(scene, EVIL_PROP_VALUE)
58     global_prop = getattr(scene, EVIL_PROP_PROP)
59     global_prop_orig = getattr(scene, EVIL_PROP_PROP_ORIG)
60     
61     # print((global_path, global_value, global_prop, global_prop_orig))
62
63     items = rna_item.items()
64     items.sort()
65     
66     if use_edit:
67         row = layout.row()
68         props = row.itemO("wm.properties_add", properties=True, text="Add")
69         props.path = context_member
70         del row
71
72     for key, val in items:
73         
74         if key == '_RNA_UI':
75             continue
76         
77         row = layout.row()
78         convert_to_pyobject = getattr(val, "convert_to_pyobject", None)
79         
80         val_orig = val
81         if convert_to_pyobject:
82             val_draw = val = val.convert_to_pyobject()
83             val_draw = str(val_draw)
84         else:
85             val_draw = val
86
87         box = row.box()
88         
89         if use_edit and key == global_prop_orig and context_member == global_path:
90             split = box.split(percentage=0.75)
91             
92             row = split.row()
93             row.itemR(scene, EVIL_PROP_PROP)
94             row.itemR(scene, EVIL_PROP_VALUE)
95             
96             row = split.column()
97             prop = row.itemO("wm.properties_edit_end", properties=True, text="done")
98             assign_props(prop, val_draw, key)
99             
100         else:
101             if use_edit:
102                 split = box.split(percentage=0.75)
103                 row = split.row()
104             else:
105                 row = box.row()
106             
107             row.itemL(text=key)
108             
109             # explicit exception for arrays
110             if convert_to_pyobject and not hasattr(val_orig, "len"):
111                 row.itemL(text=val_draw)
112             else:
113                 row.itemR(rna_item, '["%s"]' % key, text="")
114                 
115             if use_edit:
116                 row = split.row(align=True)
117                 prop = row.itemO("wm.properties_edit_begin", properties=True, text="edit")
118                 assign_props(prop, val_draw, key)
119                 
120                 prop = row.itemO("wm.properties_remove", properties=True, text="", icon='ICON_ZOOMOUT')
121                 assign_props(prop, val_draw, key)
122     
123
124 from bpy.props import *
125
126
127 rna_path = StringProperty(name="Property Edit",
128     description="Property path edit", maxlen=1024, default="")
129
130 rna_value = StringProperty(name="Property Value",
131     description="Property value edit", maxlen=1024, default="")
132
133 rna_property = StringProperty(name="Property Name",
134     description="Property name edit", maxlen=1024, default="")
135
136 class WM_OT_properties_edit_begin(bpy.types.Operator):
137     '''Internal use (edit a property path)'''
138     bl_idname = "wm.properties_edit_begin"
139     bl_label = "Edit Property"
140
141     path = rna_path
142     value = rna_value
143     property = rna_property
144
145     def execute(self, context):
146         scene = context.scene
147         
148         setattr(scene, EVIL_PROP_PATH, self.path)
149         setattr(scene, EVIL_PROP_VALUE, self.value)
150         setattr(scene, EVIL_PROP_PROP, self.property)
151         setattr(scene, EVIL_PROP_PROP_ORIG, self.property)
152         
153         return ('FINISHED',)
154
155
156 class WM_OT_properties_edit_end(bpy.types.Operator):
157     '''Internal use (edit a property path)'''
158     bl_idname = "wm.properties_edit_end"
159     bl_label = "Edit Property"
160
161     path = rna_path
162     value = rna_value
163     property = rna_property
164
165     def execute(self, context):
166         
167         scene = context.scene
168         global_path = getattr(scene, EVIL_PROP_PATH)
169         global_value = getattr(scene, EVIL_PROP_VALUE)
170         global_prop = getattr(scene, EVIL_PROP_PROP)
171         
172         setattr(scene, EVIL_PROP_PATH, "")
173         setattr(scene, EVIL_PROP_VALUE, "")
174         setattr(scene, EVIL_PROP_PROP, "")
175         setattr(scene, EVIL_PROP_PROP_ORIG, "")
176         
177         try:
178             value = eval(global_value)
179         except:
180             value = global_value
181         
182         if type(value) == str:
183             value = '"' + value + '"'
184         
185         
186         # First remove
187         exec_str = "del context.%s['%s']" % (global_path, self.property)
188         # print(exec_str)
189         exec(exec_str)
190         
191         
192         # Reassign
193         exec_str = "context.%s['%s'] = %s" % (global_path, global_prop, value)
194         # print(exec_str)
195         exec(exec_str)
196         
197         return ('FINISHED',)
198
199
200 class WM_OT_properties_add(bpy.types.Operator):
201     '''Internal use (edit a property path)'''
202     bl_idname = "wm.properties_add"
203     bl_label = "Add Property"
204
205     path = rna_path
206
207     def execute(self, context):
208         item = eval("context.%s" % self.path)
209         
210         def unique_name(names):
211             prop = 'prop'
212             prop_new = prop
213             i = 1
214             while prop_new in names:
215                 prop_new = prop + str(i)
216                 i+=1
217         
218             return prop_new
219         
220         property = unique_name(item.keys())
221         
222         item[property] = 1.0
223         return ('FINISHED',)
224         
225 class WM_OT_properties_remove(bpy.types.Operator):
226     '''Internal use (edit a property path)'''
227     bl_idname = "wm.properties_remove"
228     bl_label = "Add Property"
229
230     path = rna_path
231     property = rna_property
232
233     def execute(self, context):
234         item = eval("context.%s" % self.path)
235         del item[self.property]
236         return ('FINISHED',)