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