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