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