5069d206a3fcdda9dbf03c16441bd0ec7a8b3d29
[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"
72
73     path = rna_path_prop
74     value = BoolProperty(name="Value",
75             description="Assignment value", default=True)
76
77     execute = execute_context_assign
78
79
80 class WM_OT_context_set_int(bpy.types.Operator): # same as enum
81     '''Set a context value.'''
82     bl_idname = "wm.context_set_int"
83     bl_label = "Context Set"
84
85     path = rna_path_prop
86     value = IntProperty(name="Value", description="Assign value", default=0)
87
88     execute = execute_context_assign
89
90
91 class WM_OT_context_set_float(bpy.types.Operator): # same as enum
92     '''Set a context value.'''
93     bl_idname = "wm.context_set_float"
94     bl_label = "Context Set"
95
96     path = rna_path_prop
97     value = FloatProperty(name="Value",
98             description="Assignment value", default=0.0)
99
100     execute = execute_context_assign
101
102
103 class WM_OT_context_set_string(bpy.types.Operator): # same as enum
104     '''Set a context value.'''
105     bl_idname = "wm.context_set_string"
106     bl_label = "Context Set"
107
108     path = rna_path_prop
109     value = StringProperty(name="Value",
110             description="Assign value", maxlen=1024, default="")
111
112     execute = execute_context_assign
113
114
115 class WM_OT_context_set_enum(bpy.types.Operator):
116     '''Set a context value.'''
117     bl_idname = "wm.context_set_enum"
118     bl_label = "Context Set"
119
120     path = rna_path_prop
121     value = StringProperty(name="Value",
122             description="Assignment value (as a string)",
123             maxlen=1024, default="")
124
125     execute = execute_context_assign
126
127
128 class WM_OT_context_toggle(bpy.types.Operator):
129     '''Toggle a context value.'''
130     bl_idname = "wm.context_toggle"
131     bl_label = "Context Toggle"
132     path = rna_path_prop
133
134     def execute(self, context):
135
136         if context_path_validate(context, self.properties.path) is Ellipsis:
137             return ('PASS_THROUGH',)
138
139         exec("context.%s=not (context.%s)" %
140             (self.properties.path, self.properties.path))
141
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) is Ellipsis:
160             return ('PASS_THROUGH',)
161
162         exec("context.%s = ['%s', '%s'][context.%s!='%s']" % \
163             (self.properties.path, self.properties.value_1,\
164              self.properties.value_2, self.properties.path,
165              self.properties.value_2))
166
167         return ('FINISHED',)
168
169
170 class WM_OT_context_cycle_int(bpy.types.Operator):
171     '''Set a context value. Useful for cycling active material,
172     vertex keys, groups' etc.'''
173     bl_idname = "wm.context_cycle_int"
174     bl_label = "Context Int Cycle"
175     path = rna_path_prop
176     reverse = rna_reverse_prop
177
178     def execute(self, context):
179
180         value = context_path_validate(context, self.properties.path)
181         if value is Ellipsis:
182             return ('PASS_THROUGH',)
183
184         self.properties.value = value
185         if self.properties.reverse:
186             self.properties.value -= 1
187         else:
188             self.properties.value += 1
189         execute_context_assign(self, context)
190
191         if self.properties.value != eval("context.%s" % self.properties.path):
192             # relies on rna clamping int's out of the range
193             if self.properties.reverse:
194                 self.properties.value = (1 << 32)
195             else:
196                 self.properties.value = - (1 << 32)
197             execute_context_assign(self, context)
198
199         return ('FINISHED',)
200
201
202 class WM_OT_context_cycle_enum(bpy.types.Operator):
203     '''Toggle a context value.'''
204     bl_idname = "wm.context_cycle_enum"
205     bl_label = "Context Enum Cycle"
206
207     path = rna_path_prop
208     reverse = rna_reverse_prop
209
210     def execute(self, context):
211
212         value = context_path_validate(context, self.properties.path)
213         if value is Ellipsis:
214             return ('PASS_THROUGH',)
215
216         orig_value = value
217
218         # Have to get rna enum values
219         rna_struct_str, rna_prop_str = self.properties.path.rsplit('.', 1)
220         i = rna_prop_str.find('[')
221
222         # just incse we get "context.foo.bar[0]"
223         if i != -1:
224             rna_prop_str = rna_prop_str[0:i]
225
226         rna_struct = eval("context.%s.rna_type" % rna_struct_str)
227
228         rna_prop = rna_struct.properties[rna_prop_str]
229
230         if type(rna_prop) != bpy.types.EnumProperty:
231             raise Exception("expected an enum property")
232
233         enums = rna_struct.properties[rna_prop_str].items.keys()
234         orig_index = enums.index(orig_value)
235
236         # Have the info we need, advance to the next item
237         if self.properties.reverse:
238             if orig_index == 0:
239                 advance_enum = enums[-1]
240             else:
241                 advance_enum = enums[orig_index-1]
242         else:
243             if orig_index == len(enums) - 1:
244                 advance_enum = enums[0]
245             else:
246                 advance_enum = enums[orig_index + 1]
247
248         # set the new value
249         exec("context.%s=advance_enum" % self.properties.path)
250         return ('FINISHED',)
251
252 doc_id = StringProperty(name="Doc ID",
253         description="", maxlen=1024, default="", hidden=True)
254
255 doc_new = StringProperty(name="Edit Description",
256         description="", maxlen=1024, default="")
257
258
259 class WM_OT_doc_view(bpy.types.Operator):
260     '''Load online reference docs'''
261     bl_idname = "wm.doc_view"
262     bl_label = "View Documentation"
263
264     doc_id = doc_id
265     _prefix = 'http://www.blender.org/documentation/250PythonDoc'
266
267     def _nested_class_string(self, class_string):
268         ls = []
269         class_obj = getattr(bpy.types, class_string, None).bl_rna
270         while class_obj:
271             ls.insert(0, class_obj)
272             class_obj = class_obj.nested
273         return '.'.join([class_obj.identifier for class_obj in ls])
274
275     def execute(self, context):
276         id_split = self.properties.doc_id.split('.')
277         if len(id_split) == 1: # rna, class
278             url = '%s/bpy.types.%s-class.html' % (self._prefix, id_split[0])
279         elif len(id_split) == 2: # rna, class.prop
280             class_name, class_prop = id_split
281
282             if hasattr(bpy.types, class_name.upper() + '_OT_' + class_prop):
283                 url = '%s/bpy.ops.%s-module.html#%s' % \
284                         (self._prefix, class_name, class_prop)
285             else:
286                 # It so happens that epydoc nests these
287                 class_name_full = self._nested_class_string(class_name)
288                 url = '%s/bpy.types.%s-class.html#%s' % \
289                         (self._prefix, class_name_full, class_prop)
290
291         else:
292             return ('PASS_THROUGH',)
293
294         import webbrowser
295         webbrowser.open(url)
296
297         return ('FINISHED',)
298
299
300 class WM_OT_doc_edit(bpy.types.Operator):
301     '''Load online reference docs'''
302     bl_idname = "wm.doc_edit"
303     bl_label = "Edit Documentation"
304
305     doc_id = doc_id
306     doc_new = doc_new
307
308     _url = "http://www.mindrones.com/blender/svn/xmlrpc.php"
309
310     def _send_xmlrpc(self, data_dict):
311         print("sending data:", data_dict)
312
313         import xmlrpc.client
314         user = 'blenderuser'
315         pwd = 'blender>user'
316
317         docblog = xmlrpc.client.ServerProxy(self._url)
318         docblog.metaWeblog.newPost(1, user, pwd, data_dict, 1)
319
320     def execute(self, context):
321
322         doc_id = self.properties.doc_id
323         doc_new = self.properties.doc_new
324
325         class_name, class_prop = doc_id.split('.')
326
327         if not doc_new:
328             return ('RUNNING_MODAL',)
329
330         # check if this is an operator
331         op_name = class_name.upper() + '_OT_' + class_prop
332         op_class = getattr(bpy.types, op_name, None)
333
334         # Upload this to the web server
335         upload = {}
336
337         if op_class:
338             rna = op_class.bl_rna
339             doc_orig = rna.description
340             if doc_orig == doc_new:
341                 return ('RUNNING_MODAL',)
342
343             print("op - old:'%s' -> new:'%s'" % (doc_orig, doc_new))
344             upload["title"] = 'OPERATOR %s:%s' % (doc_id, doc_orig)
345             upload["description"] = doc_new
346
347             self._send_xmlrpc(upload)
348
349         else:
350             rna = getattr(bpy.types, class_name).bl_rna
351             doc_orig = rna.properties[class_prop].description
352             if doc_orig == doc_new:
353                 return ('RUNNING_MODAL',)
354
355             print("rna - old:'%s' -> new:'%s'" % (doc_orig, doc_new))
356             upload["title"] = 'RNA %s:%s' % (doc_id, doc_orig)
357
358         upload["description"] = doc_new
359
360         self._send_xmlrpc(upload)
361
362         return ('FINISHED',)
363
364     def invoke(self, context, event):
365         wm = context.manager
366         return wm.invoke_props_popup(self, event)
367
368
369 class WM_OT_reload_scripts(bpy.types.Operator):
370     '''Load online reference docs'''
371     bl_idname = "wm.reload_scripts"
372     bl_label = "Reload Scripts"
373
374     def execute(self, context):
375         MOD = type(bpy)
376         bpy.load_scripts(True)
377         return ('FINISHED',)
378
379
380 bpy.ops.add(MESH_OT_delete_edgeloop)
381
382 bpy.ops.add(WM_OT_context_set_boolean)
383 bpy.ops.add(WM_OT_context_set_int)
384 bpy.ops.add(WM_OT_context_set_float)
385 bpy.ops.add(WM_OT_context_set_string)
386 bpy.ops.add(WM_OT_context_set_enum)
387 bpy.ops.add(WM_OT_context_toggle)
388 bpy.ops.add(WM_OT_context_toggle_enum)
389 bpy.ops.add(WM_OT_context_cycle_enum)
390 bpy.ops.add(WM_OT_context_cycle_int)
391
392 bpy.ops.add(WM_OT_doc_view)
393 bpy.ops.add(WM_OT_doc_edit)
394
395 bpy.ops.add(WM_OT_reload_scripts)
396
397 # experemental!
398 import rna_prop_ui
399 bpy.ops.add(rna_prop_ui.WM_OT_properties_edit)
400 bpy.ops.add(rna_prop_ui.WM_OT_properties_add)
401 bpy.ops.add(rna_prop_ui.WM_OT_properties_remove)