Mercurial > libervia-backend
view frontends/src/quick_frontend/quick_widgets.py @ 1300:ba73798317a7 frontends_multi_profiles
core: fixed items registration in roster management
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 06 Feb 2015 18:54:44 +0100 |
parents | faa1129559b8 |
children | afc57b34c0a3 |
line wrap: on
line source
#!/usr/bin/python # -*- coding: utf-8 -*- # helper class for making 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.log import getLogger log = getLogger(__name__) from sat.core import exceptions classes_map = {} try: # FIXME: to be removed when an acceptable solution is here unicode('') # XXX: unicode doesn't exist in pyjamas except (TypeError, AttributeError): # Error raised is not the same depending on pyjsbuild options unicode = str def register(base_cls, child_cls=None): """Register a child class to use by default when a base class is needed @param base_cls: "Quick..." base class (like QuickChat or QuickContact), must inherit from QuickWidget @param child_cls: inherited class to use when Quick... class is requested, must inherit from base_cls. Can be None if it's the base_cls itself which register """ classes_map[base_cls] = child_cls class QuickWidgetsManager(object): """This class is used to manage all the widgets of a frontend A widget can be a window, a graphical thing, or someting else depending of the frontend""" def __init__(self, host): self.host = host self._widgets = {} def __iter__(self): """Iterate throught all widgets""" for widget_map in self._widgets.itervalues(): for widget in widget_map.itervalues(): yield widget def getRealClass(self, class_): """Return class registered for given class_ @param class_: subclass of QuickWidget @return: class actually used to create widget """ try: cls = classes_map[class_] except KeyError: cls = class_ if cls is None: raise exceptions.InternalError("There is not class registered for {}".format(class_)) return cls def getWidgets(self, class_): """Get all subclassed widgets @param class_: subclass of QuickWidget, same parameter as used in [getOrCreateWidget] @return: iterator on widgets """ class_ = self.getRealClass(class_) try: widgets_map = self._widgets[class_] except KeyError: return iter([]) else: return widgets_map.itervalues() def getOrCreateWidget(self, class_, target, *args, **kwargs): """Get an existing widget or create a new one when necessary If the widget is new, self.host.newWidget will be called with it. @param class_(class): class of the widget to create @param target: target depending of the widget, usually a JID instance @param args(list): optional args to create a new instance of class_ @param kwargs(list): optional kwargs to create anew instance of class_ if 'profile' key is present, it will be popped and put in 'profiles' if there is neither 'profile' nor 'profiles', None will be used for 'profiles' if 'on_new_widget' is present it can have the following values: 'NEW_WIDGET' [default]: self.host.newWidget will be called on widget creation [callable]: this method will be called instead of self.host.newWidget None: do nothing if 'force_hash' is present, the hash given in value will be used instead of the one returned by class_.getWidgetHash @return: a class_ instance, either new or already existing """ cls = self.getRealClass(class_) # arguments management _args = [self.host, target] + list(args) or [] # FIXME: check if it's really necessary to use optional args _kwargs = kwargs or {} if 'profiles' in _kwargs and 'profile' in _kwargs: raise ValueError("You can't have 'profile' and 'profiles' keys at the same time") try: _kwargs['profiles'] = _kwargs.pop('profile') except KeyError: if not 'profiles' in _kwargs: _kwargs['profiles'] = None # we get the hash try: hash_ = _kwargs.pop('force_hash') except KeyError: hash_ = cls.getWidgetHash(target, _kwargs['profiles']) # widget creation or retrieval widgets_map = self._widgets.setdefault(cls, {}) # we sorts widgets by classes if not cls.SINGLE: widget = None # if the class is not SINGLE, we always create a new widget else: try: widget = widgets_map[hash_] widget.addTarget(target) except KeyError: widget = None if widget is None: # we need to create a new widget try: #on_new_widget tell what to do for the new widget creation on_new_widget = _kwargs.pop('on_new_widget') except KeyError: on_new_widget = 'NEW_WIDGET' log.debug(u"Creating new widget for target {} {}".format(target, cls)) widget = cls(*_args, **_kwargs) widgets_map[hash_] = widget if on_new_widget == 'NEW_WIDGET': self.host.newWidget(widget) elif callable(on_new_widget): on_new_widget(widget) else: assert on_new_widget is None return widget class QuickWidget(object): """generic widget base""" SINGLE=True # if True, there can be only one widget per target(s) PROFILES_MULTIPLE=False PROFILES_ALLOW_NONE=False def __init__(self, host, target, profiles=None): """ @param host: %(doc_host)s @param target: target specific for this widget class @param profiles: can be either: - (unicode): used when widget class manage a unique profile - (iterable): some widget class can manage several profiles, several at once can be specified here - None: no profile is managed by this widget class (rare) @raise: ValueError when (iterable) or None is given to profiles for a widget class which manage one unique profile. """ self.host = host self.targets = set() self.addTarget(target) self.profiles = set() if isinstance(profiles, basestring): self.addProfile(profiles) elif profiles is None: if not self.PROFILES_ALLOW_NONE: raise ValueError("profiles can't have a value of None") else: if not self.PROFILES_MULTIPLE: raise ValueError("multiple profiles are not allowed") for profile in profiles: self.addProfile(profile) @property def profile(self): assert len(self.profiles) == 1 and not self.PROFILES_MULTIPLE and not self.PROFILES_ALLOW_NONE return list(self.profiles)[0] def addTarget(self, target): """Add a target if it doesn't already exists @param target: target to add """ self.targets.add(target) def addProfile(self, profile): """Add a profile is if doesn't already exists @param profile: profile to add """ if self.profiles and not self.PROFILES_MULTIPLE: raise ValueError("multiple profiles are not allowed") self.profiles.add(profile) @staticmethod def getWidgetHash(target, profiles): """Return the hash associated with this target for this widget class some widget classes can manage several target on the same instance (e.g.: a chat widget with multiple resources on the same bare jid), this method allow to return a hash associated to one or several targets to retrieve the good instance. For example, a widget managing JID targets, and all resource of the same bare jid would return the bare jid as hash. @param target: target to check @param profiles: profile(s) associated to target, see __init__ docstring @return: a hash (can correspond to one or many targets or profiles, depending of widget class) """ return unicode(target) # by defaut, there is one hash for one target