some more work on plugin manager
[enigma2.git] / lib / python / Components / Element.py
1 from Tools.CList import CList
2
3 # down                       up
4 # Render Converter Converter Source
5
6 # a bidirectional connection
7
8 def cached(f):
9         name = f.__name__
10         def wrapper(self):
11                 cache = self.cache
12                 if cache is None:
13                         return f(self)
14                 if name not in cache:
15                         cache[name] = (True, f(self))
16                 return cache[name][1]
17         return wrapper
18
19 class ElementError(Exception):
20     def __init__(self, message):
21         self.msg = message
22
23     def __str__(self):
24         return self.msg
25
26 class Element(object):
27         CHANGED_DEFAULT = 0   # initial "pull" state
28         CHANGED_ALL = 1       # really everything changed
29         CHANGED_CLEAR = 2     # we're expecting a real update soon. don't bother polling NOW, but clear data.
30         CHANGED_SPECIFIC = 3  # second tuple will specify what exactly changed
31         CHANGED_POLL = 4      # a timer expired
32
33         SINGLE_SOURCE = True
34
35         def __init__(self):
36                 self.downstream_elements = CList()
37                 self.master = None
38                 self.sources = [ ]
39                 self.source = None
40                 self.__suspended = True
41                 self.cache = None
42
43         def connectDownstream(self, downstream):
44                 self.downstream_elements.append(downstream)
45                 if self.master is None:
46                         self.master = downstream
47         
48         def connectUpstream(self, upstream):
49                 assert not self.SINGLE_SOURCE or self.source is None
50                 self.sources.append(upstream)
51                 # self.source always refers to the last recent source added.
52                 self.source = upstream
53                 self.changed((self.CHANGED_DEFAULT,))
54         
55         def connect(self, upstream):
56                 self.connectUpstream(upstream)
57                 upstream.connectDownstream(self)
58
59         # we disconnect from down to up
60         def disconnectAll(self):
61                 # we should not disconnect from upstream if
62                 # there are still elements depending on us.
63                 assert len(self.downstream_elements) == 0, "there are still downstream elements left"
64
65                 # Sources don't have a source themselves. don't do anything here.
66                 for s in self.sources:
67                         s.disconnectDownstream(self)
68
69                 if self.source:
70                         # sources are owned by the Screen, so don't destroy them here.
71                         self.destroy()
72                 self.source = None
73                 self.sources = [ ]
74
75         def disconnectDownstream(self, downstream):
76                 self.downstream_elements.remove(downstream)
77                 if self.master == downstream:
78                         self.master = None
79                 
80                 if len(self.downstream_elements) == 0:
81                         self.disconnectAll()
82
83         # default action: push downstream
84         def changed(self, *args, **kwargs):
85                 self.cache = { }
86                 self.downstream_elements.changed(*args, **kwargs)
87                 self.cache = None
88
89         def setSuspend(self, suspended):
90                 changed = self.__suspended != suspended
91                 if not self.__suspended and suspended:
92                         self.doSuspend(1)
93                 elif self.__suspended and not suspended:
94                         self.doSuspend(0)
95                         
96                 self.__suspended = suspended
97                 if changed:
98                         for s in self.sources:
99                                 s.checkSuspend()
100         
101         suspended = property(lambda self: self.__suspended, setSuspend)
102         
103         def checkSuspend(self):
104                 self.suspended = reduce(lambda x, y: x and y.__suspended, self.downstream_elements, True)
105
106         def doSuspend(self, suspend):
107                 pass
108
109         def destroy(self):
110                 pass