8d124f222a1eccaf6036af2185d5ee951655db10
[blender-staging.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 import bpy
20
21 def collect_baseclasses(_class, bases):
22
23     if _class is type or _class is object:
24         return bases
25
26     bases.append(_class)
27     for _superclass in _class.__bases__:
28         collect_baseclasses(_superclass, bases)
29
30     return bases
31
32 def collect_subclasses(_class, subs):
33
34     if _class is type or _class is object:
35         return subs
36
37     subs.append(_class)
38     for _subclass in _class.__subclasses__():
39         collect_subclasses(_subclass, subs)
40
41     return subs
42
43 class DynMenu(bpy.types.Menu):
44
45     def draw(self, context):
46         '''
47         This is a draw function that is used to call all subclasses draw functions
48         starting from the registered classes draw function and working down.
49
50         DynMenu.setup() must be called first.
51
52         Sort/group classes could be nice
53         '''
54
55         subclass_ls = []
56         collect_subclasses(self.__class__, subclass_ls)
57         # print(subclass_ls)
58
59         for subclass in subclass_ls:
60             # print("drawwing", subclass) # , dir(subclass))
61             subclass.internal_draw(self, context)
62             # print("subclass.internal_draw", subclass.internal_draw)
63
64 def setup(menu_class):
65     '''
66     Setup subclasses (not needed when self.add() is used)
67     '''
68     bases = collect_baseclasses(menu_class, [])
69
70     # Incase 'DynMenu' isnt last
71     while bases[-1] is not DynMenu:
72         bases.pop()
73     bases.pop() # remove 'DynMenu'
74
75     root_class = bases[-1] # this is the registered class
76
77     for subclass in collect_subclasses(root_class, []):
78         #print(subclass)
79
80         draw = getattr(subclass, 'draw', None)
81         if draw and not hasattr(subclass, 'internal_draw'):
82             # print("replace", subclass, draw)
83             try:
84                 del subclass.draw
85             except:
86                 pass
87             subclass.internal_draw = draw
88
89     root_class.draw = DynMenu.draw
90
91 def add(menu_class, func):
92     '''
93     Add a single function directly without having to make a class
94
95     important that the returned value should be stored in the module that called it.
96     '''
97
98     newclass = type('<menuclass>', (menu_class,), {})
99     newclass.internal_draw = func
100     setup(menu_class)
101     return newclass
102
103 '''
104 # so we dont need to import this module
105 DynMenu.setup = setup
106 DynMenu.add = add
107
108 # Only so we can access as bpy.types.
109 # dont ever use this directly!
110 bpy.types.register(DynMenu)
111 '''
112
113