Mercurial > libervia-web
annotate browser_side/xmlui.py @ 324:8131d0ccf21b
browser_side: prepare user input for microblog titles
author | souliane <souliane@mailoo.org> |
---|---|
date | Sat, 04 Jan 2014 02:38:23 +0100 |
parents | bfbd9d6eb901 |
children | d07b54fdc60a |
rev | line source |
---|---|
143 | 1 #!/usr/bin/python |
2 # -*- coding: utf-8 -*- | |
3 | |
4 """ | |
5 Libervia: a Salut à Toi frontend | |
165 | 6 Copyright (C) 2011, 2012, 2013 Jérôme Poisson <goffi@goffi.org> |
143 | 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 | |
321
bfbd9d6eb901
browser_side: info dialog to let the user know that a refresh is needed after GUI parameters have been changed
souliane <souliane@mailoo.org>
parents:
299
diff
changeset
|
22 from sat.core.i18n import _ |
143 | 23 from pyjamas.ui.VerticalPanel import VerticalPanel |
24 from pyjamas.ui.HorizontalPanel import HorizontalPanel | |
25 from pyjamas.ui.CellPanel import CellPanel | |
26 from pyjamas.ui.TabPanel import TabPanel | |
27 from pyjamas.ui.Grid import Grid | |
28 from pyjamas.ui.Label import Label | |
29 from pyjamas.ui.TextBoxBase import TextBoxBase | |
30 from pyjamas.ui.TextBox import TextBox | |
31 from pyjamas.ui.PasswordTextBox import PasswordTextBox | |
32 from pyjamas.ui.TextArea import TextArea | |
33 from pyjamas.ui.CheckBox import CheckBox | |
34 from pyjamas.ui.ListBox import ListBox | |
35 from pyjamas.ui.Button import Button | |
321
bfbd9d6eb901
browser_side: info dialog to let the user know that a refresh is needed after GUI parameters have been changed
souliane <souliane@mailoo.org>
parents:
299
diff
changeset
|
36 from dialog import InfoDialog |
143 | 37 from nativedom import NativeDOM |
38 | |
39 | |
40 class InvalidXMLUI(Exception): | |
41 pass | |
42 | |
43 class Pairs(Grid): | |
44 | |
45 def __init__(self): | |
247
fe83837d3491
browser_side: removed some trailing spaces
Goffi <goffi@goffi.org>
parents:
229
diff
changeset
|
46 Grid.__init__(self, 0, 0) |
143 | 47 self.row = 0 |
48 self.col = 0 | |
49 | |
50 def append(self, widget): | |
51 if self.col == 0: | |
52 self.resize(self.row+1, 2) | |
53 self.setWidget(self.row, self.col, widget) | |
54 self.col += 1 | |
55 if self.col == 2: | |
56 self.row +=1 | |
57 self.col = 0 | |
247
fe83837d3491
browser_side: removed some trailing spaces
Goffi <goffi@goffi.org>
parents:
229
diff
changeset
|
58 |
143 | 59 class XMLUI(VerticalPanel): |
247
fe83837d3491
browser_side: removed some trailing spaces
Goffi <goffi@goffi.org>
parents:
229
diff
changeset
|
60 |
143 | 61 def __init__(self, host, xml_data, title = None, options = None, misc = None, close_cb = None): |
62 print "XMLUI init" | |
63 VerticalPanel.__init__(self) | |
64 self.dom = NativeDOM() | |
65 self.host = host | |
66 self.title = title | |
67 self.options = options or [] | |
68 self.misc = misc or {} | |
69 self.close_cb = close_cb | |
70 self.__dest = "window" | |
71 self.ctrl_list = {} # usefull to access ctrl | |
72 self.constructUI(xml_data) | |
73 self.setSize('100%', '100%') | |
74 | |
75 def setCloseCb(self, close_cb): | |
76 self.close_cb = close_cb | |
77 | |
78 def close(self): | |
79 if self.close_cb: | |
80 self.close_cb() | |
81 else: | |
82 print "WARNING: no close method defined" | |
83 | |
84 def __parseElems(self, node, parent): | |
85 """Parse elements inside a <layout> tags, and add them to the parent""" | |
86 for elem in node.childNodes: | |
87 if elem.nodeName != "elem": | |
88 raise Exception("Unmanaged tag [%s]" % (elem.nodeName)) | |
89 node_id = elem.getAttribute("node_id") | |
90 name = elem.getAttribute("name") | |
91 node_type = elem.getAttribute("type") | |
92 value = elem.getAttribute("value") if elem.hasAttribute('value') else u'' | |
93 if node_type=="empty": | |
247
fe83837d3491
browser_side: removed some trailing spaces
Goffi <goffi@goffi.org>
parents:
229
diff
changeset
|
94 ctrl = Label('') |
143 | 95 elif node_type=="text": |
96 try: | |
97 value = elem.childNodes[0].wholeText | |
98 except IndexError: | |
99 print ("WARNING: text node has no child !") | |
100 ctrl = Label(value) | |
101 elif node_type=="label": | |
102 ctrl = Label(value+": ") | |
103 elif node_type=="string": | |
104 ctrl = TextBox() | |
105 ctrl.setText(value) | |
299
e4f586fc6101
server and browser side: updated callback system to follow core changes
Goffi <goffi@goffi.org>
parents:
247
diff
changeset
|
106 self.ctrl_list[name] = {'node_type':node_type, 'control':ctrl} |
143 | 107 elif node_type=="password": |
108 ctrl = PasswordTextBox() | |
109 ctrl.setText(value) | |
299
e4f586fc6101
server and browser side: updated callback system to follow core changes
Goffi <goffi@goffi.org>
parents:
247
diff
changeset
|
110 self.ctrl_list[name] = {'node_type':node_type, 'control':ctrl} |
143 | 111 elif node_type=="textbox": |
112 ctrl = TextArea() | |
113 ctrl.setText(value) | |
299
e4f586fc6101
server and browser side: updated callback system to follow core changes
Goffi <goffi@goffi.org>
parents:
247
diff
changeset
|
114 self.ctrl_list[name] = {'node_type':node_type, 'control':ctrl} |
143 | 115 elif node_type=="bool": |
116 ctrl = CheckBox() | |
117 ctrl.setChecked(value=="true") | |
299
e4f586fc6101
server and browser side: updated callback system to follow core changes
Goffi <goffi@goffi.org>
parents:
247
diff
changeset
|
118 self.ctrl_list[name] = {'node_type':node_type, 'control':ctrl} |
143 | 119 elif node_type=="list": |
299
e4f586fc6101
server and browser side: updated callback system to follow core changes
Goffi <goffi@goffi.org>
parents:
247
diff
changeset
|
120 _options = [(option.getAttribute("label"), option.getAttribute("value")) for option in elem.getElementsByTagName("option")] |
e4f586fc6101
server and browser side: updated callback system to follow core changes
Goffi <goffi@goffi.org>
parents:
247
diff
changeset
|
121 attr_map = {label: value for label, value in _options} |
143 | 122 ctrl = ListBox() |
123 ctrl.setMultipleSelect(elem.getAttribute("multi")=='yes') | |
299
e4f586fc6101
server and browser side: updated callback system to follow core changes
Goffi <goffi@goffi.org>
parents:
247
diff
changeset
|
124 for option in _options: |
e4f586fc6101
server and browser side: updated callback system to follow core changes
Goffi <goffi@goffi.org>
parents:
247
diff
changeset
|
125 ctrl.addItem(option[0]) |
229
e632f77c4219
bridge: asyncGetParamA takes a security_limit argument
souliane <souliane@mailoo.org>
parents:
218
diff
changeset
|
126 ctrl.selectItem(value) |
299
e4f586fc6101
server and browser side: updated callback system to follow core changes
Goffi <goffi@goffi.org>
parents:
247
diff
changeset
|
127 self.ctrl_list[name] = {'node_type':node_type, 'control':ctrl, 'attr_map': attr_map} |
143 | 128 elif node_type=="button": |
129 callback_id = elem.getAttribute("callback_id") | |
130 ctrl = Button(value, self.onButtonPress) | |
131 ctrl.param_id = (callback_id,[field.getAttribute('name') for field in elem.getElementsByTagName("field_back")]) | |
132 else: | |
133 print("FIXME FIXME FIXME: type [%s] is not implemented" % node_type) #FIXME ! | |
134 raise NotImplementedError | |
135 if self.node_type == 'param': | |
229
e632f77c4219
bridge: asyncGetParamA takes a security_limit argument
souliane <souliane@mailoo.org>
parents:
218
diff
changeset
|
136 if isinstance(ctrl, TextBoxBase): |
143 | 137 ctrl.addChangeListener(self.onParamChange) |
138 elif isinstance(ctrl, CheckBox): | |
139 ctrl.addClickListener(self.onParamChange) | |
229
e632f77c4219
bridge: asyncGetParamA takes a security_limit argument
souliane <souliane@mailoo.org>
parents:
218
diff
changeset
|
140 elif isinstance(ctrl, ListBox): |
e632f77c4219
bridge: asyncGetParamA takes a security_limit argument
souliane <souliane@mailoo.org>
parents:
218
diff
changeset
|
141 ctrl.addChangeListener(self.onParamChange) |
143 | 142 ctrl._param_category = self._current_category |
143 ctrl._param_name = name | |
144 parent.append(ctrl) | |
145 | |
146 def __parseChilds(self, current, elem, wanted = ['layout'], data = None): | |
147 """Recursively parse childNodes of an elemen | |
148 @param current: widget container with 'append' method | |
149 @param elem: element from which childs will be parsed | |
150 @param wanted: list of tag names that can be present in the childs to be SàT XMLUI compliant""" | |
151 for node in elem.childNodes: | |
152 if wanted and not node.nodeName in wanted: | |
153 raise InvalidXMLUI("ERROR: unexpected nodeName") | |
154 if node.nodeName == "layout": | |
155 node_type = node.getAttribute('type') | |
156 if node_type == "tabs": | |
218
4e6467efd6bf
browser_side: small improvements for parameters panel
souliane <souliane@mailoo.org>
parents:
165
diff
changeset
|
157 tab_cont = TabPanel() |
143 | 158 tab_cont.setStyleName('liberviaTabPanel') |
159 tab_cont.setHeight('100%') | |
160 self.__parseChilds(current, node, ['category'], tab_cont) | |
161 current.append(tab_cont) | |
162 if isinstance(current, CellPanel): | |
163 current.setCellHeight(tab_cont, '100%') | |
218
4e6467efd6bf
browser_side: small improvements for parameters panel
souliane <souliane@mailoo.org>
parents:
165
diff
changeset
|
164 if len(tab_cont.getChildren()) > 0: |
4e6467efd6bf
browser_side: small improvements for parameters panel
souliane <souliane@mailoo.org>
parents:
165
diff
changeset
|
165 tab_cont.selectTab(0) |
143 | 166 elif node_type == "vertical": |
167 self.__parseElems(node, current) | |
168 elif node_type == "pairs": | |
169 pairs = Pairs() | |
170 self.__parseElems(node, pairs) | |
171 current.append(pairs) | |
172 else: | |
173 print("WARNING: Unknown layout [%s], using default one" % (node_type,)) | |
174 self.__parseElems(node, current) | |
175 elif node.nodeName == "category": | |
247
fe83837d3491
browser_side: removed some trailing spaces
Goffi <goffi@goffi.org>
parents:
229
diff
changeset
|
176 name = node.getAttribute('name') |
fe83837d3491
browser_side: removed some trailing spaces
Goffi <goffi@goffi.org>
parents:
229
diff
changeset
|
177 label = node.getAttribute('label') |
143 | 178 if not name or not isinstance(data,TabPanel): |
247
fe83837d3491
browser_side: removed some trailing spaces
Goffi <goffi@goffi.org>
parents:
229
diff
changeset
|
179 raise InvalidXMLUI |
143 | 180 if self.node_type == 'param': |
181 self._current_category = name #XXX: awful hack because params need category and we don't keep parent | |
182 tab_cont = data | |
183 tab_body = VerticalPanel() | |
184 tab_cont.add(tab_body, label or name) | |
185 self.__parseChilds(tab_body, node, ['layout']) | |
186 else: | |
187 message=_("Unknown tag") | |
188 raise NotImplementedError(message) | |
189 | |
190 def constructUI(self, xml_data): | |
247
fe83837d3491
browser_side: removed some trailing spaces
Goffi <goffi@goffi.org>
parents:
229
diff
changeset
|
191 |
143 | 192 cat_dom = self.dom.parseString(xml_data) |
247
fe83837d3491
browser_side: removed some trailing spaces
Goffi <goffi@goffi.org>
parents:
229
diff
changeset
|
193 |
143 | 194 top=cat_dom.documentElement |
195 self.node_type = top.getAttribute("type") | |
196 self.title = top.getAttribute("title") or self.title | |
299
e4f586fc6101
server and browser side: updated callback system to follow core changes
Goffi <goffi@goffi.org>
parents:
247
diff
changeset
|
197 self.session_id = top.getAttribute("session_id") or None |
e4f586fc6101
server and browser side: updated callback system to follow core changes
Goffi <goffi@goffi.org>
parents:
247
diff
changeset
|
198 self.submit_id = top.getAttribute("submit") or None |
143 | 199 if top.nodeName != "sat_xmlui" or not self.node_type in ['form', 'param', 'window']: |
200 raise InvalidXMLUI | |
201 | |
202 if self.node_type == 'param': | |
203 self.param_changed = set() | |
204 | |
205 self.__parseChilds(self, cat_dom.documentElement) | |
247
fe83837d3491
browser_side: removed some trailing spaces
Goffi <goffi@goffi.org>
parents:
229
diff
changeset
|
206 |
143 | 207 if self.node_type == 'form': |
208 hpanel = HorizontalPanel() | |
209 hpanel.add(Button('Submit',self.onFormSubmitted)) | |
210 if not 'NO_CANCEL' in self.options: | |
211 hpanel.add(Button('Cancel',self.onFormCancelled)) | |
212 self.add(hpanel) | |
213 elif self.node_type == 'param': | |
214 assert(isinstance(self.children[0],TabPanel)) | |
215 hpanel = HorizontalPanel() | |
247
fe83837d3491
browser_side: removed some trailing spaces
Goffi <goffi@goffi.org>
parents:
229
diff
changeset
|
216 hpanel.add(Button('Cancel', lambda ignore: self.close())) |
fe83837d3491
browser_side: removed some trailing spaces
Goffi <goffi@goffi.org>
parents:
229
diff
changeset
|
217 hpanel.add(Button('Save', self.onSaveParams)) |
143 | 218 self.add(hpanel) |
219 | |
220 ##EVENTS## | |
221 | |
222 def onButtonPress(self, button): | |
223 print "onButtonPress (%s)" % (button,) | |
224 callback_id, fields = button.param_id | |
225 for field in fields: | |
226 ctrl = self.ctrl_list[field] | |
227 if isinstance(ctrl['control'],ListBox): | |
229
e632f77c4219
bridge: asyncGetParamA takes a security_limit argument
souliane <souliane@mailoo.org>
parents:
218
diff
changeset
|
228 data[field] = '\t'.join(ctrl['control'].getSelectedItemText()) |
143 | 229 elif isinstance(ctrl['control'],CheckBox): |
230 data[field] = "true" if ctrl['control'].isChecked() else "false" | |
231 else: | |
232 data[field] = ctrl['control'].getText() | |
233 | |
299
e4f586fc6101
server and browser side: updated callback system to follow core changes
Goffi <goffi@goffi.org>
parents:
247
diff
changeset
|
234 self.host.launchAction(callback_id, None) |
143 | 235 |
236 def onParamChange(self, widget): | |
237 """Called when type is param and a widget to save is modified""" | |
238 assert(self.node_type == "param") | |
239 print "onParamChange:", widget | |
240 self.param_changed.add(widget) | |
241 | |
242 def onFormSubmitted(self, button): | |
243 print "onFormSubmitted" | |
247
fe83837d3491
browser_side: removed some trailing spaces
Goffi <goffi@goffi.org>
parents:
229
diff
changeset
|
244 # FIXME: untested |
143 | 245 print "FIXME FIXME FIXME: Form submitting not managed yet" |
246 data = [] | |
247 for ctrl_name in self.ctrl_list: | |
299
e4f586fc6101
server and browser side: updated callback system to follow core changes
Goffi <goffi@goffi.org>
parents:
247
diff
changeset
|
248 escaped = u"%s%s" % (SAT_FORM_PREFIX, ctrl_name) |
e4f586fc6101
server and browser side: updated callback system to follow core changes
Goffi <goffi@goffi.org>
parents:
247
diff
changeset
|
249 ctrl = self.ctrl_list[escaped] |
143 | 250 if isinstance(ctrl['control'], ListBox): |
299
e4f586fc6101
server and browser side: updated callback system to follow core changes
Goffi <goffi@goffi.org>
parents:
247
diff
changeset
|
251 data.append((escaped, '\t'.join([ctrl['attr_map'][label] for label in ctrl['control'].getSelectedItemText()]))) |
143 | 252 elif isinstance(ctrl['control'], CheckBox): |
299
e4f586fc6101
server and browser side: updated callback system to follow core changes
Goffi <goffi@goffi.org>
parents:
247
diff
changeset
|
253 data.append((escaped, "true" if ctrl['control'].isChecked() else "false")) |
143 | 254 else: |
299
e4f586fc6101
server and browser side: updated callback system to follow core changes
Goffi <goffi@goffi.org>
parents:
247
diff
changeset
|
255 data.append((escaped, ctrl['control'].getText())) |
143 | 256 if 'action_back' in self.misc: #FIXME FIXME FIXME: WTF ! Must be cleaned |
257 raise NotImplementedError | |
258 elif 'callback' in self.misc: | |
259 self.misc['callback'](data) | |
299
e4f586fc6101
server and browser side: updated callback system to follow core changes
Goffi <goffi@goffi.org>
parents:
247
diff
changeset
|
260 elif self.submit_id is not None: |
e4f586fc6101
server and browser side: updated callback system to follow core changes
Goffi <goffi@goffi.org>
parents:
247
diff
changeset
|
261 data = dict(selected_values) |
e4f586fc6101
server and browser side: updated callback system to follow core changes
Goffi <goffi@goffi.org>
parents:
247
diff
changeset
|
262 if self.session_id is not None: |
e4f586fc6101
server and browser side: updated callback system to follow core changes
Goffi <goffi@goffi.org>
parents:
247
diff
changeset
|
263 data["session_id"] = self.session_id |
e4f586fc6101
server and browser side: updated callback system to follow core changes
Goffi <goffi@goffi.org>
parents:
247
diff
changeset
|
264 self.host.launchAction(self.submit_id, data) |
143 | 265 else: |
266 print ("WARNING: The form data is not sent back, the type is not managed properly") | |
267 | |
268 self.close() | |
247
fe83837d3491
browser_side: removed some trailing spaces
Goffi <goffi@goffi.org>
parents:
229
diff
changeset
|
269 |
143 | 270 def onFormCancelled(self, button): |
271 self.close() | |
272 | |
273 def onSaveParams(self, button): | |
274 print "onSaveParams" | |
321
bfbd9d6eb901
browser_side: info dialog to let the user know that a refresh is needed after GUI parameters have been changed
souliane <souliane@mailoo.org>
parents:
299
diff
changeset
|
275 refresh = False |
143 | 276 for ctrl in self.param_changed: |
277 if isinstance(ctrl, CheckBox): | |
278 value = "true" if ctrl.isChecked() else "false" | |
229
e632f77c4219
bridge: asyncGetParamA takes a security_limit argument
souliane <souliane@mailoo.org>
parents:
218
diff
changeset
|
279 elif isinstance(ctrl, ListBox): |
e632f77c4219
bridge: asyncGetParamA takes a security_limit argument
souliane <souliane@mailoo.org>
parents:
218
diff
changeset
|
280 value = '\t'.join(ctrl.getSelectedItemText()) |
143 | 281 else: |
282 value = ctrl.getText() | |
283 self.host.bridge.call('setParam', None, ctrl._param_name, value, ctrl._param_category) | |
321
bfbd9d6eb901
browser_side: info dialog to let the user know that a refresh is needed after GUI parameters have been changed
souliane <souliane@mailoo.org>
parents:
299
diff
changeset
|
284 if ctrl._param_name in self.host.ui_refresh_params: |
bfbd9d6eb901
browser_side: info dialog to let the user know that a refresh is needed after GUI parameters have been changed
souliane <souliane@mailoo.org>
parents:
299
diff
changeset
|
285 refresh = True |
143 | 286 self.close() |
321
bfbd9d6eb901
browser_side: info dialog to let the user know that a refresh is needed after GUI parameters have been changed
souliane <souliane@mailoo.org>
parents:
299
diff
changeset
|
287 if refresh: |
bfbd9d6eb901
browser_side: info dialog to let the user know that a refresh is needed after GUI parameters have been changed
souliane <souliane@mailoo.org>
parents:
299
diff
changeset
|
288 InfoDialog(_("Refresh needed"), _("A parameter requesting a page refresh has been modified.<br/>Your changes will be fully effective only after you refresh the page with 'F5' or from your browser menu.")).show() |