Mercurial > libervia-backend
view frontends/src/wix/xmlui.py @ 1265:e3a9ea76de35 frontends_multi_profiles
quick_frontend, primitivus: multi-profiles refactoring part 1 (big commit, sorry :p):
This refactoring allow primitivus to manage correctly several profiles at once, with various other improvments:
- profile_manager can now plug several profiles at once, requesting password when needed. No more profile plug specific method is used anymore in backend, instead a "validated" key is used in actions
- Primitivus widget are now based on a common "PrimitivusWidget" classe which mainly manage the decoration so far
- all widgets are treated in the same way (contactList, Chat, Progress, etc), no more chat_wins specific behaviour
- widgets are created in a dedicated manager, with facilities to react on new widget creation or other events
- quick_frontend introduce a new QuickWidget class, which aims to be as generic and flexible as possible. It can manage several targets (jids or something else), and several profiles
- each widget class return a Hash according to its target. For example if given a target jid and a profile, a widget class return a hash like (target.bare, profile), the same widget will be used for all resources of the same jid
- better management of CHAT_GROUP mode for Chat widgets
- some code moved from Primitivus to QuickFrontend, the final goal is to have most non backend code in QuickFrontend, and just graphic code in subclasses
- no more (un)escapePrivate/PRIVATE_PREFIX
- contactList improved a lot: entities not in roster and special entities (private MUC conversations) are better managed
- resources can be displayed in Primitivus, and their status messages
- profiles are managed in QuickFrontend with dedicated managers
This is work in progress, other frontends are broken. Urwid SàText need to be updated. Most of features of Primitivus should work as before (or in a better way ;))
author | Goffi <goffi@goffi.org> |
---|---|
date | Wed, 10 Dec 2014 19:00:09 +0100 |
parents | f91e7028e2c3 |
children |
line wrap: on
line source
#!/usr/bin/python # -*- coding: utf-8 -*- # wix: a SAT frontend # Copyright (C) 2009, 2010, 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.i18n import _ import wx from sat.core.log import getLogger log = getLogger(__name__) from sat_frontends.tools import xmlui from sat_frontends.constants import Const as C class EventWidget(object): """ Used to manage change event of widgets """ def _xmluiOnChange(self, callback): """ Call callback with widget as only argument """ def change_cb(event): callback(self) self.Bind(self._xmlui_change_event, change_cb) class WixWidget(object): _xmlui_proportion = 0 class ValueWidget(WixWidget): def _xmluiSetValue(self, value): self.SetValue(value) def _xmluiGetValue(self): return self.GetValue() class EmptyWidget(WixWidget, xmlui.EmptyWidget, wx.Window): def __init__(self, _xmlui_parent): wx.Window.__init__(self, _xmlui_parent, -1) class TextWidget(WixWidget, xmlui.TextWidget, wx.StaticText): def __init__(self, _xmlui_parent, value): wx.StaticText.__init__(self, _xmlui_parent, -1, value) class LabelWidget(xmlui.LabelWidget, TextWidget): def __init__(self, _xmlui_parent, value): super(LabelWidget, self).__init__(_xmlui_parent, value+": ") class JidWidget(xmlui.JidWidget, TextWidget): pass class DividerWidget(WixWidget, xmlui.DividerWidget, wx.StaticLine): def __init__(self, _xmlui_parent, style='line'): wx.StaticLine.__init__(self, _xmlui_parent, -1) class StringWidget(EventWidget, ValueWidget, xmlui.StringWidget, wx.TextCtrl): _xmlui_change_event = wx.EVT_TEXT def __init__(self, _xmlui_parent, value, read_only=False): style = wx.TE_READONLY if read_only else 0 wx.TextCtrl.__init__(self, _xmlui_parent, -1, value, style=style) self._xmlui_proportion = 1 class PasswordWidget(EventWidget, ValueWidget, xmlui.PasswordWidget, wx.TextCtrl): _xmlui_change_event = wx.EVT_TEXT def __init__(self, _xmlui_parent, value, read_only=False): style = wx.TE_PASSWORD if read_only: style |= wx.TE_READONLY wx.TextCtrl.__init__(self, _xmlui_parent, -1, value, style=style) self._xmlui_proportion = 1 class TextBoxWidget(EventWidget, ValueWidget, xmlui.TextBoxWidget, wx.TextCtrl): _xmlui_change_event = wx.EVT_TEXT def __init__(self, _xmlui_parent, value, read_only=False): style = wx.TE_MULTILINE if read_only: style |= wx.TE_READONLY wx.TextCtrl.__init__(self, _xmlui_parent, -1, value, style=style) self._xmlui_proportion = 1 class BoolWidget(EventWidget, ValueWidget, xmlui.BoolWidget, wx.CheckBox): _xmlui_change_event = wx.EVT_CHECKBOX def __init__(self, _xmlui_parent, state, read_only=False): style = wx.CHK_2STATE if read_only: style |= wx.TE_READONLY wx.CheckBox.__init__(self, _xmlui_parent, -1, "", style=wx.CHK_2STATE) self.SetValue(state) self._xmlui_proportion = 1 def _xmluiSetValue(self, value): self.SetValue(value == 'true') def _xmluiGetValue(self): return "true" if self.GetValue() else "false" # TODO: use wx.SpinCtrl instead of wx.TextCtrl class IntWidget(EventWidget, ValueWidget, xmlui.IntWidget, wx.TextCtrl): _xmlui_change_event = wx.EVT_TEXT def __init__(self, _xmlui_parent, value, read_only=False): style = wx.TE_READONLY if read_only else 0 wx.TextCtrl.__init__(self, _xmlui_parent, -1, value, style=style) self._xmlui_proportion = 1 class ButtonWidget(EventWidget, WixWidget, xmlui.ButtonWidget, wx.Button): _xmlui_change_event = wx.EVT_BUTTON def __init__(self, _xmlui_parent, value, click_callback): wx.Button.__init__(self, _xmlui_parent, -1, value) self._xmlui_click_callback = click_callback _xmlui_parent.Bind(wx.EVT_BUTTON, lambda evt: click_callback(evt.GetEventObject()), self) self._xmlui_parent = _xmlui_parent def _xmluiOnClick(self, callback): self._xmlui_parent.Bind(wx.EVT_BUTTON, lambda evt: callback(evt.GetEventObject()), self) class ListWidget(EventWidget, WixWidget, xmlui.ListWidget, wx.ListBox): _xmlui_change_event = wx.EVT_LISTBOX def __init__(self, _xmlui_parent, options, selected, flags): styles = wx.LB_MULTIPLE if not 'single' in flags else wx.LB_SINGLE wx.ListBox.__init__(self, _xmlui_parent, -1, choices=[option[1] for option in options], style=styles) self._xmlui_attr_map = {label: value for value, label in options} self._xmlui_proportion = 1 self._xmluiSelectValues(selected) def _xmluiSelectValue(self, value): 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 for idx in xrange(self.GetCount()): self.SetSelection(idx, self.GetString(idx) == label) def _xmluiSelectValues(self, values): labels = [label for label, _value in self._xmlui_attr_map.items() if _value in values] for idx in xrange(self.GetCount()): self.SetSelection(idx, self.GetString(idx) in labels) def _xmluiGetSelectedValues(self): ret = [] labels = [self.GetString(idx) for idx in self.GetSelections()] for label in labels: ret.append(self._xmlui_attr_map[label]) return ret def _xmluiAddValues(self, values, select=True): selected = self._xmluiGetSelectedValues() for value in values: if value not in self._xmlui_attr_map.values(): wx.ListBox.Append(self, value) self._xmlui_attr_map[value] = value if value not in selected: selected.append(value) self._xmluiSelectValues(selected) class WixContainer(object): _xmlui_proportion = 1 def _xmluiAppend(self, widget): self.sizer.Add(widget, self._xmlui_proportion, flag=wx.EXPAND) class AdvancedListContainer(WixContainer, xmlui.AdvancedListContainer, wx.ScrolledWindow): def __init__(self, _xmlui_parent, columns, selectable='no'): wx.ScrolledWindow.__init__(self, _xmlui_parent) self._xmlui_selectable = selectable != 'no' if selectable: columns += 1 self.sizer = wx.FlexGridSizer(cols=columns) self.SetSizer(self.sizer) self._xmlui_select_cb = None self._xmlui_select_idx = None self._xmlui_select_widgets = [] def _xmluiAddRow(self, idx): # XXX: select_button is a Q&D way to implement row selection # FIXME: must be done properly if not self._xmlui_selectable: return select_button = wx.Button(self, wx.ID_OK, label=_("select")) self.sizer.Add(select_button) def click_cb(event, idx=idx): cb = self._xmlui_select_cb self._xmlui_select_idx = idx # TODO: fill self._xmlui_select_widgets if cb is not None: cb(self) event.Skip() self.Bind(wx.EVT_BUTTON, click_cb) def _xmluiGetSelectedWidgets(self): return self._xmlui_select_widgets def _xmluiGetSelectedIndex(self): return self._xmlui_select_idx def _xmluiOnSelect(self, callback): self._xmlui_select_cb = callback class PairsContainer(WixContainer, xmlui.PairsContainer, wx.Panel): def __init__(self, _xmlui_parent): wx.Panel.__init__(self, _xmlui_parent) self.sizer = wx.FlexGridSizer(cols=2) self.sizer.AddGrowableCol(1) #The growable column need most of time to be the right one in pairs self.SetSizer(self.sizer) class TabsContainer(WixContainer, xmlui.TabsContainer, wx.Notebook): def __init__(self, _xmlui_parent): wx.Notebook.__init__(self, _xmlui_parent, -1, style=wx.NB_LEFT if self._xmlui_main.type=='param' else 0) def _xmluiAddTab(self, label): tab_panel = wx.Panel(self, -1) tab_panel.sizer = wx.BoxSizer(wx.VERTICAL) tab_panel.SetSizer(tab_panel.sizer) self.AddPage(tab_panel, label) VerticalContainer._xmluiAdapt(tab_panel) return tab_panel class VerticalContainer(WixContainer, xmlui.VerticalContainer, wx.Panel): def __init__(self, _xmlui_parent): wx.Panel.__init__(self, _xmlui_parent) self.sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(self.sizer) ## Dialogs ## class WixDialog(object): def __init__(self, _xmlui_parent, level): self.host = _xmlui_parent.host self.ok_cb = None self.cancel_cb = None if level == C.XMLUI_DATA_LVL_INFO: self.flags = wx.ICON_INFORMATION elif level == C.XMLUI_DATA_LVL_ERROR: self.flags = wx.ICON_ERROR else: self.flags = wx.ICON_INFORMATION log.warning(_("Unmanaged dialog level: %s") % level) def _xmluiShow(self): answer = self.ShowModal() if answer == wx.ID_YES or answer == wx.ID_OK: self._xmluiValidated() else: self._xmluiCancelled() def _xmluiClose(self): self.Destroy() class MessageDialog(WixDialog, xmlui.MessageDialog, wx.MessageDialog): def __init__(self, _xmlui_parent, title, message, level): WixDialog.__init__(self, _xmlui_parent, level) xmlui.MessageDialog.__init__(self, _xmlui_parent) self.flags |= wx.OK wx.MessageDialog.__init__(self, _xmlui_parent.host, message, title, style = self.flags) class NoteDialog(xmlui.NoteDialog, MessageDialog): # TODO: separate NoteDialog pass class ConfirmDialog(WixDialog, xmlui.ConfirmDialog, wx.MessageDialog): def __init__(self, _xmlui_parent, title, message, level, buttons_set): WixDialog.__init__(self, _xmlui_parent, level) xmlui.ConfirmDialog.__init__(self, _xmlui_parent) if buttons_set == C.XMLUI_DATA_BTNS_SET_YESNO: self.flags |= wx.YES_NO else: self.flags |= wx.OK | wx.CANCEL wx.MessageDialog.__init__(self, _xmlui_parent.host, message, title, style = self.flags) class FileDialog(WixDialog, xmlui.FileDialog, wx.FileDialog): def __init__(self, _xmlui_parent, title, message, level, filetype): # TODO: message and filetype are not managed yet WixDialog.__init__(self, _xmlui_parent, level) self.flags = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT # FIXME: use the legacy flags, but must manage cases like dir or open xmlui.FileDialog.__init__(self, _xmlui_parent) wx.FileDialog.__init__(self, _xmlui_parent.host, title, style = self.flags) def _xmluiShow(self): answer = self.ShowModal() if answer == wx.ID_OK: self._xmluiValidated({'path': self.GetPath()}) else: self._xmluiCancelled() class GenericFactory(object): def __getattr__(self, attr): if attr.startswith("create"): cls = globals()[attr[6:]] return cls class WidgetFactory(GenericFactory): def __getattr__(self, attr): if attr.startswith("create"): cls = GenericFactory.__getattr__(self, attr) cls._xmlui_main = self._xmlui_main return cls class XMLUIPanel(xmlui.XMLUIPanel, wx.Frame): """Create an user interface from a SàT XML""" widget_factory = WidgetFactory() def __init__(self, host, parsed_xml, title=None, flags = None,): self.widget_factory._xmlui_main = self xmlui.XMLUIPanel.__init__(self, host, parsed_xml, title, flags) def constructUI(self, parsed_dom): style = wx.DEFAULT_FRAME_STYLE & ~wx.CLOSE_BOX if 'NO_CANCEL' in self.flags else wx.DEFAULT_FRAME_STYLE wx.Frame.__init__(self, None, style=style) self.sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(self.sizer) def postTreat(): if self.title: self.SetTitle(self.title) if self.type == 'form': dialogButtons = wx.StdDialogButtonSizer() submitButton = wx.Button(self.main_cont,wx.ID_OK, label=_("Submit")) dialogButtons.AddButton(submitButton) self.main_cont.Bind(wx.EVT_BUTTON, self.onFormSubmitted, submitButton) if not 'NO_CANCEL' in self.flags: cancelButton = wx.Button(self.main_cont,wx.ID_CANCEL) dialogButtons.AddButton(cancelButton) self.main_cont.Bind(wx.EVT_BUTTON, self.onFormCancelled, cancelButton) dialogButtons.Realize() self.main_cont.sizer.Add(dialogButtons, flag=wx.ALIGN_CENTER_HORIZONTAL) self.sizer.Add(self.main_cont, 1, flag=wx.EXPAND) self.sizer.Fit(self) self.Show() super(XMLUIPanel, self).constructUI(parsed_dom, postTreat) if not 'NO_CANCEL' in self.flags: self.Bind(wx.EVT_CLOSE, self.onClose, self) self.MakeModal() def _xmluiClose(self): self.MakeModal(False) self.Destroy() ###events def onParamChange(self, ctrl): super(XMLUIPanel, self).onParamChange(ctrl) def onFormSubmitted(self, event): """Called when submit button is clicked""" button = event.GetEventObject() super(XMLUIPanel, self).onFormSubmitted(button) def onClose(self, event): """Close event: we have to send the form.""" log.debug(_("close")) if self.type == 'param': self.onSaveParams() else: self._xmluiClose() event.Skip() class XMLUIDialog(xmlui.XMLUIDialog): dialog_factory = WidgetFactory() xmlui.registerClass(xmlui.CLASS_PANEL, XMLUIPanel) xmlui.registerClass(xmlui.CLASS_DIALOG, XMLUIDialog) create = xmlui.create