Merge branch 'blender2.7'
[blender.git] / release / scripts / modules / bpy_types.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8-80 compliant>
20
21 from _bpy import types as bpy_types
22 import _bpy
23
24 StructRNA = bpy_types.bpy_struct
25 StructMetaPropGroup = bpy_types.bpy_struct_meta_idprop
26 # StructRNA = bpy_types.Struct
27
28 bpy_types.BlendDataLibraries.load = _bpy._library_load
29 bpy_types.BlendDataLibraries.write = _bpy._library_write
30 bpy_types.BlendData.user_map = _bpy._rna_id_collection_user_map
31 bpy_types.BlendData.batch_remove = _bpy._rna_id_collection_batch_remove
32
33
34 class Context(StructRNA):
35     __slots__ = ()
36
37     def copy(self):
38         from types import BuiltinMethodType
39         new_context = {}
40         generic_attrs = (
41             *StructRNA.__dict__.keys(),
42             "bl_rna", "rna_type", "copy",
43         )
44         for attr in dir(self):
45             if not (attr.startswith("_") or attr in generic_attrs):
46                 value = getattr(self, attr)
47                 if type(value) != BuiltinMethodType:
48                     new_context[attr] = value
49
50         return new_context
51
52
53 class Library(bpy_types.ID):
54     __slots__ = ()
55
56     @property
57     def users_id(self):
58         """ID data blocks which use this library"""
59         import bpy
60
61         # See: readblenentry.c, IDTYPE_FLAGS_ISLINKABLE,
62         # we could make this an attribute in rna.
63         attr_links = ("actions", "armatures", "brushes", "cameras",
64                       "curves", "grease_pencil", "collections", "images",
65                       "lights", "lattices", "materials", "metaballs",
66                       "meshes", "node_groups", "objects", "scenes",
67                       "sounds", "speakers", "textures", "texts",
68                       "fonts", "worlds")
69
70         return tuple(id_block
71                      for attr in attr_links
72                      for id_block in getattr(bpy.data, attr)
73                      if id_block.library == self)
74
75
76 class Texture(bpy_types.ID):
77     __slots__ = ()
78
79     @property
80     def users_material(self):
81         """Materials that use this texture"""
82         import bpy
83         return tuple(mat for mat in bpy.data.materials
84                      if self in [slot.texture
85                                  for slot in mat.texture_slots
86                                  if slot]
87                      )
88
89     @property
90     def users_object_modifier(self):
91         """Object modifiers that use this texture"""
92         import bpy
93         return tuple(obj for obj in bpy.data.objects if
94                      self in [mod.texture
95                               for mod in obj.modifiers
96                               if mod.type == 'DISPLACE']
97                      )
98
99
100 class Collection(bpy_types.ID):
101     __slots__ = ()
102
103     @property
104     def users_dupli_group(self):
105         """The collection instance objects this collection is used in"""
106         import bpy
107         return tuple(obj for obj in bpy.data.objects
108                      if self == obj.instance_collection)
109
110
111 class Object(bpy_types.ID):
112     __slots__ = ()
113
114     @property
115     def children(self):
116         """All the children of this object. Warning: takes O(len(bpy.data.objects)) time."""
117         import bpy
118         return tuple(child for child in bpy.data.objects
119                      if child.parent == self)
120
121     @property
122     def users_collection(self):
123         """The collections this object is in. Warning: takes O(len(bpy.data.collections) + len(bpy.data.scenes)) time."""
124         import bpy
125         return tuple(collection for collection in bpy.data.collections
126                      if self in collection.objects[:]) + \
127                tuple(scene.collection for scene in bpy.data.scenes
128                      if self in scene.collection.objects[:])
129
130     @property
131     def users_scene(self):
132         """The scenes this object is in. Warning: takes O(len(bpy.data.scenes) * len(bpy.data.objects)) time."""
133         import bpy
134         return tuple(scene for scene in bpy.data.scenes
135                      if self in scene.objects[:])
136
137
138 class WindowManager(bpy_types.ID):
139     __slots__ = ()
140
141     def popup_menu(self, draw_func, title="", icon='NONE'):
142         import bpy
143         popup = self.popmenu_begin__internal(title, icon=icon)
144
145         try:
146             draw_func(popup, bpy.context)
147         finally:
148             self.popmenu_end__internal(popup)
149
150     def popover(
151             self, draw_func, *,
152             ui_units_x=0,
153             keymap=None,
154     ):
155         import bpy
156         popup = self.popover_begin__internal(
157             ui_units_x=ui_units_x,
158         )
159
160         try:
161             draw_func(popup, bpy.context)
162         finally:
163             self.popover_end__internal(popup, keymap=keymap)
164
165     def popup_menu_pie(self, event, draw_func, title="", icon='NONE'):
166         import bpy
167         pie = self.piemenu_begin__internal(title, icon=icon, event=event)
168
169         if pie:
170             try:
171                 draw_func(pie, bpy.context)
172             finally:
173                 self.piemenu_end__internal(pie)
174
175
176 class _GenericBone:
177     """
178     functions for bones, common between Armature/Pose/Edit bones.
179     internal subclassing use only.
180     """
181     __slots__ = ()
182
183     def translate(self, vec):
184         """Utility function to add *vec* to the head and tail of this bone"""
185         self.head += vec
186         self.tail += vec
187
188     def parent_index(self, parent_test):
189         """
190         The same as 'bone in other_bone.parent_recursive'
191         but saved generating a list.
192         """
193         # use the name so different types can be tested.
194         name = parent_test.name
195
196         parent = self.parent
197         i = 1
198         while parent:
199             if parent.name == name:
200                 return i
201             parent = parent.parent
202             i += 1
203
204         return 0
205
206     @property
207     def x_axis(self):
208         """ Vector pointing down the x-axis of the bone.
209         """
210         from mathutils import Vector
211         return self.matrix.to_3x3() @ Vector((1.0, 0.0, 0.0))
212
213     @property
214     def y_axis(self):
215         """ Vector pointing down the y-axis of the bone.
216         """
217         from mathutils import Vector
218         return self.matrix.to_3x3() @ Vector((0.0, 1.0, 0.0))
219
220     @property
221     def z_axis(self):
222         """ Vector pointing down the z-axis of the bone.
223         """
224         from mathutils import Vector
225         return self.matrix.to_3x3() @ Vector((0.0, 0.0, 1.0))
226
227     @property
228     def basename(self):
229         """The name of this bone before any '.' character"""
230         # return self.name.rsplit(".", 1)[0]
231         return self.name.split(".")[0]
232
233     @property
234     def parent_recursive(self):
235         """A list of parents, starting with the immediate parent"""
236         parent_list = []
237         parent = self.parent
238
239         while parent:
240             if parent:
241                 parent_list.append(parent)
242
243             parent = parent.parent
244
245         return parent_list
246
247     @property
248     def center(self):
249         """The midpoint between the head and the tail."""
250         return (self.head + self.tail) * 0.5
251
252     @property
253     def length(self):
254         """
255         The distance from head to tail,
256         when set the head is moved to fit the length.
257         """
258         return self.vector.length
259
260     @length.setter
261     def length(self, value):
262         self.tail = self.head + ((self.tail - self.head).normalized() * value)
263
264     @property
265     def vector(self):
266         """
267         The direction this bone is pointing.
268         Utility function for (tail - head)
269         """
270         return (self.tail - self.head)
271
272     @property
273     def children(self):
274         """A list of all the bones children. Warning: takes O(len(bones)) time."""
275         return [child for child in self._other_bones if child.parent == self]
276
277     @property
278     def children_recursive(self):
279         """A list of all children from this bone. Warning: takes O(len(bones)**2) time."""
280         bones_children = []
281         for bone in self._other_bones:
282             index = bone.parent_index(self)
283             if index:
284                 bones_children.append((index, bone))
285
286         # sort by distance to parent
287         bones_children.sort(key=lambda bone_pair: bone_pair[0])
288         return [bone for index, bone in bones_children]
289
290     @property
291     def children_recursive_basename(self):
292         """
293         Returns a chain of children with the same base name as this bone.
294         Only direct chains are supported, forks caused by multiple children
295         with matching base names will terminate the function
296         and not be returned. Warning: takes O(len(bones)**2) time.
297         """
298         basename = self.basename
299         chain = []
300
301         child = self
302         while True:
303             children = child.children
304             children_basename = []
305
306             for child in children:
307                 if basename == child.basename:
308                     children_basename.append(child)
309
310             if len(children_basename) == 1:
311                 child = children_basename[0]
312                 chain.append(child)
313             else:
314                 if children_basename:
315                     print("multiple basenames found, "
316                           "this is probably not what you want!",
317                           self.name, children_basename)
318
319                 break
320
321         return chain
322
323     @property
324     def _other_bones(self):
325         id_data = self.id_data
326         id_data_type = type(id_data)
327
328         if id_data_type == bpy_types.Object:
329             bones = id_data.pose.bones
330         elif id_data_type == bpy_types.Armature:
331             bones = id_data.edit_bones
332             if not bones:  # not in edit mode
333                 bones = id_data.bones
334
335         return bones
336
337
338 class PoseBone(StructRNA, _GenericBone, metaclass=StructMetaPropGroup):
339     __slots__ = ()
340
341     @property
342     def children(self):
343         obj = self.id_data
344         pbones = obj.pose.bones
345         self_bone = self.bone
346
347         return tuple(pbones[bone.name] for bone in obj.data.bones
348                      if bone.parent == self_bone)
349
350
351 class Bone(StructRNA, _GenericBone, metaclass=StructMetaPropGroup):
352     __slots__ = ()
353
354
355 class EditBone(StructRNA, _GenericBone, metaclass=StructMetaPropGroup):
356     __slots__ = ()
357
358     def align_orientation(self, other):
359         """
360         Align this bone to another by moving its tail and settings its roll
361         the length of the other bone is not used.
362         """
363         vec = other.vector.normalized() * self.length
364         self.tail = self.head + vec
365         self.roll = other.roll
366
367     def transform(self, matrix, scale=True, roll=True):
368         """
369         Transform the the bones head, tail, roll and envelope
370         (when the matrix has a scale component).
371
372         :arg matrix: 3x3 or 4x4 transformation matrix.
373         :type matrix: :class:`mathutils.Matrix`
374         :arg scale: Scale the bone envelope by the matrix.
375         :type scale: bool
376         :arg roll:
377
378            Correct the roll to point in the same relative
379            direction to the head and tail.
380
381         :type roll: bool
382         """
383         from mathutils import Vector
384         z_vec = self.matrix.to_3x3() @ Vector((0.0, 0.0, 1.0))
385         self.tail = matrix @ self.tail
386         self.head = matrix @ self.head
387
388         if scale:
389             scalar = matrix.median_scale
390             self.head_radius *= scalar
391             self.tail_radius *= scalar
392
393         if roll:
394             self.align_roll(matrix @ z_vec)
395
396
397 def ord_ind(i1, i2):
398     if i1 < i2:
399         return i1, i2
400     return i2, i1
401
402
403 class Mesh(bpy_types.ID):
404     __slots__ = ()
405
406     def from_pydata(self, vertices, edges, faces):
407         """
408         Make a mesh from a list of vertices/edges/faces
409         Until we have a nicer way to make geometry, use this.
410
411         :arg vertices:
412
413            float triplets each representing (X, Y, Z)
414            eg: [(0.0, 1.0, 0.5), ...].
415
416         :type vertices: iterable object
417         :arg edges:
418
419            int pairs, each pair contains two indices to the
420            *vertices* argument. eg: [(1, 2), ...]
421
422         :type edges: iterable object
423         :arg faces:
424
425            iterator of faces, each faces contains three or more indices to
426            the *vertices* argument. eg: [(5, 6, 8, 9), (1, 2, 3), ...]
427
428         :type faces: iterable object
429
430         .. warning::
431
432            Invalid mesh data
433            *(out of range indices, edges with matching indices,
434            2 sided faces... etc)* are **not** prevented.
435            If the data used for mesh creation isn't known to be valid,
436            run :class:`Mesh.validate` after this function.
437         """
438         from itertools import chain, islice, accumulate
439
440         face_lengths = tuple(map(len, faces))
441
442         self.vertices.add(len(vertices))
443         self.edges.add(len(edges))
444         self.loops.add(sum(face_lengths))
445         self.polygons.add(len(faces))
446
447         self.vertices.foreach_set("co", tuple(chain.from_iterable(vertices)))
448         self.edges.foreach_set("vertices", tuple(chain.from_iterable(edges)))
449
450         vertex_indices = tuple(chain.from_iterable(faces))
451         loop_starts = tuple(islice(chain([0], accumulate(face_lengths)), len(faces)))
452
453         self.polygons.foreach_set("loop_total", face_lengths)
454         self.polygons.foreach_set("loop_start", loop_starts)
455         self.polygons.foreach_set("vertices", vertex_indices)
456
457         # if no edges - calculate them
458         if faces and (not edges):
459             self.update(calc_edges=True)
460         elif edges:
461             self.update(calc_edges_loose=True)
462
463     @property
464     def edge_keys(self):
465         return [ed.key for ed in self.edges]
466
467
468 class MeshEdge(StructRNA):
469     __slots__ = ()
470
471     @property
472     def key(self):
473         return ord_ind(*tuple(self.vertices))
474
475
476 class MeshLoopTriangle(StructRNA):
477     __slots__ = ()
478
479     @property
480     def center(self):
481         """The midpoint of the face."""
482         face_verts = self.vertices[:]
483         mesh_verts = self.id_data.vertices
484         return (mesh_verts[face_verts[0]].co +
485                 mesh_verts[face_verts[1]].co +
486                 mesh_verts[face_verts[2]].co
487                 ) / 3.0
488
489     @property
490     def edge_keys(self):
491         verts = self.vertices[:]
492         return (ord_ind(verts[0], verts[1]),
493                 ord_ind(verts[1], verts[2]),
494                 ord_ind(verts[2], verts[0]),
495                 )
496
497
498 class MeshPolygon(StructRNA):
499     __slots__ = ()
500
501     @property
502     def edge_keys(self):
503         verts = self.vertices[:]
504         vlen = len(self.vertices)
505         return [ord_ind(verts[i], verts[(i + 1) % vlen]) for i in range(vlen)]
506
507     @property
508     def loop_indices(self):
509         start = self.loop_start
510         end = start + self.loop_total
511         return range(start, end)
512
513
514 class Text(bpy_types.ID):
515     __slots__ = ()
516
517     def as_string(self):
518         """Return the text as a string."""
519         return "\n".join(line.body for line in self.lines)
520
521     def from_string(self, string):
522         """Replace text with this string."""
523         self.clear()
524         self.write(string)
525
526
527 class Sound(bpy_types.ID):
528     __slots__ = ()
529
530     @property
531     def factory(self):
532         """The aud.Factory object of the sound."""
533         import aud
534         return aud._sound_from_pointer(self.as_pointer())
535
536
537 class RNAMeta(type):
538     # TODO(campbell): move to C-API
539     @property
540     def is_registered(cls):
541         return "bl_rna" in cls.__dict__
542
543
544 class RNAMetaPropGroup(StructMetaPropGroup, RNAMeta):
545     pass
546
547
548 # Same as 'Operator'
549 # only without 'as_keywords'
550 class Gizmo(StructRNA):
551     __slots__ = ()
552
553     def __getattribute__(self, attr):
554         properties = StructRNA.path_resolve(self, "properties")
555         bl_rna = getattr(properties, "bl_rna", None)
556         if (bl_rna is not None) and (attr in bl_rna.properties):
557             return getattr(properties, attr)
558         return super().__getattribute__(attr)
559
560     def __setattr__(self, attr, value):
561         properties = StructRNA.path_resolve(self, "properties")
562         bl_rna = getattr(properties, "bl_rna", None)
563         if (bl_rna is not None) and (attr in bl_rna.properties):
564             return setattr(properties, attr, value)
565         return super().__setattr__(attr, value)
566
567     def __delattr__(self, attr):
568         properties = StructRNA.path_resolve(self, "properties")
569         bl_rna = getattr(properties, "bl_rna", None)
570         if (bl_rna is not None) and (attr in bl_rna.properties):
571             return delattr(properties, attr)
572         return super().__delattr__(attr)
573
574     from _bpy import (
575         _rna_gizmo_target_set_handler as target_set_handler,
576         _rna_gizmo_target_get_value as target_get_value,
577         _rna_gizmo_target_set_value as target_set_value,
578         _rna_gizmo_target_get_range as target_get_range,
579     )
580
581     # Convenience wrappers around private `_gpu` module.
582     def draw_custom_shape(self, shape, *, matrix=None, select_id=None):
583         """
584         Draw a shape created form :class:`bpy.types.Gizmo.draw_custom_shape`.
585
586         :arg shape: The cached shape to draw.
587         :type shape: Undefined.
588         :arg matrix: 4x4 matrix, when not given
589            :class:`bpy.types.Gizmo.matrix_world` is used.
590         :type matrix: :class:`mathutils.Matrix`
591         :arg select_id: The selection id.
592            Only use when drawing within :class:`bpy.types.Gizmo.draw_select`.
593         :type select_it: int
594         """
595         import gpu
596
597         if matrix is None:
598             matrix = self.matrix_world
599
600         batch, shader = shape
601         shader.bind()
602
603         if select_id is not None:
604             gpu.select.load_id(select_id)
605         else:
606             if self.is_highlight:
607                 color = (*self.color_highlight, self.alpha_highlight)
608             else:
609                 color = (*self.color, self.alpha)
610             shader.uniform_float("color", color)
611
612         with gpu.matrix.push_pop():
613             gpu.matrix.multiply_matrix(matrix)
614             batch.draw()
615
616     @staticmethod
617     def new_custom_shape(type, verts):
618         """
619         Create a new shape that can be passed to :class:`bpy.types.Gizmo.draw_custom_shape`.
620
621         :arg type: The type of shape to create in (POINTS, LINES, TRIS, LINE_STRIP).
622         :type type: string
623         :arg verts: Coordinates.
624         :type verts: sequence of of 2D or 3D coordinates.
625         :arg display_name: Optional callback that takes the full path, returns the name to display.
626         :type display_name: Callable that takes a string and returns a string.
627         :return: The newly created shape.
628         :rtype: Undefined (it may change).
629         """
630         import gpu
631         from gpu.types import (
632             GPUBatch,
633             GPUVertBuf,
634             GPUVertFormat,
635         )
636         dims = len(verts[0])
637         if dims not in {2, 3}:
638             raise ValueError("Expected 2D or 3D vertex")
639         fmt = GPUVertFormat()
640         pos_id = fmt.attr_add(id="pos", comp_type='F32', len=dims, fetch_mode='FLOAT')
641         vbo = GPUVertBuf(len=len(verts), format=fmt)
642         vbo.attr_fill(id=pos_id, data=verts)
643         batch = GPUBatch(type=type, buf=vbo)
644         shader = gpu.shader.from_builtin('3D_UNIFORM_COLOR' if dims == 3 else '2D_UNIFORM_COLOR')
645         batch.program_set(shader)
646         return (batch, shader)
647
648
649 # Only defined so operators members can be used by accessing self.order
650 # with doc generation 'self.properties.bl_rna.properties' can fail
651 class Operator(StructRNA, metaclass=RNAMeta):
652     __slots__ = ()
653
654     def __getattribute__(self, attr):
655         properties = StructRNA.path_resolve(self, "properties")
656         bl_rna = getattr(properties, "bl_rna", None)
657         if (bl_rna is not None) and (attr in bl_rna.properties):
658             return getattr(properties, attr)
659         return super().__getattribute__(attr)
660
661     def __setattr__(self, attr, value):
662         properties = StructRNA.path_resolve(self, "properties")
663         bl_rna = getattr(properties, "bl_rna", None)
664         if (bl_rna is not None) and (attr in bl_rna.properties):
665             return setattr(properties, attr, value)
666         return super().__setattr__(attr, value)
667
668     def __delattr__(self, attr):
669         properties = StructRNA.path_resolve(self, "properties")
670         bl_rna = getattr(properties, "bl_rna", None)
671         if (bl_rna is not None) and (attr in bl_rna.properties):
672             return delattr(properties, attr)
673         return super().__delattr__(attr)
674
675     def as_keywords(self, ignore=()):
676         """Return a copy of the properties as a dictionary"""
677         ignore = ignore + ("rna_type",)
678         return {attr: getattr(self, attr)
679                 for attr in self.properties.rna_type.properties.keys()
680                 if attr not in ignore}
681
682
683 class Macro(StructRNA):
684     # bpy_types is imported before ops is defined
685     # so we have to do a local import on each run
686     __slots__ = ()
687
688     @classmethod
689     def define(self, opname):
690         from _bpy import ops
691         return ops.macro_define(self, opname)
692
693
694 class PropertyGroup(StructRNA, metaclass=RNAMetaPropGroup):
695     __slots__ = ()
696
697
698 class RenderEngine(StructRNA, metaclass=RNAMeta):
699     __slots__ = ()
700
701
702 class KeyingSetInfo(StructRNA, metaclass=RNAMeta):
703     __slots__ = ()
704
705
706 class AddonPreferences(StructRNA, metaclass=RNAMeta):
707     __slots__ = ()
708
709
710 class _GenericUI:
711     __slots__ = ()
712
713     @classmethod
714     def _dyn_ui_initialize(cls):
715         draw_funcs = getattr(cls.draw, "_draw_funcs", None)
716
717         if draw_funcs is None:
718
719             def draw_ls(self, context):
720                 # ensure menus always get default context
721                 operator_context_default = self.layout.operator_context
722
723                 # Support filtering out by owner
724                 workspace = context.workspace
725                 if workspace.use_filter_by_owner:
726                     owner_names = {owner_id.name for owner_id in workspace.owner_ids}
727                 else:
728                     owner_names = None
729
730                 for func in draw_ls._draw_funcs:
731
732                     # Begin 'owner_id' filter.
733                     if owner_names is not None:
734                         owner_id = getattr(func, "_owner", None)
735                         if owner_id is not None:
736                             if func._owner not in owner_names:
737                                 continue
738                     # End 'owner_id' filter.
739
740                     # so bad menu functions don't stop
741                     # the entire menu from drawing
742                     try:
743                         func(self, context)
744                     except:
745                         import traceback
746                         traceback.print_exc()
747
748                     self.layout.operator_context = operator_context_default
749
750             draw_funcs = draw_ls._draw_funcs = [cls.draw]
751             cls.draw = draw_ls
752
753         return draw_funcs
754
755     @staticmethod
756     def _dyn_owner_apply(draw_func):
757         from _bpy import _bl_owner_id_get
758         owner_id = _bl_owner_id_get()
759         if owner_id is not None:
760             draw_func._owner = owner_id
761
762     @classmethod
763     def is_extended(cls):
764         return bool(getattr(cls.draw, "_draw_funcs", None))
765
766     @classmethod
767     def append(cls, draw_func):
768         """
769         Append a draw function to this menu,
770         takes the same arguments as the menus draw function
771         """
772         draw_funcs = cls._dyn_ui_initialize()
773         cls._dyn_owner_apply(draw_func)
774         draw_funcs.append(draw_func)
775
776     @classmethod
777     def prepend(cls, draw_func):
778         """
779         Prepend a draw function to this menu, takes the same arguments as
780         the menus draw function
781         """
782         draw_funcs = cls._dyn_ui_initialize()
783         cls._dyn_owner_apply(draw_func)
784         draw_funcs.insert(0, draw_func)
785
786     @classmethod
787     def remove(cls, draw_func):
788         """Remove a draw function that has been added to this menu"""
789         draw_funcs = cls._dyn_ui_initialize()
790         try:
791             draw_funcs.remove(draw_func)
792         except ValueError:
793             pass
794
795
796 class Panel(StructRNA, _GenericUI, metaclass=RNAMeta):
797     __slots__ = ()
798
799
800 class UIList(StructRNA, _GenericUI, metaclass=RNAMeta):
801     __slots__ = ()
802
803
804 class Header(StructRNA, _GenericUI, metaclass=RNAMeta):
805     __slots__ = ()
806
807
808 class Menu(StructRNA, _GenericUI, metaclass=RNAMeta):
809     __slots__ = ()
810
811     def path_menu(self, searchpaths, operator, *,
812                   props_default=None, prop_filepath="filepath",
813                   filter_ext=None, filter_path=None, display_name=None,
814                   add_operator=None):
815         """
816         Populate a menu from a list of paths.
817
818         :arg searchpaths: Paths to scan.
819         :type searchpaths: sequence of strings.
820         :arg operator: The operator id to use with each file.
821         :type operator: string
822         :arg prop_filepath: Optional operator filepath property (defaults to "filepath").
823         :type prop_filepath: string
824         :arg props_default: Properties to assign to each operator.
825         :type props_default: dict
826         :arg filter_ext: Optional callback that takes the file extensions.
827
828            Returning false excludes the file from the list.
829
830         :type filter_ext: Callable that takes a string and returns a bool.
831         :arg display_name: Optional callback that takes the full path, returns the name to display.
832         :type display_name: Callable that takes a string and returns a string.
833         """
834
835         layout = self.layout
836
837         import os
838         import bpy.utils
839
840         layout = self.layout
841
842         if not searchpaths:
843             layout.label(text="* Missing Paths *")
844
845         # collect paths
846         files = []
847         for directory in searchpaths:
848             files.extend(
849                 [(f, os.path.join(directory, f))
850                  for f in os.listdir(directory)
851                  if (not f.startswith("."))
852                  if ((filter_ext is None) or
853                      (filter_ext(os.path.splitext(f)[1])))
854                  if ((filter_path is None) or
855                      (filter_path(f)))
856                  ])
857
858         files.sort()
859
860         col = layout.column(align=True)
861
862         for f, filepath in files:
863             # Intentionally pass the full path to 'display_name' callback,
864             # since the callback may want to use part a directory in the name.
865             row = col.row(align=True)
866             name = display_name(filepath) if display_name else bpy.path.display_name(f)
867             props = row.operator(
868                 operator,
869                 text=name,
870                 translate=False,
871             )
872
873             if props_default is not None:
874                 for attr, value in props_default.items():
875                     setattr(props, attr, value)
876
877             setattr(props, prop_filepath, filepath)
878             if operator == "script.execute_preset":
879                 props.menu_idname = self.bl_idname
880
881             if add_operator:
882                 props = row.operator(add_operator, text="", icon='REMOVE')
883                 props.name = name
884                 props.remove_name = True
885
886         if add_operator:
887             wm = bpy.data.window_managers[0]
888
889             layout.separator()
890             row = layout.row()
891
892             sub = row.row()
893             sub.emboss = 'NORMAL'
894             sub.prop(wm, "preset_name", text="")
895
896             props = row.operator(add_operator, text="", icon='ADD')
897             props.name = wm.preset_name
898
899     def draw_preset(self, context):
900         """
901         Define these on the subclass:
902         - preset_operator (string)
903         - preset_subdir (string)
904
905         Optionally:
906         - preset_add_operator (string)
907         - preset_extensions (set of strings)
908         - preset_operator_defaults (dict of keyword args)
909         """
910         import bpy
911         ext_valid = getattr(self, "preset_extensions", {".py", ".xml"})
912         props_default = getattr(self, "preset_operator_defaults", None)
913         add_operator = getattr(self, "preset_add_operator", None)
914         self.path_menu(bpy.utils.preset_paths(self.preset_subdir),
915                        self.preset_operator,
916                        props_default=props_default,
917                        filter_ext=lambda ext: ext.lower() in ext_valid,
918                        add_operator=add_operator)
919
920     @classmethod
921     def draw_collapsible(cls, context, layout):
922         # helper function for (optionally) collapsed header menus
923         # only usable within headers
924         if context.area.show_menus:
925             # Align menus to space them closely.
926             layout.row(align=True).menu_contents(cls.__name__)
927         else:
928             layout.menu(cls.__name__, icon='COLLAPSEMENU')
929
930
931 class NodeTree(bpy_types.ID, metaclass=RNAMetaPropGroup):
932     __slots__ = ()
933
934
935 class Node(StructRNA, metaclass=RNAMetaPropGroup):
936     __slots__ = ()
937
938     @classmethod
939     def poll(cls, ntree):
940         return True
941
942
943 class NodeInternal(Node):
944     __slots__ = ()
945
946
947 class NodeSocket(StructRNA, metaclass=RNAMetaPropGroup):
948     __slots__ = ()
949
950     @property
951     def links(self):
952         """List of node links from or to this socket. Warning: takes O(len(nodetree.links)) time."""
953         return tuple(link for link in self.id_data.links
954                      if (link.from_socket == self or
955                          link.to_socket == self))
956
957
958 class NodeSocketInterface(StructRNA, metaclass=RNAMetaPropGroup):
959     __slots__ = ()
960
961
962 # These are intermediate subclasses, need a bpy type too
963 class CompositorNode(NodeInternal):
964     __slots__ = ()
965
966     @classmethod
967     def poll(cls, ntree):
968         return ntree.bl_idname == 'CompositorNodeTree'
969
970     def update(self):
971         self.tag_need_exec()
972
973
974 class ShaderNode(NodeInternal):
975     __slots__ = ()
976
977     @classmethod
978     def poll(cls, ntree):
979         return ntree.bl_idname == 'ShaderNodeTree'
980
981
982 class TextureNode(NodeInternal):
983     __slots__ = ()
984
985     @classmethod
986     def poll(cls, ntree):
987         return ntree.bl_idname == 'TextureNodeTree'