rna flag PROP_ENUM_FLAG which makes rna props a tuple of enums when converted into...
[blender.git] / release / scripts / op / wm.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-80 compliant>
20
21 import bpy
22 import os
23
24 from bpy.props import *
25
26 class MESH_OT_delete_edgeloop(bpy.types.Operator):
27     '''Export a single object as a stanford PLY with normals,
28     colours and texture coordinates.'''
29     bl_idname = "mesh.delete_edgeloop"
30     bl_label = "Delete Edge Loop"
31
32     def execute(self, context):
33         bpy.ops.tfm.edge_slide(value=1.0)
34         bpy.ops.mesh.select_more()
35         bpy.ops.mesh.remove_doubles()
36
37         return ('FINISHED',)
38
39 rna_path_prop = StringProperty(name="Context Attributes",
40         description="rna context string", maxlen=1024, default="")
41
42 rna_reverse_prop = BoolProperty(name="Reverse",
43         description="Cycle backwards", default=False)
44
45 class NullPathMember:
46     pass
47
48
49 def context_path_validate(context, path):
50     import sys
51     try:
52         value = eval("context.%s" % path)
53     except AttributeError:
54         if "'NoneType'" in str(sys.exc_info()[1]):
55             # One of the items in the rna path is None, just ignore this
56             value = NullPathMember
57         else:
58             # We have a real error in the rna path, dont ignore that
59             raise
60
61     return value
62
63
64 def execute_context_assign(self, context):
65     if context_path_validate(context, self.properties.path) == NullPathMember:
66         return ('PASS_THROUGH',)
67     exec("context.%s=self.properties.value" % self.properties.path)
68     return ('FINISHED',)
69
70
71 class WM_OT_context_set_boolean(bpy.types.Operator):
72     '''Set a context value.'''
73     bl_idname = "wm.context_set_boolean"
74     bl_label = "Context Set"
75
76     path = rna_path_prop
77     value = BoolProperty(name="Value",
78             description="Assignment value", default=True)
79
80     execute = execute_context_assign
81
82
83 class WM_OT_context_set_int(bpy.types.Operator): # same as enum
84     '''Set a context value.'''
85     bl_idname = "wm.context_set_int"
86     bl_label = "Context Set"
87
88     path = rna_path_prop
89     value = IntProperty(name="Value", description="Assign value", default=0)
90
91     execute = execute_context_assign
92
93
94 class WM_OT_context_set_float(bpy.types.Operator): # same as enum
95     '''Set a context value.'''
96     bl_idname = "wm.context_set_float"
97     bl_label = "Context Set"
98
99     path = rna_path_prop
100     value = FloatProperty(name="Value",
101             description="Assignment value", default=0.0)
102
103     execute = execute_context_assign
104
105
106 class WM_OT_context_set_string(bpy.types.Operator): # same as enum
107     '''Set a context value.'''
108     bl_idname = "wm.context_set_string"
109     bl_label = "Context Set"
110
111     path = rna_path_prop
112     value = StringProperty(name="Value",
113             description="Assign value", maxlen=1024, default="")
114
115     execute = execute_context_assign
116
117
118 class WM_OT_context_set_enum(bpy.types.Operator):
119     '''Set a context value.'''
120     bl_idname = "wm.context_set_enum"
121     bl_label = "Context Set"
122
123     path = rna_path_prop
124     value = StringProperty(name="Value",
125             description="Assignment value (as a string)",
126             maxlen=1024, default="")
127
128     execute = execute_context_assign
129
130
131 class WM_OT_context_toggle(bpy.types.Operator):
132     '''Toggle a context value.'''
133     bl_idname = "wm.context_toggle"
134     bl_label = "Context Toggle"
135     path = rna_path_prop
136
137     def execute(self, context):
138
139         if context_path_validate(context, self.properties.path) == NullPathMember:
140             return ('PASS_THROUGH',)
141
142         exec("context.%s=not (context.%s)" % (self.properties.path, self.properties.path))
143         return ('FINISHED',)
144
145
146 class WM_OT_context_toggle_enum(bpy.types.Operator):
147     '''Toggle a context value.'''
148     bl_idname = "wm.context_toggle_enum"
149     bl_label = "Context Toggle Values"
150
151     path = rna_path_prop
152     value_1 = StringProperty(name="Value", \
153                 description="Toggle enum", maxlen=1024, default="")
154
155     value_2 = StringProperty(name="Value", \
156                 description="Toggle enum", maxlen=1024, default="")
157
158     def execute(self, context):
159
160         if context_path_validate(context, self.properties.path) == NullPathMember:
161             return ('PASS_THROUGH',)
162
163         exec("context.%s = ['%s', '%s'][context.%s!='%s']" % \
164             (self.properties.path, self.properties.value_1, self.properties.value_2, self.properties.path, self.properties.value_2))
165
166         return ('FINISHED',)
167
168
169 class WM_OT_context_cycle_int(bpy.types.Operator):
170     '''Set a context value. Useful for cycling active material,
171     vertex keys, groups' etc.'''
172     bl_idname = "wm.context_cycle_int"
173     bl_label = "Context Int Cycle"
174     path = rna_path_prop
175     reverse = rna_reverse_prop
176
177     def execute(self, context):
178
179         value = context_path_validate(context, self.properties.path)
180         if value == NullPathMember:
181             return ('PASS_THROUGH',)
182
183         self.properties.value = value
184         if self.properties.reverse:
185             self.properties.value -= 1
186         else:
187             self.properties.value += 1
188         execute_context_assign(self, context)
189
190         if self.properties.value != eval("context.%s" % self.properties.path):
191             # relies on rna clamping int's out of the range
192             if self.properties.reverse:
193                 self.properties.value = (1 << 32)
194             else:
195                 self.properties.value = - (1 << 32)
196             execute_context_assign(self, context)
197
198         return ('FINISHED',)
199
200
201 class WM_OT_context_cycle_enum(bpy.types.Operator):
202     '''Toggle a context value.'''
203     bl_idname = "wm.context_cycle_enum"
204     bl_label = "Context Enum Cycle"
205
206     path = rna_path_prop
207     reverse = rna_reverse_prop
208
209     def execute(self, context):
210
211         value = context_path_validate(context, self.properties.path)
212         if value == NullPathMember:
213             return ('PASS_THROUGH',)
214
215         orig_value = value
216
217         # Have to get rna enum values
218         rna_struct_str, rna_prop_str = self.properties.path.rsplit('.', 1)
219         i = rna_prop_str.find('[')
220
221         # just incse we get "context.foo.bar[0]"
222         if i != -1:
223             rna_prop_str = rna_prop_str[0:i]
224
225         rna_struct = eval("context.%s.rna_type" % rna_struct_str)
226
227         rna_prop = rna_struct.properties[rna_prop_str]
228
229         if type(rna_prop) != bpy.types.EnumProperty:
230             raise Exception("expected an enum property")
231
232         enums = rna_struct.properties[rna_prop_str].items.keys()
233         orig_index = enums.index(orig_value)
234
235         # Have the info we need, advance to the next item
236         if self.properties.reverse:
237             if orig_index == 0:
238                 advance_enum = enums[-1]
239             else:
240                 advance_enum = enums[orig_index-1]
241         else:
242             if orig_index == len(enums) - 1:
243                 advance_enum = enums[0]
244             else:
245                 advance_enum = enums[orig_index + 1]
246
247         # set the new value
248         exec("context.%s=advance_enum" % self.properties.path)
249         return ('FINISHED',)
250
251 doc_id = StringProperty(name="Doc ID",
252         description="", maxlen=1024, default="", hidden=True)
253
254 doc_new = StringProperty(name="Edit Description",
255         description="", maxlen=1024, default="")
256
257
258 class WM_OT_doc_view(bpy.types.Operator):
259     '''Load online reference docs'''
260     bl_idname = "wm.doc_view"
261     bl_label = "View Documentation"
262
263     doc_id = doc_id
264     _prefix = 'http://www.blender.org/documentation/250PythonDoc'
265
266     def _nested_class_string(self, class_string):
267         ls = []
268         class_obj = getattr(bpy.types, class_string, None).bl_rna
269         while class_obj:
270             ls.insert(0, class_obj)
271             class_obj = class_obj.nested
272         return '.'.join([class_obj.identifier for class_obj in ls])
273
274     def execute(self, context):
275         id_split = self.properties.doc_id.split('.')
276         if len(id_split) == 1: # rna, class
277             url = '%s/bpy.types.%s-class.html' % (self._prefix, id_split[0])
278         elif len(id_split) == 2: # rna, class.prop
279             class_name, class_prop = id_split
280
281             if hasattr(bpy.types, class_name.upper() + '_OT_' + class_prop):
282                 url = '%s/bpy.ops.%s-module.html#%s' % \
283                         (self._prefix, class_name, class_prop)
284             else:
285                 # It so happens that epydoc nests these
286                 class_name_full = self._nested_class_string(class_name)
287                 url = '%s/bpy.types.%s-class.html#%s' % \
288                         (self._prefix, class_name_full, class_prop)
289
290         else:
291             return ('PASS_THROUGH',)
292
293         import webbrowser
294         webbrowser.open(url)
295
296         return ('FINISHED',)
297
298
299 class WM_OT_doc_edit(bpy.types.Operator):
300     '''Load online reference docs'''
301     bl_idname = "wm.doc_edit"
302     bl_label = "Edit Documentation"
303
304     doc_id = doc_id
305     doc_new = doc_new
306
307     _url = "http://www.mindrones.com/blender/svn/xmlrpc.php"
308
309     def _send_xmlrpc(self, data_dict):
310         print("sending data:", data_dict)
311
312         import xmlrpc.client
313         user = 'blenderuser'
314         pwd = 'blender>user'
315
316         docblog = xmlrpc.client.ServerProxy(self._url)
317         docblog.metaWeblog.newPost(1, user, pwd, data_dict, 1)
318
319     def execute(self, context):
320
321         class_name, class_prop = self.properties.doc_id.split('.')
322
323         if not self.properties.doc_new:
324             return ('RUNNING_MODAL',)
325
326         # check if this is an operator
327         op_name = class_name.upper() + '_OT_' + class_prop
328         op_class = getattr(bpy.types, op_name, None)
329
330         # Upload this to the web server
331         upload = {}
332
333         if op_class:
334             rna = op_class.bl_rna
335             doc_orig = rna.description
336             if doc_orig == self.properties.doc_new:
337                 return ('RUNNING_MODAL',)
338
339             print("op - old:'%s' -> new:'%s'" % (doc_orig, self.properties.doc_new))
340             upload["title"] = 'OPERATOR %s:%s' % (self.properties.doc_id, doc_orig)
341             upload["description"] = self.properties.doc_new
342
343             self._send_xmlrpc(upload)
344
345         else:
346             rna = getattr(bpy.types, class_name).bl_rna
347             doc_orig = rna.properties[class_prop].description
348             if doc_orig == self.properties.doc_new:
349                 return ('RUNNING_MODAL',)
350
351             print("rna - old:'%s' -> new:'%s'" % (doc_orig, self.properties.doc_new))
352             upload["title"] = 'RNA %s:%s' % (self.properties.doc_id, doc_orig)
353
354         upload["description"] = self.properties.doc_new
355
356         self._send_xmlrpc(upload)
357
358         return ('FINISHED',)
359
360     def invoke(self, context, event):
361         wm = context.manager
362         return wm.invoke_props_popup(self, event)
363
364
365 class WM_OT_reload_scripts(bpy.types.Operator):
366     '''Load online reference docs'''
367     bl_idname = "wm.reload_scripts"
368     bl_label = "Reload Scripts"
369
370     def execute(self, context):
371         MOD = type(bpy)
372         import sys
373         bpy.load_scripts(True)
374         '''
375         prefix = bpy.base_path
376         items = list(sys.modules.items())
377         items.sort()
378         items.reverse()
379         for mod_name, mod in items:
380             mod_file = getattr(mod, "__file__", "")
381             if mod_file.startswith(prefix) and "__init__" not in mod_file:
382                 print(mod_file)
383                 reload(mod)
384                 """
385                 for submod_name in dir(mod):
386                     submod = getattr(mod, submod_name)
387                     if isinstance(submod, MOD):
388                         reload(submod)
389                 """
390             else:
391                 print("Ignoring:", mod, mod_file)
392         '''
393         return ('FINISHED',)
394
395
396 bpy.ops.add(MESH_OT_delete_edgeloop)
397
398 bpy.ops.add(WM_OT_context_set_boolean)
399 bpy.ops.add(WM_OT_context_set_int)
400 bpy.ops.add(WM_OT_context_set_float)
401 bpy.ops.add(WM_OT_context_set_string)
402 bpy.ops.add(WM_OT_context_set_enum)
403 bpy.ops.add(WM_OT_context_toggle)
404 bpy.ops.add(WM_OT_context_toggle_enum)
405 bpy.ops.add(WM_OT_context_cycle_enum)
406 bpy.ops.add(WM_OT_context_cycle_int)
407
408 bpy.ops.add(WM_OT_doc_view)
409 bpy.ops.add(WM_OT_doc_edit)
410
411 bpy.ops.add(WM_OT_reload_scripts)
412
413 # experemental!
414 import rna_prop_ui
415 bpy.ops.add(rna_prop_ui.WM_OT_properties_edit)
416 bpy.ops.add(rna_prop_ui.WM_OT_properties_add)
417 bpy.ops.add(rna_prop_ui.WM_OT_properties_remove)