comparison browser_side/xmlui.py @ 143:03c22ddd7c94

browser side: XMLUI implentation
author Goffi <goffi@goffi.org>
date Sun, 09 Dec 2012 23:28:49 +0100
parents
children 9763dec220ed
comparison
equal deleted inserted replaced
142:f6aeeb753c06 143:03c22ddd7c94
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3
4 """
5 Libervia: a Salut à Toi frontend
6 Copyright (C) 2011, 2012 Jérôme Poisson <goffi@goffi.org>
7
8 This program is free software: you can redistribute it and/or modify
9 it under the terms of the GNU Affero General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU Affero General Public License for more details.
17
18 You should have received a copy of the GNU Affero General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 """
21
22 from pyjamas.ui.VerticalPanel import VerticalPanel
23 from pyjamas.ui.HorizontalPanel import HorizontalPanel
24 from pyjamas.ui.CellPanel import CellPanel
25 from pyjamas.ui.TabPanel import TabPanel
26 from pyjamas.ui.Grid import Grid
27 from pyjamas.ui.Label import Label
28 from pyjamas.ui.TextBoxBase import TextBoxBase
29 from pyjamas.ui.TextBox import TextBox
30 from pyjamas.ui.PasswordTextBox import PasswordTextBox
31 from pyjamas.ui.TextArea import TextArea
32 from pyjamas.ui.CheckBox import CheckBox
33 from pyjamas.ui.ListBox import ListBox
34 from pyjamas.ui.Button import Button
35 from nativedom import NativeDOM
36
37
38 class InvalidXMLUI(Exception):
39 pass
40
41 class Pairs(Grid):
42
43 def __init__(self):
44 Grid.__init__(self, 0, 0)
45 self.row = 0
46 self.col = 0
47
48 def append(self, widget):
49 if self.col == 0:
50 self.resize(self.row+1, 2)
51 self.setWidget(self.row, self.col, widget)
52 self.col += 1
53 if self.col == 2:
54 self.row +=1
55 self.col = 0
56
57 class XMLUI(VerticalPanel):
58
59 def __init__(self, host, xml_data, title = None, options = None, misc = None, close_cb = None):
60 print "XMLUI init"
61 VerticalPanel.__init__(self)
62 self.dom = NativeDOM()
63 self.host = host
64 self.title = title
65 self.options = options or []
66 self.misc = misc or {}
67 self.close_cb = close_cb
68 self.__dest = "window"
69 self.ctrl_list = {} # usefull to access ctrl
70 self.constructUI(xml_data)
71 self.setSize('100%', '100%')
72
73 def setCloseCb(self, close_cb):
74 self.close_cb = close_cb
75
76 def close(self):
77 if self.close_cb:
78 self.close_cb()
79 else:
80 print "WARNING: no close method defined"
81
82 def __parseElems(self, node, parent):
83 """Parse elements inside a <layout> tags, and add them to the parent"""
84 for elem in node.childNodes:
85 if elem.nodeName != "elem":
86 raise Exception("Unmanaged tag [%s]" % (elem.nodeName))
87 node_id = elem.getAttribute("node_id")
88 name = elem.getAttribute("name")
89 node_type = elem.getAttribute("type")
90 value = elem.getAttribute("value") if elem.hasAttribute('value') else u''
91 if node_type=="empty":
92 ctrl = Label('')
93 elif node_type=="text":
94 try:
95 value = elem.childNodes[0].wholeText
96 except IndexError:
97 print ("WARNING: text node has no child !")
98 ctrl = Label(value)
99 elif node_type=="label":
100 ctrl = Label(value+": ")
101 elif node_type=="string":
102 ctrl = TextBox()
103 ctrl.setText(value)
104 self.ctrl_list[name] = ({'node_type':node_type, 'control':ctrl})
105 elif node_type=="password":
106 ctrl = PasswordTextBox()
107 ctrl.setText(value)
108 self.ctrl_list[name] = ({'node_type':node_type, 'control':ctrl})
109 elif node_type=="textbox":
110 ctrl = TextArea()
111 ctrl.setText(value)
112 self.ctrl_list[name] = ({'node_type':node_type, 'control':ctrl})
113 elif node_type=="bool":
114 ctrl = CheckBox()
115 ctrl.setChecked(value=="true")
116 self.ctrl_list[name] = ({'node_type':node_type, 'control':ctrl})
117 elif node_type=="list":
118 ctrl = ListBox()
119 ctrl.setMultipleSelect(elem.getAttribute("multi")=='yes')
120 for option in elem.getElementsByTagName("option"):
121 ctrl.addItem(option.getAttribute("value"))
122 self.ctrl_list[name] = ({'node_type':node_type, 'control':ctrl})
123 elif node_type=="button":
124 callback_id = elem.getAttribute("callback_id")
125 ctrl = Button(value, self.onButtonPress)
126 ctrl.param_id = (callback_id,[field.getAttribute('name') for field in elem.getElementsByTagName("field_back")])
127 else:
128 print("FIXME FIXME FIXME: type [%s] is not implemented" % node_type) #FIXME !
129 raise NotImplementedError
130 if self.node_type == 'param':
131 if isinstance(ctrl,TextBoxBase):
132 ctrl.addChangeListener(self.onParamChange)
133 elif isinstance(ctrl, CheckBox):
134 ctrl.addClickListener(self.onParamChange)
135 ctrl._param_category = self._current_category
136 ctrl._param_name = name
137 parent.append(ctrl)
138
139 def __parseChilds(self, current, elem, wanted = ['layout'], data = None):
140 """Recursively parse childNodes of an elemen
141 @param current: widget container with 'append' method
142 @param elem: element from which childs will be parsed
143 @param wanted: list of tag names that can be present in the childs to be SàT XMLUI compliant"""
144 for node in elem.childNodes:
145 if wanted and not node.nodeName in wanted:
146 raise InvalidXMLUI("ERROR: unexpected nodeName")
147 if node.nodeName == "layout":
148 node_type = node.getAttribute('type')
149 if node_type == "tabs":
150 tab_cont = TabPanel()
151 tab_cont.setStyleName('liberviaTabPanel')
152 tab_cont.setHeight('100%')
153 self.__parseChilds(current, node, ['category'], tab_cont)
154 current.append(tab_cont)
155 if isinstance(current, CellPanel):
156 current.setCellHeight(tab_cont, '100%')
157 elif node_type == "vertical":
158 self.__parseElems(node, current)
159 elif node_type == "pairs":
160 pairs = Pairs()
161 self.__parseElems(node, pairs)
162 current.append(pairs)
163 else:
164 print("WARNING: Unknown layout [%s], using default one" % (node_type,))
165 self.__parseElems(node, current)
166 elif node.nodeName == "category":
167 name = node.getAttribute('name')
168 label = node.getAttribute('label')
169 if not name or not isinstance(data,TabPanel):
170 raise InvalidXMLUI
171 if self.node_type == 'param':
172 self._current_category = name #XXX: awful hack because params need category and we don't keep parent
173 tab_cont = data
174 tab_body = VerticalPanel()
175 tab_cont.add(tab_body, label or name)
176 self.__parseChilds(tab_body, node, ['layout'])
177 else:
178 message=_("Unknown tag")
179 raise NotImplementedError(message)
180
181 def constructUI(self, xml_data):
182
183 cat_dom = self.dom.parseString(xml_data)
184
185 top=cat_dom.documentElement
186 self.node_type = top.getAttribute("type")
187 self.title = top.getAttribute("title") or self.title
188 if top.nodeName != "sat_xmlui" or not self.node_type in ['form', 'param', 'window']:
189 raise InvalidXMLUI
190
191 if self.node_type == 'param':
192 self.param_changed = set()
193
194 self.__parseChilds(self, cat_dom.documentElement)
195
196 if self.node_type == 'form':
197 hpanel = HorizontalPanel()
198 hpanel.add(Button('Submit',self.onFormSubmitted))
199 if not 'NO_CANCEL' in self.options:
200 hpanel.add(Button('Cancel',self.onFormCancelled))
201 self.add(hpanel)
202 elif self.node_type == 'param':
203 assert(isinstance(self.children[0],TabPanel))
204 hpanel = HorizontalPanel()
205 hpanel.add(Button('Cancel', lambda ignore: self.close()))
206 hpanel.add(Button('Save', self.onSaveParams))
207 self.add(hpanel)
208
209 ##EVENTS##
210
211 def onButtonPress(self, button):
212 print "onButtonPress (%s)" % (button,)
213 callback_id, fields = button.param_id
214 data = {"callback_id":callback_id}
215 for field in fields:
216 ctrl = self.ctrl_list[field]
217 if isinstance(ctrl['control'],ListBox):
218 data[field] = '\t'.join(ctrl['control'].getSelectedValues())
219 elif isinstance(ctrl['control'],CheckBox):
220 data[field] = "true" if ctrl['control'].isChecked() else "false"
221 else:
222 data[field] = ctrl['control'].getText()
223
224 self.host.bridge.call('launchAction', None, "button", data)
225 self.host.current_action_ids.add(id)
226
227 def onParamChange(self, widget):
228 """Called when type is param and a widget to save is modified"""
229 assert(self.node_type == "param")
230 print "onParamChange:", widget
231 self.param_changed.add(widget)
232
233 def onFormSubmitted(self, button):
234 print "onFormSubmitted"
235 # FIXME: untested
236 print "FIXME FIXME FIXME: Form submitting not managed yet"
237 data = []
238 for ctrl_name in self.ctrl_list:
239 ctrl = self.ctrl_list[ctrl_name]
240 if isinstance(ctrl['control'], ListBox):
241 data.append((ctrl_name, ctrl['control'].getValue()))
242 elif isinstance(ctrl['control'], CheckBox):
243 data.append((ctrl_name, "true" if ctrl['control'].isChecked() else "false"))
244 else:
245 data.append((ctrl_name, ctrl['control'].getText()))
246 if 'action_back' in self.misc: #FIXME FIXME FIXME: WTF ! Must be cleaned
247 raise NotImplementedError
248 elif 'callback' in self.misc:
249 self.misc['callback'](data)
250 else:
251 print ("WARNING: The form data is not sent back, the type is not managed properly")
252
253 self.close()
254
255 def onFormCancelled(self, button):
256 self.close()
257
258 def onSaveParams(self, button):
259 print "onSaveParams"
260 for ctrl in self.param_changed:
261 if isinstance(ctrl, CheckBox):
262 value = "true" if ctrl.isChecked() else "false"
263 else:
264 value = ctrl.getText()
265 self.host.bridge.call('setParam', None, ctrl._param_name, value, ctrl._param_category)
266 self.close()