Sculpt Branch:
[blender.git] / release / scripts / modules / dynamic_menu.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8 compliant>
20
21 import bpy
22
23
24 def collect_baseclasses(_class, bases):
25
26     if _class is type or _class is object:
27         return bases
28
29     bases.append(_class)
30     for _superclass in _class.__bases__:
31         collect_baseclasses(_superclass, bases)
32
33     return bases
34
35
36 def collect_subclasses(_class, subs):
37
38     if _class is type or _class is object:
39         return subs
40
41     subs.append(_class)
42     for _subclass in _class.__subclasses__():
43         collect_subclasses(_subclass, subs)
44
45     return subs
46
47
48 class DynMenu(bpy.types.Menu):
49
50     def draw(self, context):
51         '''
52         This is a draw function that is used to call all subclasses draw functions
53         starting from the registered classes draw function and working down.
54
55         DynMenu.setup() must be called first.
56
57         Sort/group classes could be nice
58         '''
59
60         subclass_ls = []
61         collect_subclasses(self.__class__, subclass_ls)
62         # print(subclass_ls)
63
64         for subclass in subclass_ls:
65             # print("drawwing", subclass) # , dir(subclass))
66             subclass.internal_draw(self, context)
67             # print("subclass.internal_draw", subclass.internal_draw)
68
69
70 def setup(menu_class):
71     '''
72     Setup subclasses (not needed when self.add() is used)
73     '''
74     bases = collect_baseclasses(menu_class, [])
75
76     # Incase 'DynMenu' isnt last
77     while bases[-1] is not DynMenu:
78         bases.pop()
79     bases.pop() # remove 'DynMenu'
80
81     root_class = bases[-1] # this is the registered class
82
83     for subclass in collect_subclasses(root_class, []):
84         #print(subclass)
85
86         draw = getattr(subclass, 'draw', None)
87         if draw and not hasattr(subclass, 'internal_draw'):
88             # print("replace", subclass, draw)
89             try:
90                 del subclass.draw
91             except:
92                 pass
93             subclass.internal_draw = draw
94
95     root_class.draw = DynMenu.draw
96
97
98 def add(menu_class, func):
99     '''
100     Add a single function directly without having to make a class
101
102     important that the returned value should be stored in the module that called it.
103     '''
104
105     newclass = type('<menuclass>', (menu_class,), {})
106     newclass.internal_draw = func
107     setup(menu_class)
108     return newclass
109
110 '''
111 # so we dont need to import this module
112 DynMenu.setup = setup
113 DynMenu.add = add
114
115 # Only so we can access as bpy.types.
116 # dont ever use this directly!
117 bpy.types.register(DynMenu)
118 '''