Mercurial > libervia-web
view src/browser/sat_browser/xmlui.py @ 589:a5019e62c3e9 frontends_multi_profiles
browser side: big refactoring to base Libervia on QuickFrontend, first draft:
/!\ not finished, partially working and highly instable
- add collections module with an OrderedDict like class
- SatWebFrontend inherit from QuickApp
- general sat_frontends tools.jid module is used
- bridge/json methods have moved to json module
- UniBox is partially removed (should be totally removed before merge to trunk)
- Signals are now register with the generic registerSignal method (which is called mainly in QuickFrontend)
- the generic getOrCreateWidget method from QuickWidgetsManager is used instead of Libervia's specific methods
- all Widget are now based more or less directly on QuickWidget
- with the new QuickWidgetsManager.getWidgets method, it's no more necessary to check all widgets which are instance of a particular class
- ChatPanel and related moved to chat module
- MicroblogPanel and related moved to blog module
- global and overcomplicated send method has been disabled: each class should manage its own sending
- for consistency with other frontends, former ContactPanel has been renamed to ContactList and vice versa
- for the same reason, ChatPanel has been renamed to Chat
- for compatibility with QuickFrontend, a fake profile is used in several places, it is set to C.PROF_KEY_NONE (real profile is managed server side for obvious security reasons)
- changed default url for web panel to SàT website, and contact address to generic SàT contact address
- ContactList is based on QuickContactList, UI changes are done in update method
- bride call (now json module) have been greatly improved, in particular call can be done in the same way as for other frontends (bridge.method_name(arg1, arg2, ..., callback=cb, errback=eb). Blocking method must be called like async methods due to javascript architecture
- in bridge calls, a callback can now exists without errback
- hard reload on BridgeSignals remote error has been disabled, a better option should be implemented
- use of constants where that make sens, some style improvments
- avatars are temporarily disabled
- lot of code disabled, will be fixed or removed before merge
- various other changes, check diff for more details
server side: manage remote exception on getEntityData, removed getProfileJid call, added getWaitingConf, added getRoomsSubjects
author | Goffi <goffi@goffi.org> |
---|---|
date | Sat, 24 Jan 2015 01:45:39 +0100 |
parents | fed185c95f1c |
children | ebb602d8b3f2 |
line wrap: on
line source
#!/usr/bin/python # -*- coding: utf-8 -*- # Libervia: a Salut à Toi frontend # Copyright (C) 2011, 2012, 2013, 2014 Jérôme Poisson <goffi@goffi.org> # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. from sat.core.log import getLogger log = getLogger(__name__) from sat_frontends.tools import xmlui from sat_frontends.tools import strings from sat_browser.constants import Const as C from sat_browser import dialog from sat_browser import html_tools from pyjamas.ui.VerticalPanel import VerticalPanel from pyjamas.ui.HorizontalPanel import HorizontalPanel from pyjamas.ui.TabPanel import TabPanel from pyjamas.ui.Grid import Grid from pyjamas.ui.Label import Label from pyjamas.ui.TextBox import TextBox from pyjamas.ui.PasswordTextBox import PasswordTextBox from pyjamas.ui.TextArea import TextArea from pyjamas.ui.CheckBox import CheckBox from pyjamas.ui.ListBox import ListBox from pyjamas.ui.Button import Button from pyjamas.ui.HTML import HTML import nativedom class EmptyWidget(xmlui.EmptyWidget, Label): def __init__(self, _xmlui_parent): Label.__init__(self, '') class TextWidget(xmlui.TextWidget, Label): def __init__(self, _xmlui_parent, value): Label.__init__(self, value) class LabelWidget(xmlui.LabelWidget, TextWidget): def __init__(self, _xmlui_parent, value): TextWidget.__init__(self, _xmlui_parent, value + ": ") class JidWidget(xmlui.JidWidget, TextWidget): def __init__(self, _xmlui_parent, value): TextWidget.__init__(self, _xmlui_parent, value) class DividerWidget(xmlui.DividerWidget, HTML): def __init__(self, _xmlui_parent, style='line'): """Add a divider @param _xmlui_parent @param style (string): one of: - line: a simple line - dot: a line of dots - dash: a line of dashes - plain: a full thick line - blank: a blank line/space """ HTML.__init__(self, "<hr/>") self.addStyleName(style) class StringWidget(xmlui.StringWidget, TextBox): def __init__(self, _xmlui_parent, value, read_only=False): TextBox.__init__(self) self.setText(value) self.setReadonly(read_only) def _xmluiSetValue(self, value): self.setText(value) def _xmluiGetValue(self): return self.getText() def _xmluiOnChange(self, callback): self.addChangeListener(callback) class PasswordWidget(xmlui.PasswordWidget, PasswordTextBox): def __init__(self, _xmlui_parent, value, read_only=False): PasswordTextBox.__init__(self) self.setText(value) self.setReadonly(read_only) def _xmluiSetValue(self, value): self.setText(value) def _xmluiGetValue(self): return self.getText() def _xmluiOnChange(self, callback): self.addChangeListener(callback) class TextBoxWidget(xmlui.TextBoxWidget, TextArea): def __init__(self, _xmlui_parent, value, read_only=False): TextArea.__init__(self) self.setText(value) self.setReadonly(read_only) def _xmluiSetValue(self, value): self.setText(value) def _xmluiGetValue(self): return self.getText() def _xmluiOnChange(self, callback): self.addChangeListener(callback) class BoolWidget(xmlui.BoolWidget, CheckBox): def __init__(self, _xmlui_parent, state, read_only=False): CheckBox.__init__(self) self.setChecked(state) self.setReadonly(read_only) def _xmluiSetValue(self, value): self.setChecked(value == "true") def _xmluiGetValue(self): return "true" if self.isChecked() else "false" def _xmluiOnChange(self, callback): self.addClickListener(callback) class IntWidget(xmlui.IntWidget, TextBox): def __init__(self, _xmlui_parent, value, read_only=False): TextBox.__init__(self) self.setText(value) self.setReadonly(read_only) def _xmluiSetValue(self, value): self.setText(value) def _xmluiGetValue(self): return self.getText() def _xmluiOnChange(self, callback): self.addChangeListener(callback) class ButtonWidget(xmlui.ButtonWidget, Button): def __init__(self, _xmlui_parent, value, click_callback): Button.__init__(self, value, click_callback) def _xmluiOnClick(self, callback): self.addClickListener(callback) class ListWidget(xmlui.ListWidget, ListBox): def __init__(self, _xmlui_parent, options, selected, flags): ListBox.__init__(self) multi_selection = 'single' not in flags self.setMultipleSelect(multi_selection) if multi_selection: self.setVisibleItemCount(5) for option in options: self.addItem(option[1]) self._xmlui_attr_map = {label: value for value, label in options} self._xmluiSelectValues(selected) def _xmluiSelectValue(self, value): """Select a value checking its item""" try: label = [label for label, _value in self._xmlui_attr_map.items() if _value == value][0] except IndexError: log.warning("Can't find value [%s] to select" % value) return self.selectItem(label) def _xmluiSelectValues(self, values): """Select multiple values, ignore the items""" self.setValueSelection(values) def _xmluiGetSelectedValues(self): ret = [] for label in self.getSelectedItemText(): ret.append(self._xmlui_attr_map[label]) return ret def _xmluiOnChange(self, callback): self.addChangeListener(callback) def _xmluiAddValues(self, values, select=True): selected = self._xmluiGetSelectedValues() for value in values: if value not in self._xmlui_attr_map.values(): self.addItem(value) self._xmlui_attr_map[value] = value if value not in selected: selected.append(value) self._xmluiSelectValues(selected) class LiberviaContainer(object): def _xmluiAppend(self, widget): self.append(widget) class AdvancedListContainer(xmlui.AdvancedListContainer, Grid): def __init__(self, _xmlui_parent, columns, selectable='no'): Grid.__init__(self, 0, columns) self.columns = columns self.row = -1 self.col = 0 self._xmlui_rows_idx = [] self._xmlui_selectable = selectable != 'no' self._xmlui_selected_row = None self.addTableListener(self) if self._xmlui_selectable: self.addStyleName('AdvancedListSelectable') def onCellClicked(self, grid, row, col): if not self._xmlui_selectable: return self._xmlui_selected_row = row try: self._xmlui_select_cb(self) except AttributeError: log.warning("no select callback set") def _xmluiAppend(self, widget): self.setWidget(self.row, self.col, widget) self.col += 1 def _xmluiAddRow(self, idx): self.row += 1 self.col = 0 self._xmlui_rows_idx.insert(self.row, idx) self.resizeRows(self.row + 1) def _xmluiGetSelectedWidgets(self): return [self.getWidget(self._xmlui_selected_row, col) for col in range(self.columns)] def _xmluiGetSelectedIndex(self): try: return self._xmlui_rows_idx[self._xmlui_selected_row] except TypeError: return None def _xmluiOnSelect(self, callback): self._xmlui_select_cb = callback class PairsContainer(xmlui.PairsContainer, Grid): def __init__(self, _xmlui_parent): Grid.__init__(self, 0, 0) self.row = 0 self.col = 0 def _xmluiAppend(self, widget): if self.col == 0: self.resize(self.row + 1, 2) self.setWidget(self.row, self.col, widget) self.col += 1 if self.col == 2: self.row += 1 self.col = 0 class TabsContainer(LiberviaContainer, xmlui.TabsContainer, TabPanel): def __init__(self, _xmlui_parent): TabPanel.__init__(self) self.setStyleName('liberviaTabPanel') def _xmluiAddTab(self, label): tab_panel = VerticalContainer(self) self.add(tab_panel, label) if len(self.getChildren()) == 1: self.selectTab(0) return tab_panel class VerticalContainer(LiberviaContainer, xmlui.VerticalContainer, VerticalPanel): __bases__ = (LiberviaContainer, xmlui.VerticalContainer, VerticalPanel) def __init__(self, _xmlui_parent): VerticalPanel.__init__(self) ## Dialogs ## class Dlg(object): def _xmluiShow(self): self.show() def _xmluiClose(self): pass class MessageDialog(Dlg, xmlui.MessageDialog, dialog.InfoDialog): def __init__(self, _xmlui_parent, title, message, level): #TODO: level is not managed title = html_tools.html_sanitize(title) message = strings.addURLToText(html_tools.XHTML2Text(message)) Dlg.__init__(self) xmlui.MessageDialog.__init__(self, _xmlui_parent) dialog.InfoDialog.__init__(self, title, message, self._xmluiValidated()) class NoteDialog(xmlui.NoteDialog, MessageDialog): # TODO: separate NoteDialog def __init__(self, _xmlui_parent, title, message, level): xmlui.NoteDialog.__init__(self, _xmlui_parent) MessageDialog.__init__(self, _xmlui_parent, title, message, level) class ConfirmDialog(xmlui.ConfirmDialog, Dlg, dialog.ConfirmDialog): def __init__(self, _xmlui_parent, title, message, level): #TODO: level is not managed title = html_tools.html_sanitize(title) message = strings.addURLToText(html_tools.XHTML2Text(message)) xmlui.ConfirmDialog.__init__(self, _xmlui_parent) Dlg.__init__(self) dialog.ConfirmDialog.__init__(self, self.answered, message, title) def answered(self, validated): if validated: self._xmluiValidated() else: self._xmluiCancelled() class FileDialog(xmlui.FileDialog, Dlg): #TODO: def __init__(self, _xmlui_parent, title, message, level, filetype): raise NotImplementedError("FileDialog is not implemented in Libervia yet") class GenericFactory(object): # XXX: __getattr__ doens't work here with pyjamas for an unknown reason # so an introspection workaround is used def __init__(self): for name, cls in globals().items(): if name.endswith("Widget") or name.endswith("Container") or name.endswith("Dialog"): log.info("registering: %s" % name) def createCreater(cls): return lambda *args, **kwargs: self._genericCreate(cls, *args, **kwargs) setattr(self, "create%s" % name, createCreater(cls)) def _genericCreate(self, cls, *args, **kwargs): instance = cls(*args, **kwargs) return instance # def __getattr__(self, attr): # if attr.startswith("create"): # cls = globals()[attr[6:]] # cls._xmlui_main = self._xmlui_main # return cls class WidgetFactory(GenericFactory): def _genericCreate(self, cls, *args, **kwargs): instance = GenericFactory._genericCreate(self, cls, *args, **kwargs) instance._xmlui_main = self._xmlui_main return instance class LiberviaXMLUIBase(object): def _xmluiLaunchAction(self, action_id, data): self.host.launchAction(action_id, data) class XMLUIPanel(LiberviaXMLUIBase, xmlui.XMLUIPanel, VerticalPanel): widget_factory = WidgetFactory() def __init__(self, host, parsed_xml, title=None, flags=None, callback=None, profile=C.PROF_KEY_NONE): self.widget_factory._xmlui_main = self VerticalPanel.__init__(self) self.setSize('100%', '100%') xmlui.XMLUIPanel.__init__(self, host, parsed_xml, title, flags, callback, profile) def setCloseCb(self, close_cb): self.close_cb = close_cb def _xmluiClose(self): if self.close_cb: self.close_cb() else: log.warning("no close method defined") def _xmluiSetParam(self, name, value, category): self.host.bridge.call('setParam', None, name, value, category) def constructUI(self, parsed_dom): super(XMLUIPanel, self).constructUI(parsed_dom) self.add(self.main_cont) self.setCellHeight(self.main_cont, '100%') if self.type == 'form': hpanel = HorizontalPanel() hpanel.setStyleName('marginAuto') hpanel.add(Button('Submit', self.onFormSubmitted)) if not 'NO_CANCEL' in self.flags: hpanel.add(Button('Cancel', self.onFormCancelled)) self.add(hpanel) elif self.type == 'param': assert(isinstance(self.children[0][0], TabPanel)) hpanel = HorizontalPanel() hpanel.add(Button('Save', self.onSaveParams)) hpanel.add(Button('Cancel', lambda ignore: self._xmluiClose())) self.add(hpanel) def show(self): options = ['NO_CLOSE'] if self.type == C.XMLUI_FORM else [] _dialog = dialog.GenericDialog(self.xmlui_title, self, options=options) self.setCloseCb(_dialog.close) _dialog.show() class XMLUIDialog(LiberviaXMLUIBase, xmlui.XMLUIDialog): dialog_factory = GenericFactory() def __init__(self, host, parsed_dom, title = None, flags = None, callback=None, profile=C.PROF_KEY_NONE): xmlui.XMLUIDialog.__init__(self, host, parsed_dom, title, flags, callback, profile) xmlui.registerClass(xmlui.CLASS_PANEL, XMLUIPanel) xmlui.registerClass(xmlui.CLASS_DIALOG, XMLUIDialog) def create(*args, **kwargs): dom = nativedom.NativeDOM() kwargs['dom_parse'] = lambda xml_data: dom.parseString(xml_data) return xmlui.create(*args, **kwargs)