initial import of ussd-pad
[ussd-widget] / ussd-pad / src / opt / ussd-pad / ui / layout / Arrangement.py
1 from ui.Widget import Widget
2 from utils.MiniXML import MiniXML
3
4 _PX  = 0
5 _PCT = 1
6 _LIKE_X1 = 2
7 _LIKE_Y1 = 3
8 _LIKE_X2 = 4
9 _LIKE_Y2 = 5
10
11
12 class Arrangement(Widget):
13     """
14     Layouter for complex arrangements of widgets.
15     @since: 2009.10.29
16     """
17
18     EVENT_RESIZED = "event-resized"
19     
20
21     def __init__(self):
22     
23         # table: name -> widget
24         self.__names = {}
25
26         # table: widget -> (x1, y1, x2, y2)
27         self.__constraints = {}
28         
29         # cache of arrangements, table: widget -> (x1, y1, x2, y2)
30         self.__cache = {}
31     
32         Widget.__init__(self)
33         
34         
35     def connect_resized(self, cb, *args):
36     
37         self._connect(self.EVENT_RESIZED, cb, *args)
38
39         
40     @staticmethod
41     def px(v):
42     
43         return (_PX, v)
44
45
46     @staticmethod
47     def pct(v):
48     
49         return (_PCT, v)
50         
51         
52     @staticmethod
53     def like_x1(child):
54     
55         return (_LIKE_X1, child)
56
57
58     @staticmethod
59     def like_y1(child):
60     
61         return (_LIKE_Y1, child)
62
63
64     @staticmethod
65     def like_x2(child):
66     
67         return (_LIKE_X2, child)
68
69
70     @staticmethod
71     def like_y2(child):
72     
73         return (_LIKE_Y2, child)
74
75         
76
77     def __resolve_child(self, child):
78     
79         if (not child in self.__constraints):
80             return (0, 0, 10, 10)
81         
82         x1, y1, x2, y2 = self.__constraints[child]
83         w, h = self.get_size()
84         
85         if (child in self.__cache):
86             real_x1, real_y1, real_x2, real_y2 = self.__cache[child]
87         else:
88             real_x1 = self.__resolve(x1, w)
89             real_y1 = self.__resolve(y1, h)
90             real_x2 = self.__resolve(x2, w)
91             real_y2 = self.__resolve(y2, h)
92             self.__cache[child] = (real_x1, real_y1, real_x2, real_y2)
93
94         return (real_x1, real_y1, real_x2, real_y2)
95
96         
97     def __resolve(self, v, full_width):
98     
99         vtype, value = v
100         if (vtype == _PX):
101             if (value >= 0):
102                 return value
103             else:
104                 return full_width + value 
105
106         elif (vtype == _PCT):
107             if (value >= 0):
108                 return int(full_width * (value / 100.0))
109             else:
110                 return full_width + int(full_width * (value / 100.0)) 
111         
112         elif (vtype == _LIKE_X1):
113             x1, y1, x2, y2 = self.__resolve_child(value)
114             return x1
115
116         elif (vtype == _LIKE_Y1):
117             x1, y1, x2, y2 = self.__resolve_child(value)
118             return y1
119
120         elif (vtype == _LIKE_X2):
121             x1, y1, x2, y2 = self.__resolve_child(value)
122             return x2
123
124         elif (vtype == _LIKE_Y2):
125             x1, y1, x2, y2 = self.__resolve_child(value)
126             return y2
127
128         
129     def set_size(self, w, h):
130     
131         prev_w, prev_h = self.get_size()
132         Widget.set_size(self, w, h)
133         if ((w, h) != (prev_w, prev_h)):
134             self.__cache.clear()
135             self.emit_event(self.EVENT_RESIZED)
136
137
138     def add(self, child, name = ""):
139         """
140         Adds a child widget. A unique name must be given when using arrangement
141         definitions in XML format.
142         
143         @param child: child widget
144         @param name: name string
145         """
146         
147         if (name):
148             self.__names[name] = child
149         Widget.add(self, child)
150
151         
152     def place(self, child, x1, y1, x2, y2):
153         """
154         Changes the placement of the given child widget.
155         
156         @param child: child widget
157         @param x1: x-coordinate of top-left corner
158         @param y1: y-coordinate of top-left corner
159         @param x2: x-coordinate of bottom-right corner
160         @param y2: y-coordinate of botton-right corner
161         """
162     
163         self.__constraints[child] = (x1, y1, x2, y2)
164         
165         if (child in self.__cache):
166             del self.__cache[child]
167
168
169     def set_xml(self, xml):
170         """
171         Loads an arrangement from an XML arrangement definition.
172         
173         @param xml: XML string containing the arrangement
174         """
175     
176         dom = MiniXML(xml).get_dom()
177         self.__parse_node(dom, [], [])
178
179         
180     def __parse_node(self, node, required_visibles, required_invisibles):
181     
182         name = node.get_name()
183         if (name == "if-visible"):
184             child = self.__names[node.get_attr("name")]
185             required_visibles.append(child)
186         elif (name == "if-invisible"):
187             child = self.__names[node.get_attr("name")]
188             required_invisibles.append(child)
189         elif (name == "widget"):
190             child = self.__names[node.get_attr("name")]
191             x1 = self.__parse_value(node.get_attr("x1"))
192             y1 = self.__parse_value(node.get_attr("y1"))
193             x2 = self.__parse_value(node.get_attr("x2"))
194             y2 = self.__parse_value(node.get_attr("y2"))
195             
196             if (self.__check_visibles(required_visibles, required_invisibles)):
197                 self.place(child, x1, y1, x2, y2)
198         #end if
199         
200         for c in node.get_children():
201             self.__parse_node(c, required_visibles[:], required_invisibles[:])
202         #end for
203
204
205     def __parse_value(self, v):
206     
207         if (v[-1] == "%"):
208             return self.pct(int(v[:-1]))
209         else:
210             return self.px(int(v))
211
212
213     def __check_visibles(self, required_visibles, required_invisibles):
214     
215         for v in required_visibles:
216             if (not v.is_visible()):
217                 return False
218         #end for
219         
220         for v in required_invisibles:
221             if (v.is_visible()):
222                 return False
223         #end for
224         
225         return True
226         
227
228     def render_this(self):
229
230         for child in self.get_children():
231             x1, y1, x2, y2 = self.__resolve_child(child)
232             child.set_geometry(x1, y1, x2 - x1, y2 - y1)
233         #end for
234