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