merged branch frontends_multi_profiles
This commit is contained in:
commit
6760c927d3
73 changed files with 4611 additions and 4514 deletions
3
INSTALL
3
INSTALL
|
|
@ -49,9 +49,6 @@ $ sat
|
|||
$ sat stop
|
||||
- to launch Primitivus, enter:
|
||||
$ primitivus
|
||||
then create a profile.
|
||||
- to launch Wix, enter
|
||||
$ wix
|
||||
then create a profile.
|
||||
- to use jp, follow its help:
|
||||
$ jp --help
|
||||
|
|
|
|||
|
|
@ -5,6 +5,6 @@ global-include *.po *.mo
|
|||
global-include CHANGELOG COPYING* INSTALL README*
|
||||
global-include *.sh
|
||||
include src/sat.*
|
||||
include frontends/src/jp/jp frontends/src/primitivus/primitivus frontends/src/wix/wix
|
||||
include frontends/src/jp/jp frontends/src/primitivus/primitivus
|
||||
include src/bridge/bridge_constructor/mediawiki_template.tpl
|
||||
prune src/bridge/bridge_constructor/generated
|
||||
|
|
|
|||
7
README
7
README
|
|
@ -28,7 +28,6 @@ In addition, SàT has been built with ethic in mind, a social contract show the
|
|||
|
||||
For the moment, the frontends are:
|
||||
|
||||
* wix: based on WxWidgets, a minimal and old fashioned desktop frontend
|
||||
* primitivus: a console interface client, for screen lovers
|
||||
* jp: the powerful command line toolkit.
|
||||
* libervia: the web frontend. It's in a different package, check libervia on the wiki: http://wiki.goffi.org/wiki/Libervia
|
||||
|
|
@ -81,7 +80,6 @@ http://wiki.goffi.org/wiki/Primitivus
|
|||
SàT is the acronym (yes another one :( ) in tribute to the song Salut à Toi from the Bérurier Noir band. I recommend also the excellent cover from Les Ogres de Barback.
|
||||
jp stands for "Jabber coPy" or "JumP".
|
||||
Primitivus is based on Urwid, and, according to their FAQ, "ur" is a German prefix for "ancestral or primal" (primitivus is a latin word).
|
||||
wix come of course from the WX of wxwidgets and just sounds cool :).
|
||||
|
||||
|
||||
** /!\ PRIVACY WARNING /!\ **
|
||||
|
|
@ -107,9 +105,6 @@ SàT is heavily based on the twisted framework (http://twistedmatrix.com/trac/),
|
|||
wokkel:
|
||||
SàT use a library with high level enhancements on top of twisted, which is called wokkel (http://wokkel.ik.nu). Lot of thanks to Ralph Meijer and all other contributors.
|
||||
|
||||
wxWidgets & wxPython:
|
||||
The Wix frontend is made with wxWidgets (www.wxwidgets.org) and its python version wxPython (wxPython), which is a really handy UI toolkit who works on most popular platforms. It can be found on probably all major Gnu/Linux distributions.
|
||||
|
||||
Urwid:
|
||||
Primitivus is based on Urwid (http://excess.org/urwid/) which saved me a lot of time. It's really a great library to easily make a sophisticated interface.
|
||||
|
||||
|
|
@ -162,7 +157,7 @@ Salut à Toi has received contributions from:
|
|||
|
||||
- Matteo Cypriani <mcy@lm7.fr>: jp's mainloop update + doc improvements + various fixes. He's also the other co-maintainer of the Debian package.
|
||||
|
||||
- Olly Betts <olly@survex.com>: icon fix in Wix
|
||||
- Olly Betts <olly@survex.com>: icon fix in Wix [N.B: Wix has been removed]
|
||||
|
||||
Many thanks to them.
|
||||
|
||||
|
|
|
|||
|
|
@ -49,12 +49,6 @@ DBus
|
|||
|
||||
Suggested: Python X Library (for user notifications in a graphical session)
|
||||
|
||||
---------------------------------
|
||||
Dependencies for the Wix frontend
|
||||
---------------------------------
|
||||
|
||||
wxPython
|
||||
|
||||
--------------------------------------
|
||||
Dependencies for the Libervia frontend
|
||||
--------------------------------------
|
||||
|
|
@ -107,7 +101,7 @@ The case of SàT Media is particular are there is a compilation of media from se
|
|||
Packages and versions
|
||||
#####################
|
||||
|
||||
We have packaged Wix, Primitivus, and Jp with the core backend, as we think theses frontends should be shipped with each SàT distribution. But allowing them to be installed separately would be good: for example, Wix will be useless for a server or non-graphical workstation.
|
||||
We have packaged Primitivus and Jp with the core backend, as we think theses frontends should be shipped with each SàT distribution.
|
||||
|
||||
Libervia is distributed as a separate package because of its specificities (it's a huge project by itself, it uses Pyjamas Python => JS Compiler, etc.). But it's part of the same project.
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ NOTE: *.po files are in i18n directory
|
|||
To translate a file, you can use a dedicated tool as the excellent gtranslator:
|
||||
|
||||
- use the template .po file (e.g. sat.po) and name it to your translated language (e.g. fr.po for french); you can preferably generate a new template directly from the source with the following command (eventually adapted, the following command use zsh's globbing syntax), launched from root sat dir after having emptied the build directory:
|
||||
> xgettext -L python -d sat --keyword=D_ -p i18n **/*(.py|.tac|primitivus|wix)(.) **/jp(.)
|
||||
> xgettext -L python -d sat --keyword=D_ -p i18n **/*(.py|.tac|primitivus)(.) **/jp(.)
|
||||
|
||||
- then to start a new translation, copy i18n/sat.po to your language file, e.g. cd i18n; cp sat.po fr.po
|
||||
|
||||
|
|
|
|||
|
|
@ -102,10 +102,10 @@ class DBusBridgeFrontend(BridgeFrontend):
|
|||
async = False
|
||||
|
||||
if kwargs:
|
||||
if 'callback' in kwargs and 'errback' in kwargs:
|
||||
if 'callback' in kwargs:
|
||||
async = True
|
||||
_callback = kwargs.pop('callback')
|
||||
_errback = kwargs.pop('errback')
|
||||
_errback = kwargs.pop('errback', lambda failure: log.error(unicode(failure)))
|
||||
elif len(args) >= 2 and callable(args[-1]) and callable(args[-2]):
|
||||
async = True
|
||||
args = list(args)
|
||||
|
|
@ -122,138 +122,511 @@ class DBusBridgeFrontend(BridgeFrontend):
|
|||
return method(*args, **kwargs)
|
||||
|
||||
return getPluginMethod
|
||||
def addContact(self, entity_jid, profile_key="@DEFAULT@"):
|
||||
return self.db_core_iface.addContact(entity_jid, profile_key)
|
||||
|
||||
def addContact(self, entity_jid, profile_key="@DEFAULT@", callback=None, errback=None):
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
kwargs={}
|
||||
if callback is not None:
|
||||
kwargs['timeout'] = const_TIMEOUT
|
||||
kwargs['reply_handler'] = callback
|
||||
kwargs['error_handler'] = error_handler
|
||||
return self.db_core_iface.addContact(entity_jid, profile_key, **kwargs)
|
||||
|
||||
def asyncConnect(self, profile_key="@DEFAULT@", password='', callback=None, errback=None):
|
||||
error_handler = None if callback is None else lambda err:errback(dbus_to_bridge_exception(err))
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
return self.db_core_iface.asyncConnect(profile_key, password, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler)
|
||||
|
||||
def asyncCreateProfile(self, profile, password='', callback=None, errback=None):
|
||||
error_handler = None if callback is None else lambda err:errback(dbus_to_bridge_exception(err))
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
return self.db_core_iface.asyncCreateProfile(profile, password, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler)
|
||||
|
||||
def asyncDeleteProfile(self, profile, callback=None, errback=None):
|
||||
error_handler = None if callback is None else lambda err:errback(dbus_to_bridge_exception(err))
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
return self.db_core_iface.asyncDeleteProfile(profile, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler)
|
||||
|
||||
def asyncGetParamA(self, name, category, attribute="value", security_limit=-1, profile_key="@DEFAULT@", callback=None, errback=None):
|
||||
error_handler = None if callback is None else lambda err:errback(dbus_to_bridge_exception(err))
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
return unicode(self.db_core_iface.asyncGetParamA(name, category, attribute, security_limit, profile_key, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler))
|
||||
|
||||
def confirmationAnswer(self, id, accepted, data, profile):
|
||||
return self.db_core_iface.confirmationAnswer(id, accepted, data, profile)
|
||||
def confirmationAnswer(self, id, accepted, data, profile, callback=None, errback=None):
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
kwargs={}
|
||||
if callback is not None:
|
||||
kwargs['timeout'] = const_TIMEOUT
|
||||
kwargs['reply_handler'] = callback
|
||||
kwargs['error_handler'] = error_handler
|
||||
return self.db_core_iface.confirmationAnswer(id, accepted, data, profile, **kwargs)
|
||||
|
||||
def delContact(self, entity_jid, profile_key="@DEFAULT@"):
|
||||
return self.db_core_iface.delContact(entity_jid, profile_key)
|
||||
def delContact(self, entity_jid, profile_key="@DEFAULT@", callback=None, errback=None):
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
return self.db_core_iface.delContact(entity_jid, profile_key, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler)
|
||||
|
||||
def discoInfos(self, entity_jid, profile_key, callback=None, errback=None):
|
||||
error_handler = None if callback is None else lambda err:errback(dbus_to_bridge_exception(err))
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
return self.db_core_iface.discoInfos(entity_jid, profile_key, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler)
|
||||
|
||||
def discoItems(self, entity_jid, profile_key, callback=None, errback=None):
|
||||
error_handler = None if callback is None else lambda err:errback(dbus_to_bridge_exception(err))
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
return self.db_core_iface.discoItems(entity_jid, profile_key, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler)
|
||||
|
||||
def disconnect(self, profile_key="@DEFAULT@"):
|
||||
return self.db_core_iface.disconnect(profile_key)
|
||||
def disconnect(self, profile_key="@DEFAULT@", callback=None, errback=None):
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
kwargs={}
|
||||
if callback is not None:
|
||||
kwargs['timeout'] = const_TIMEOUT
|
||||
kwargs['reply_handler'] = callback
|
||||
kwargs['error_handler'] = error_handler
|
||||
return self.db_core_iface.disconnect(profile_key, **kwargs)
|
||||
|
||||
def getConfig(self, section, name):
|
||||
return unicode(self.db_core_iface.getConfig(section, name))
|
||||
def getConfig(self, section, name, callback=None, errback=None):
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
kwargs={}
|
||||
if callback is not None:
|
||||
kwargs['timeout'] = const_TIMEOUT
|
||||
kwargs['reply_handler'] = callback
|
||||
kwargs['error_handler'] = error_handler
|
||||
return unicode(self.db_core_iface.getConfig(section, name, **kwargs))
|
||||
|
||||
def getContacts(self, profile_key="@DEFAULT@"):
|
||||
return self.db_core_iface.getContacts(profile_key)
|
||||
def getContacts(self, profile_key="@DEFAULT@", callback=None, errback=None):
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
return self.db_core_iface.getContacts(profile_key, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler)
|
||||
|
||||
def getContactsFromGroup(self, group, profile_key="@DEFAULT@"):
|
||||
return self.db_core_iface.getContactsFromGroup(group, profile_key)
|
||||
def getContactsFromGroup(self, group, profile_key="@DEFAULT@", callback=None, errback=None):
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
kwargs={}
|
||||
if callback is not None:
|
||||
kwargs['timeout'] = const_TIMEOUT
|
||||
kwargs['reply_handler'] = callback
|
||||
kwargs['error_handler'] = error_handler
|
||||
return self.db_core_iface.getContactsFromGroup(group, profile_key, **kwargs)
|
||||
|
||||
def getEntityData(self, jid, keys, profile):
|
||||
return self.db_core_iface.getEntityData(jid, keys, profile)
|
||||
def getEntitiesData(self, jids, keys, profile, callback=None, errback=None):
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
kwargs={}
|
||||
if callback is not None:
|
||||
kwargs['timeout'] = const_TIMEOUT
|
||||
kwargs['reply_handler'] = callback
|
||||
kwargs['error_handler'] = error_handler
|
||||
return self.db_core_iface.getEntitiesData(jids, keys, profile, **kwargs)
|
||||
|
||||
def getEntityData(self, jid, keys, profile, callback=None, errback=None):
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
kwargs={}
|
||||
if callback is not None:
|
||||
kwargs['timeout'] = const_TIMEOUT
|
||||
kwargs['reply_handler'] = callback
|
||||
kwargs['error_handler'] = error_handler
|
||||
return self.db_core_iface.getEntityData(jid, keys, profile, **kwargs)
|
||||
|
||||
def getHistory(self, from_jid, to_jid, limit, between=True, search='', profile="@NONE@", callback=None, errback=None):
|
||||
error_handler = None if callback is None else lambda err:errback(dbus_to_bridge_exception(err))
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
return self.db_core_iface.getHistory(from_jid, to_jid, limit, between, search, profile, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler)
|
||||
|
||||
def getLastResource(self, contact_jid, profile_key="@DEFAULT@"):
|
||||
return unicode(self.db_core_iface.getLastResource(contact_jid, profile_key))
|
||||
def getMainResource(self, contact_jid, profile_key="@DEFAULT@", callback=None, errback=None):
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
kwargs={}
|
||||
if callback is not None:
|
||||
kwargs['timeout'] = const_TIMEOUT
|
||||
kwargs['reply_handler'] = callback
|
||||
kwargs['error_handler'] = error_handler
|
||||
return unicode(self.db_core_iface.getMainResource(contact_jid, profile_key, **kwargs))
|
||||
|
||||
def getMenuHelp(self, menu_id, language):
|
||||
return unicode(self.db_core_iface.getMenuHelp(menu_id, language))
|
||||
def getMenuHelp(self, menu_id, language, callback=None, errback=None):
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
kwargs={}
|
||||
if callback is not None:
|
||||
kwargs['timeout'] = const_TIMEOUT
|
||||
kwargs['reply_handler'] = callback
|
||||
kwargs['error_handler'] = error_handler
|
||||
return unicode(self.db_core_iface.getMenuHelp(menu_id, language, **kwargs))
|
||||
|
||||
def getMenus(self, language, security_limit):
|
||||
return self.db_core_iface.getMenus(language, security_limit)
|
||||
def getMenus(self, language, security_limit, callback=None, errback=None):
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
kwargs={}
|
||||
if callback is not None:
|
||||
kwargs['timeout'] = const_TIMEOUT
|
||||
kwargs['reply_handler'] = callback
|
||||
kwargs['error_handler'] = error_handler
|
||||
return self.db_core_iface.getMenus(language, security_limit, **kwargs)
|
||||
|
||||
def getParamA(self, name, category, attribute="value", profile_key="@DEFAULT@"):
|
||||
return unicode(self.db_core_iface.getParamA(name, category, attribute, profile_key))
|
||||
def getParamA(self, name, category, attribute="value", profile_key="@DEFAULT@", callback=None, errback=None):
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
kwargs={}
|
||||
if callback is not None:
|
||||
kwargs['timeout'] = const_TIMEOUT
|
||||
kwargs['reply_handler'] = callback
|
||||
kwargs['error_handler'] = error_handler
|
||||
return unicode(self.db_core_iface.getParamA(name, category, attribute, profile_key, **kwargs))
|
||||
|
||||
def getParams(self, security_limit=-1, app='', profile_key="@DEFAULT@", callback=None, errback=None):
|
||||
error_handler = None if callback is None else lambda err:errback(dbus_to_bridge_exception(err))
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
return unicode(self.db_core_iface.getParams(security_limit, app, profile_key, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler))
|
||||
|
||||
def getParamsCategories(self, ):
|
||||
return self.db_core_iface.getParamsCategories()
|
||||
def getParamsCategories(self, callback=None, errback=None):
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
kwargs={}
|
||||
if callback is not None:
|
||||
kwargs['timeout'] = const_TIMEOUT
|
||||
kwargs['reply_handler'] = callback
|
||||
kwargs['error_handler'] = error_handler
|
||||
return self.db_core_iface.getParamsCategories(**kwargs)
|
||||
|
||||
def getParamsForCategory(self, category, security_limit=-1, app='', profile_key="@DEFAULT@", callback=None, errback=None):
|
||||
error_handler = None if callback is None else lambda err:errback(dbus_to_bridge_exception(err))
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
return unicode(self.db_core_iface.getParamsForCategory(category, security_limit, app, profile_key, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler))
|
||||
|
||||
def getParamsUI(self, security_limit=-1, app='', profile_key="@DEFAULT@", callback=None, errback=None):
|
||||
error_handler = None if callback is None else lambda err:errback(dbus_to_bridge_exception(err))
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
return unicode(self.db_core_iface.getParamsUI(security_limit, app, profile_key, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler))
|
||||
|
||||
def getPresenceStatuses(self, profile_key="@DEFAULT@"):
|
||||
return self.db_core_iface.getPresenceStatuses(profile_key)
|
||||
def getPresenceStatuses(self, profile_key="@DEFAULT@", callback=None, errback=None):
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
kwargs={}
|
||||
if callback is not None:
|
||||
kwargs['timeout'] = const_TIMEOUT
|
||||
kwargs['reply_handler'] = callback
|
||||
kwargs['error_handler'] = error_handler
|
||||
return self.db_core_iface.getPresenceStatuses(profile_key, **kwargs)
|
||||
|
||||
def getProfileName(self, profile_key="@DEFAULT@"):
|
||||
return unicode(self.db_core_iface.getProfileName(profile_key))
|
||||
def getProfileName(self, profile_key="@DEFAULT@", callback=None, errback=None):
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
kwargs={}
|
||||
if callback is not None:
|
||||
kwargs['timeout'] = const_TIMEOUT
|
||||
kwargs['reply_handler'] = callback
|
||||
kwargs['error_handler'] = error_handler
|
||||
return unicode(self.db_core_iface.getProfileName(profile_key, **kwargs))
|
||||
|
||||
def getProfilesList(self, ):
|
||||
return self.db_core_iface.getProfilesList()
|
||||
def getProfilesList(self, callback=None, errback=None):
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
kwargs={}
|
||||
if callback is not None:
|
||||
kwargs['timeout'] = const_TIMEOUT
|
||||
kwargs['reply_handler'] = callback
|
||||
kwargs['error_handler'] = error_handler
|
||||
return self.db_core_iface.getProfilesList(**kwargs)
|
||||
|
||||
def getProgress(self, id, profile):
|
||||
return self.db_core_iface.getProgress(id, profile)
|
||||
def getProgress(self, id, profile, callback=None, errback=None):
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
kwargs={}
|
||||
if callback is not None:
|
||||
kwargs['timeout'] = const_TIMEOUT
|
||||
kwargs['reply_handler'] = callback
|
||||
kwargs['error_handler'] = error_handler
|
||||
return self.db_core_iface.getProgress(id, profile, **kwargs)
|
||||
|
||||
def getReady(self, callback=None, errback=None):
|
||||
error_handler = None if callback is None else lambda err:errback(dbus_to_bridge_exception(err))
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
return self.db_core_iface.getReady(timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler)
|
||||
|
||||
def getVersion(self, ):
|
||||
return unicode(self.db_core_iface.getVersion())
|
||||
def getVersion(self, callback=None, errback=None):
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
kwargs={}
|
||||
if callback is not None:
|
||||
kwargs['timeout'] = const_TIMEOUT
|
||||
kwargs['reply_handler'] = callback
|
||||
kwargs['error_handler'] = error_handler
|
||||
return unicode(self.db_core_iface.getVersion(**kwargs))
|
||||
|
||||
def getWaitingConf(self, profile_key):
|
||||
return self.db_core_iface.getWaitingConf(profile_key)
|
||||
def getWaitingConf(self, profile_key, callback=None, errback=None):
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
kwargs={}
|
||||
if callback is not None:
|
||||
kwargs['timeout'] = const_TIMEOUT
|
||||
kwargs['reply_handler'] = callback
|
||||
kwargs['error_handler'] = error_handler
|
||||
return self.db_core_iface.getWaitingConf(profile_key, **kwargs)
|
||||
|
||||
def getWaitingSub(self, profile_key="@DEFAULT@"):
|
||||
return self.db_core_iface.getWaitingSub(profile_key)
|
||||
def getWaitingSub(self, profile_key="@DEFAULT@", callback=None, errback=None):
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
kwargs={}
|
||||
if callback is not None:
|
||||
kwargs['timeout'] = const_TIMEOUT
|
||||
kwargs['reply_handler'] = callback
|
||||
kwargs['error_handler'] = error_handler
|
||||
return self.db_core_iface.getWaitingSub(profile_key, **kwargs)
|
||||
|
||||
def isConnected(self, profile_key="@DEFAULT@"):
|
||||
return self.db_core_iface.isConnected(profile_key)
|
||||
def isConnected(self, profile_key="@DEFAULT@", callback=None, errback=None):
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
kwargs={}
|
||||
if callback is not None:
|
||||
kwargs['timeout'] = const_TIMEOUT
|
||||
kwargs['reply_handler'] = callback
|
||||
kwargs['error_handler'] = error_handler
|
||||
return self.db_core_iface.isConnected(profile_key, **kwargs)
|
||||
|
||||
def launchAction(self, callback_id, data, profile_key="@DEFAULT@", callback=None, errback=None):
|
||||
error_handler = None if callback is None else lambda err:errback(dbus_to_bridge_exception(err))
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
return self.db_core_iface.launchAction(callback_id, data, profile_key, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler)
|
||||
|
||||
def loadParamsTemplate(self, filename):
|
||||
return self.db_core_iface.loadParamsTemplate(filename)
|
||||
def loadParamsTemplate(self, filename, callback=None, errback=None):
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
kwargs={}
|
||||
if callback is not None:
|
||||
kwargs['timeout'] = const_TIMEOUT
|
||||
kwargs['reply_handler'] = callback
|
||||
kwargs['error_handler'] = error_handler
|
||||
return self.db_core_iface.loadParamsTemplate(filename, **kwargs)
|
||||
|
||||
def paramsRegisterApp(self, xml, security_limit=-1, app=''):
|
||||
return self.db_core_iface.paramsRegisterApp(xml, security_limit, app)
|
||||
def paramsRegisterApp(self, xml, security_limit=-1, app='', callback=None, errback=None):
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
kwargs={}
|
||||
if callback is not None:
|
||||
kwargs['timeout'] = const_TIMEOUT
|
||||
kwargs['reply_handler'] = callback
|
||||
kwargs['error_handler'] = error_handler
|
||||
return self.db_core_iface.paramsRegisterApp(xml, security_limit, app, **kwargs)
|
||||
|
||||
def saveParamsTemplate(self, filename):
|
||||
return self.db_core_iface.saveParamsTemplate(filename)
|
||||
def saveParamsTemplate(self, filename, callback=None, errback=None):
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
kwargs={}
|
||||
if callback is not None:
|
||||
kwargs['timeout'] = const_TIMEOUT
|
||||
kwargs['reply_handler'] = callback
|
||||
kwargs['error_handler'] = error_handler
|
||||
return self.db_core_iface.saveParamsTemplate(filename, **kwargs)
|
||||
|
||||
def sendMessage(self, to_jid, message, subject='', mess_type="auto", extra={}, profile_key="@NONE@", callback=None, errback=None):
|
||||
error_handler = None if callback is None else lambda err:errback(dbus_to_bridge_exception(err))
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
return self.db_core_iface.sendMessage(to_jid, message, subject, mess_type, extra, profile_key, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler)
|
||||
|
||||
def setParam(self, name, value, category, security_limit=-1, profile_key="@DEFAULT@"):
|
||||
return self.db_core_iface.setParam(name, value, category, security_limit, profile_key)
|
||||
def setParam(self, name, value, category, security_limit=-1, profile_key="@DEFAULT@", callback=None, errback=None):
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
kwargs={}
|
||||
if callback is not None:
|
||||
kwargs['timeout'] = const_TIMEOUT
|
||||
kwargs['reply_handler'] = callback
|
||||
kwargs['error_handler'] = error_handler
|
||||
return self.db_core_iface.setParam(name, value, category, security_limit, profile_key, **kwargs)
|
||||
|
||||
def setPresence(self, to_jid='', show='', statuses={}, profile_key="@DEFAULT@"):
|
||||
return self.db_core_iface.setPresence(to_jid, show, statuses, profile_key)
|
||||
def setPresence(self, to_jid='', show='', statuses={}, profile_key="@DEFAULT@", callback=None, errback=None):
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
kwargs={}
|
||||
if callback is not None:
|
||||
kwargs['timeout'] = const_TIMEOUT
|
||||
kwargs['reply_handler'] = callback
|
||||
kwargs['error_handler'] = error_handler
|
||||
return self.db_core_iface.setPresence(to_jid, show, statuses, profile_key, **kwargs)
|
||||
|
||||
def subscription(self, sub_type, entity, profile_key="@DEFAULT@"):
|
||||
return self.db_core_iface.subscription(sub_type, entity, profile_key)
|
||||
def subscription(self, sub_type, entity, profile_key="@DEFAULT@", callback=None, errback=None):
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
kwargs={}
|
||||
if callback is not None:
|
||||
kwargs['timeout'] = const_TIMEOUT
|
||||
kwargs['reply_handler'] = callback
|
||||
kwargs['error_handler'] = error_handler
|
||||
return self.db_core_iface.subscription(sub_type, entity, profile_key, **kwargs)
|
||||
|
||||
def updateContact(self, entity_jid, name, groups, profile_key="@DEFAULT@"):
|
||||
return self.db_core_iface.updateContact(entity_jid, name, groups, profile_key)
|
||||
def updateContact(self, entity_jid, name, groups, profile_key="@DEFAULT@", callback=None, errback=None):
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
else:
|
||||
if errback is None:
|
||||
errback = log.error
|
||||
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
|
||||
return self.db_core_iface.updateContact(entity_jid, name, groups, profile_key, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler)
|
||||
|
|
|
|||
|
|
@ -1,80 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# generic module for SàT frontends
|
||||
# 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 import constants
|
||||
from sat.core.i18n import _, D_
|
||||
|
||||
try:
|
||||
from collections import OrderedDict # only available from python 2.7
|
||||
except ImportError:
|
||||
try:
|
||||
from ordereddict import OrderedDict
|
||||
except ImportError:
|
||||
pass # libervia can not import external libraries
|
||||
|
||||
|
||||
def getPresence():
|
||||
"""We cannot do it directly in the Const class, if it is not encapsulated
|
||||
in a method we get a JS runtime SyntaxError: "missing ) in parenthetical".
|
||||
# TODO: merge this definition with those in primitivus.constants and wix.constants
|
||||
"""
|
||||
try:
|
||||
presence = OrderedDict([("", _("Online")),
|
||||
("chat", _("Free for chat")),
|
||||
("away", _("Away from keyboard")),
|
||||
("dnd", _("Do not disturb")),
|
||||
("xa", _("Extended away"))])
|
||||
except TypeError:
|
||||
presence = {"": _("Online"),
|
||||
"chat": _("Free for chat"),
|
||||
"away": _("Away from keyboard"),
|
||||
"dnd": _("Do not disturb"),
|
||||
"xa": _("Extended away")
|
||||
}
|
||||
return presence
|
||||
|
||||
|
||||
class Const(constants.Const):
|
||||
|
||||
PRESENCE = getPresence()
|
||||
|
||||
# from plugin_misc_text_syntaxes
|
||||
SYNTAX_XHTML = "XHTML"
|
||||
SYNTAX_CURRENT = "@CURRENT@"
|
||||
SYNTAX_TEXT = "text"
|
||||
|
||||
# XMLUI
|
||||
SAT_FORM_PREFIX = "SAT_FORM_"
|
||||
SAT_PARAM_SEPARATOR = "_XMLUI_PARAM_" # used to have unique elements names
|
||||
XMLUI_STATUS_VALIDATED = "validated"
|
||||
XMLUI_STATUS_CANCELLED = constants.Const.XMLUI_DATA_CANCELLED
|
||||
|
||||
# MUC
|
||||
ALL_OCCUPANTS = 1
|
||||
MUC_USER_STATES = {
|
||||
"active": u'✔',
|
||||
"inactive": u'☄',
|
||||
"gone": u'✈',
|
||||
"composing": u'✎',
|
||||
"paused": u"⦷"
|
||||
}
|
||||
|
||||
# Roster
|
||||
GROUP_NOT_IN_ROSTER = D_('Not in roster')
|
||||
|
|
@ -245,11 +245,11 @@ class Jp(object):
|
|||
callback()
|
||||
|
||||
def get_full_jid(self, param_jid):
|
||||
"""Return the full jid if possible (add last resource when find a bare jid)"""
|
||||
"""Return the full jid if possible (add main resource when find a bare jid)"""
|
||||
_jid = JID(param_jid)
|
||||
if not _jid.resource:
|
||||
#if the resource is not given, we try to add the last known resource
|
||||
last_resource = self.bridge.getLastResource(param_jid, self.profile)
|
||||
last_resource = self.bridge.getMainResource(param_jid, self.profile)
|
||||
if last_resource:
|
||||
return "%s/%s" % (_jid.bare, last_resource)
|
||||
return param_jid
|
||||
|
|
|
|||
|
|
@ -23,13 +23,15 @@ log = logging.getLogger(__name__)
|
|||
import urwid
|
||||
from urwid_satext import sat_widgets
|
||||
from urwid_satext.files_management import FileDialog
|
||||
from sat_frontends.quick_frontend import quick_widgets
|
||||
from sat_frontends.quick_frontend.quick_chat import QuickChat
|
||||
from sat_frontends.primitivus.card_game import CardGame
|
||||
from sat_frontends.quick_frontend.quick_utils import escapePrivate, unescapePrivate
|
||||
from sat_frontends.quick_frontend import quick_games
|
||||
from sat_frontends.primitivus import game_tarot
|
||||
from sat_frontends.primitivus.constants import Const as C
|
||||
from sat_frontends.primitivus.keys import action_key_map as a_key
|
||||
from sat_frontends.primitivus.widget import PrimitivusWidget
|
||||
import time
|
||||
from sat_frontends.tools.jid import JID
|
||||
from sat_frontends.tools import jid
|
||||
|
||||
|
||||
class ChatText(urwid.FlowWidget):
|
||||
|
|
@ -80,19 +82,27 @@ class ChatText(urwid.FlowWidget):
|
|||
return txt_widget
|
||||
|
||||
|
||||
class Chat(urwid.WidgetWrap, QuickChat):
|
||||
class Chat(PrimitivusWidget, QuickChat):
|
||||
|
||||
def __init__(self, target, host, type_='one2one'):
|
||||
self.target = target
|
||||
QuickChat.__init__(self, target, host, type_)
|
||||
def __init__(self, host, target, type_=C.CHAT_ONE2ONE, profiles=None):
|
||||
QuickChat.__init__(self, host, target, type_, profiles=profiles)
|
||||
self.content = urwid.SimpleListWalker([])
|
||||
self.text_list = urwid.ListBox(self.content)
|
||||
self.chat_widget = urwid.Frame(self.text_list)
|
||||
self.chat_colums = urwid.Columns([('weight', 8, self.chat_widget)])
|
||||
self.chat_colums = urwid.Columns([('weight', 8, self.chat_widget)])
|
||||
self.pile = urwid.Pile([self.chat_colums])
|
||||
urwid.WidgetWrap.__init__(self, self.__getDecoration(self.pile))
|
||||
self.setType(type_)
|
||||
PrimitivusWidget.__init__(self, self.pile, self.target)
|
||||
|
||||
# we must adapt the behaviour with the type
|
||||
if type_ == C.CHAT_ONE2ONE:
|
||||
self.historyPrint(profile=self.profile)
|
||||
elif type_ == C.CHAT_GROUP:
|
||||
if len(self.chat_colums.contents) == 1:
|
||||
present_widget = self._buildPresentList()
|
||||
self.present_panel = sat_widgets.VerticalSeparator(present_widget)
|
||||
self._appendPresentPanel()
|
||||
|
||||
self.day_change = time.strptime(time.strftime("%a %b %d 00:00:00 %Y")) #struct_time of day changing time
|
||||
self.show_timestamp = True
|
||||
self.show_short_nick = False
|
||||
|
|
@ -101,12 +111,12 @@ class Chat(urwid.WidgetWrap, QuickChat):
|
|||
|
||||
def keypress(self, size, key):
|
||||
if key == a_key['OCCUPANTS_HIDE']: #user wants to (un)hide the presents panel
|
||||
if self.type == 'group':
|
||||
if self.type == C.CHAT_GROUP:
|
||||
widgets = [widget for (widget, options) in self.chat_colums.contents]
|
||||
if self.present_panel in widgets:
|
||||
self.__removePresentPanel()
|
||||
self._removePresentPanel()
|
||||
else:
|
||||
self.__appendPresentPanel()
|
||||
self._appendPresentPanel()
|
||||
elif key == a_key['TIMESTAMP_HIDE']: #user wants to (un)hide timestamp
|
||||
self.show_timestamp = not self.show_timestamp
|
||||
for wid in self.content:
|
||||
|
|
@ -115,10 +125,6 @@ class Chat(urwid.WidgetWrap, QuickChat):
|
|||
self.show_short_nick = not self.show_short_nick
|
||||
for wid in self.content:
|
||||
wid._invalidate()
|
||||
elif key == a_key['DECORATION_HIDE']: #user wants to (un)hide widget decoration
|
||||
show = not isinstance(self._w, sat_widgets.LabelLine)
|
||||
self.showDecoration(show)
|
||||
self._invalidate()
|
||||
elif key == a_key['SUBJECT_SWITCH']: #user wants to (un)hide group's subject or change its apperance
|
||||
if self.subject:
|
||||
self.show_title = (self.show_title + 1) % 3
|
||||
|
|
@ -130,67 +136,30 @@ class Chat(urwid.WidgetWrap, QuickChat):
|
|||
self.chat_widget.header = None
|
||||
self._invalidate()
|
||||
|
||||
|
||||
return super(Chat, self).keypress(size, key)
|
||||
|
||||
def getMenu(self):
|
||||
"""Return Menu bar"""
|
||||
menu = sat_widgets.Menu(self.host.loop)
|
||||
if self.type == 'group':
|
||||
if self.type == C.CHAT_GROUP:
|
||||
self.host.addMenus(menu, C.MENU_ROOM, {'room_jid': self.target.bare})
|
||||
game = _("Game")
|
||||
menu.addMenu(game, "Tarot", self.onTarotRequest)
|
||||
elif self.type == 'one2one':
|
||||
self.host.addMenus(menu, C.MENU_SINGLE, {'jid': unescapePrivate(self.target)})
|
||||
elif self.type == C.CHAT_ONE2ONE:
|
||||
self.host.addMenus(menu, C.MENU_SINGLE, {'jid': self.target})
|
||||
menu.addMenu(_("Action"), _("Send file"), self.onSendFileRequest)
|
||||
return menu
|
||||
|
||||
def setType(self, type_):
|
||||
QuickChat.setType(self, type_)
|
||||
if type_ == 'one2one':
|
||||
self.historyPrint(profile=self.host.profile)
|
||||
elif type_ == 'group':
|
||||
if len(self.chat_colums.contents) == 1:
|
||||
present_widget = self.__buildPresentList()
|
||||
self.present_panel = sat_widgets.VerticalSeparator(present_widget)
|
||||
self.__appendPresentPanel()
|
||||
|
||||
def __getDecoration(self, widget):
|
||||
return sat_widgets.LabelLine(widget, self.__getSurrendedText())
|
||||
|
||||
def __getSurrendedText(self):
|
||||
"""Get the text to be displayed as the dialog title."""
|
||||
if not hasattr(self, "surrended_text"):
|
||||
self.__setSurrendedText()
|
||||
return self.surrended_text
|
||||
|
||||
def __setSurrendedText(self, state=None):
|
||||
"""Set the text to be displayed as the dialog title
|
||||
@param stat: chat state of the contact
|
||||
"""
|
||||
text = unicode(unescapePrivate(self.target))
|
||||
if state:
|
||||
text += " (" + state + ")"
|
||||
self.surrended_text = sat_widgets.SurroundedText(text)
|
||||
|
||||
def showDecoration(self, show=True):
|
||||
"""Show/Hide the decoration around the chat window"""
|
||||
if show:
|
||||
main_widget = self.__getDecoration(self.pile)
|
||||
else:
|
||||
main_widget = self.pile
|
||||
self._w = main_widget
|
||||
|
||||
def updateChatState(self, state, nick=None):
|
||||
"""Set the chat state (XEP-0085) of the contact. Leave nick to None
|
||||
to set the state for a one2one conversation, or give a nickname or
|
||||
C.ALL_OCCUPANTS to set the state of a participant within a MUC.
|
||||
@param state: the new chat state
|
||||
@param nick: None for one2one, the MUC user nick or C.ALL_OCCUPANTS
|
||||
"""
|
||||
if nick:
|
||||
assert(self.type == 'group')
|
||||
occupants = self.occupants if nick == C.ALL_OCCUPANTS else [nick]
|
||||
def updateChatState(self, from_jid, state):
|
||||
if self.type == C.CHAT_GROUP:
|
||||
if from_jid == C.ENTITY_ALL:
|
||||
occupants = self.occupants
|
||||
else:
|
||||
nick = from_jid.resource
|
||||
if not nick:
|
||||
log.debug("no nick found for chatstate")
|
||||
return
|
||||
occupants = [nick]
|
||||
options = self.present_wid.getAllValues()
|
||||
for index in xrange(0, len(options)):
|
||||
nick = options[index].value
|
||||
|
|
@ -199,46 +168,51 @@ class Chat(urwid.WidgetWrap, QuickChat):
|
|||
self.present_wid.changeValues(options)
|
||||
self.host.redraw()
|
||||
else:
|
||||
assert(self.type == 'one2one')
|
||||
self.__setSurrendedText(state)
|
||||
self.showDecoration()
|
||||
self.host.redraw()
|
||||
self.title_dynamic = '({})'.format(state)
|
||||
|
||||
def _presentClicked(self, list_wid, clicked_wid):
|
||||
assert(self.type == 'group')
|
||||
assert self.type == C.CHAT_GROUP
|
||||
nick = clicked_wid.getValue().value
|
||||
if nick == self.getUserNick():
|
||||
#We ignore click on our own nick
|
||||
#We ignore clicks on our own nick
|
||||
return
|
||||
#we have a click on a nick, we add the private conversation to the contact_list
|
||||
full_jid = JID("%s/%s" % (self.target.bare, nick))
|
||||
new_jid = escapePrivate(full_jid)
|
||||
if new_jid not in self.host.contact_list:
|
||||
self.host.contact_list.add(new_jid, [C.GROUP_NOT_IN_ROSTER])
|
||||
contact_list = self.host.contact_lists[self.profile]
|
||||
full_jid = jid.JID("%s/%s" % (self.target.bare, nick))
|
||||
|
||||
#we have a click on a nick, we need to create the widget if it doesn't exists
|
||||
self.getOrCreatePrivateWidget(full_jid)
|
||||
|
||||
#now we select the new window
|
||||
self.host.contact_list.setFocus(full_jid, True)
|
||||
contact_list.setFocus(full_jid, True)
|
||||
|
||||
def __buildPresentList(self):
|
||||
def _buildPresentList(self):
|
||||
self.present_wid = sat_widgets.GenericList([],option_type = sat_widgets.ClickableText, on_click=self._presentClicked)
|
||||
return self.present_wid
|
||||
|
||||
def __appendPresentPanel(self):
|
||||
def _appendPresentPanel(self):
|
||||
self.chat_colums.contents.append((self.present_panel,('weight', 2, False)))
|
||||
|
||||
def __removePresentPanel(self):
|
||||
def _removePresentPanel(self):
|
||||
for widget, options in self.chat_colums.contents:
|
||||
if widget is self.present_panel:
|
||||
self.chat_colums.contents.remove((widget, options))
|
||||
break
|
||||
|
||||
def __appendGamePanel(self, widget):
|
||||
def addGamePanel(self, widget):
|
||||
"""Insert a game panel to this Chat dialog.
|
||||
|
||||
@param widget (Widget): the game panel
|
||||
"""
|
||||
assert (len(self.pile.contents) == 1)
|
||||
self.pile.contents.insert(0,(widget,('weight', 1)))
|
||||
self.pile.contents.insert(1,(urwid.Filler(urwid.Divider('-'),('fixed', 1))))
|
||||
self.pile.contents.insert(0, (widget, ('weight', 1)))
|
||||
self.pile.contents.insert(1, (urwid.Filler(urwid.Divider('-'), ('fixed', 1))))
|
||||
self.host.redraw()
|
||||
|
||||
def __removeGamePanel(self):
|
||||
def removeGamePanel(self, widget):
|
||||
"""Remove the game panel from this Chat dialog.
|
||||
|
||||
@param widget (Widget): the game panel
|
||||
"""
|
||||
assert (len(self.pile.contents) == 3)
|
||||
del self.pile.contents[0]
|
||||
self.host.redraw()
|
||||
|
|
@ -252,12 +226,11 @@ class Chat(urwid.WidgetWrap, QuickChat):
|
|||
self.chat_widget.header = urwid.AttrMap(self.subj_wid,'title')
|
||||
self.host.redraw()
|
||||
|
||||
def setPresents(self, param_nicks):
|
||||
"""Set the users presents in the contact list for a group chat
|
||||
@param nicks: list of nicknames
|
||||
def setPresents(self, nicks):
|
||||
"""Set the occupants of a group chat.
|
||||
|
||||
@param nicks (list[unicode]): sorted list of nicknames
|
||||
"""
|
||||
nicks = [unicode(nick) for nick in param_nicks] #FIXME: should be done in DBus bridge
|
||||
nicks.sort()
|
||||
QuickChat.setPresents(self, nicks)
|
||||
self.present_wid.changeValues(nicks)
|
||||
self.host.redraw()
|
||||
|
|
@ -290,13 +263,22 @@ class Chat(urwid.WidgetWrap, QuickChat):
|
|||
self.text_list.focus_position = len(self.content) - 1 # scroll down
|
||||
self.host.redraw()
|
||||
|
||||
def printMessage(self, from_jid, msg, profile, timestamp=None):
|
||||
assert isinstance(from_jid, JID)
|
||||
try:
|
||||
jid, nick, mymess = QuickChat.printMessage(self, from_jid, msg, profile, timestamp)
|
||||
except TypeError:
|
||||
return
|
||||
def onPrivateCreated(self, widget):
|
||||
self.host.contact_lists[widget.profile].specialResourceVisible(widget.target)
|
||||
|
||||
def printMessage(self, from_jid, msg, extra=None, profile=C.PROF_KEY_NONE):
|
||||
assert isinstance(from_jid, jid.JID)
|
||||
if extra is None:
|
||||
extra = {}
|
||||
try:
|
||||
timestamp = float(extra['timestamp'])
|
||||
except KeyError:
|
||||
timestamp=None
|
||||
try:
|
||||
nick, mymess = QuickChat.printMessage(self, from_jid, msg, extra, profile)
|
||||
except TypeError:
|
||||
# None is returned, the message is managed
|
||||
return
|
||||
new_text = ChatText(self, timestamp, nick, mymess, msg)
|
||||
|
||||
if timestamp and self.content:
|
||||
|
|
@ -323,7 +305,7 @@ class Chat(urwid.WidgetWrap, QuickChat):
|
|||
# all messages and not only with the messages coming from the history.
|
||||
self._notify(from_jid, msg)
|
||||
|
||||
def printInfo(self, msg, type_='normal', timestamp=None):
|
||||
def printInfo(self, msg, type_='normal', extra=None):
|
||||
"""Print general info
|
||||
@param msg: message to print
|
||||
@type_: one of:
|
||||
|
|
@ -331,6 +313,12 @@ class Chat(urwid.WidgetWrap, QuickChat):
|
|||
me: "/me" information like "/me clenches his fist" ==> "toto clenches his fist"
|
||||
@param timestamp (float): number of seconds since epoch
|
||||
"""
|
||||
if extra is None:
|
||||
extra = {}
|
||||
try:
|
||||
timestamp = float(extra['timestamp'])
|
||||
except KeyError:
|
||||
timestamp=None
|
||||
_widget = ChatText(self, timestamp, None, False, msg, is_info=True)
|
||||
self.content.append(_widget)
|
||||
self._notify(msg=msg)
|
||||
|
|
@ -348,30 +336,18 @@ class Chat(urwid.WidgetWrap, QuickChat):
|
|||
self.text_list.focus_position = len(self.content) - 1
|
||||
self.host.redraw()
|
||||
if not self.host.x_notify.hasFocus():
|
||||
if self.type == "one2one":
|
||||
if self.type == C.CHAT_ONE2ONE:
|
||||
self.host.x_notify.sendNotification(_("Primitivus: %s is talking to you") % from_jid)
|
||||
elif self.getUserNick().lower() in msg.lower():
|
||||
self.host.x_notify.sendNotification(_("Primitivus: %(user)s mentioned you in room '%(room)s'") % {'user': from_jid, 'room': self.target})
|
||||
|
||||
def startGame(self, game_type, referee, players):
|
||||
"""Configure the chat window to start a game"""
|
||||
if game_type=="Tarot":
|
||||
self.tarot_wid = CardGame(self, referee, players, self.nick)
|
||||
self.__appendGamePanel(self.tarot_wid)
|
||||
|
||||
def getGame(self, game_type):
|
||||
"""Return class managing the game type"""
|
||||
#TODO: check that the game is launched, and manage errors
|
||||
if game_type=="Tarot":
|
||||
return self.tarot_wid
|
||||
|
||||
#MENU EVENTS#
|
||||
def onTarotRequest(self, menu):
|
||||
# TODO: move this to plugin_misc_tarot with dynamic menu
|
||||
if len(self.occupants) != 4:
|
||||
self.host.showPopUp(sat_widgets.Alert(_("Can't start game"), _("You need to be exactly 4 peoples in the room to start a Tarot game"), ok_cb=self.host.removePopUp))
|
||||
else:
|
||||
self.host.bridge.tarotGameCreate(self.id, list(self.occupants), self.host.profile)
|
||||
self.host.bridge.tarotGameCreate(self.id, list(self.occupants), self.profile)
|
||||
|
||||
def onSendFileRequest(self, menu):
|
||||
# TODO: move this to core with dynamic menus
|
||||
|
|
@ -388,11 +364,15 @@ class Chat(urwid.WidgetWrap, QuickChat):
|
|||
self.host.showDialog(_(u"File has a unicode error in its name, it's not yet managed by SàT"), title=_("Can't send file"), type_="error")
|
||||
return
|
||||
#FIXME: check last_resource: what if self.target.resource exists ?
|
||||
last_resource = self.host.bridge.getLastResource(unicode(self.target.bare), self.host.profile)
|
||||
last_resource = self.host.bridge.getMainResource(unicode(self.target.bare), self.profile)
|
||||
if last_resource:
|
||||
full_jid = JID("%s/%s" % (self.target.bare, last_resource))
|
||||
full_jid = jid.JID("%s/%s" % (self.target.bare, last_resource))
|
||||
else:
|
||||
full_jid = self.target
|
||||
progress_id = self.host.bridge.sendFile(full_jid, filepath, {}, self.host.profile)
|
||||
progress_id = self.host.bridge.sendFile(full_jid, filepath, {}, self.profile)
|
||||
self.host.addProgress(progress_id,filepath)
|
||||
self.host.showDialog(_(u"You file request has been sent, we are waiting for your contact answer"), title=_("File request sent"))
|
||||
|
||||
|
||||
quick_widgets.register(QuickChat, Chat)
|
||||
quick_widgets.register(quick_games.Tarot, game_tarot.TarotGame)
|
||||
|
|
|
|||
|
|
@ -68,6 +68,8 @@ class Const(constants.Const):
|
|||
('show_dnd_focus', 'dark red, bold', 'default'),
|
||||
('show_xa', 'dark red', 'default'),
|
||||
('show_xa_focus', 'dark red, bold', 'default'),
|
||||
('resource', 'light blue', 'default'),
|
||||
('resource_main', 'dark blue', 'default'),
|
||||
('status', 'yellow', 'default'),
|
||||
('status_focus', 'yellow, bold', 'default'),
|
||||
('param_selected','default, bold', 'dark red'),
|
||||
|
|
@ -85,3 +87,15 @@ class Const(constants.Const):
|
|||
|
||||
CONFIG_SECTION = APP_NAME.lower()
|
||||
CONFIG_OPT_KEY_PREFIX = "KEY_"
|
||||
|
||||
MENU_ID_MAIN = "MAIN_MENU"
|
||||
MENU_ID_WIDGET = "WIDGET_MENU"
|
||||
|
||||
MODE_NORMAL = 'NORMAL'
|
||||
MODE_INSERTION = 'INSERTION'
|
||||
MODE_COMMAND = 'COMMAND'
|
||||
|
||||
GROUP_DATA_FOLDED = 'folded'
|
||||
|
||||
# contacts and contact list
|
||||
ALERT_HEADER='(*) '
|
||||
|
|
|
|||
|
|
@ -21,36 +21,25 @@ from sat.core.i18n import _
|
|||
import urwid
|
||||
from urwid_satext import sat_widgets
|
||||
from sat_frontends.quick_frontend.quick_contact_list import QuickContactList
|
||||
from sat_frontends.quick_frontend.quick_utils import unescapePrivate
|
||||
from sat_frontends.tools.jid import JID
|
||||
from sat_frontends.primitivus.status import StatusBar
|
||||
from sat_frontends.primitivus.constants import Const as C
|
||||
from sat_frontends.primitivus.keys import action_key_map as a_key
|
||||
from sat_frontends.primitivus.widget import PrimitivusWidget
|
||||
from sat_frontends.tools import jid
|
||||
from sat.core import log as logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ContactList(urwid.WidgetWrap, QuickContactList):
|
||||
class ContactList(PrimitivusWidget, QuickContactList):
|
||||
signals = ['click','change']
|
||||
|
||||
def __init__(self, host, on_click=None, on_change=None, user_data=None):
|
||||
QuickContactList.__init__(self)
|
||||
self.host = host
|
||||
self.selected = None
|
||||
self.groups={}
|
||||
self.alert_jid=set()
|
||||
self.show_status = False
|
||||
self.show_disconnected = False
|
||||
self.show_empty_groups = True
|
||||
# TODO: this may lead to two successive UI refresh and needs an optimization
|
||||
self.host.bridge.asyncGetParamA(C.SHOW_EMPTY_GROUPS, "General", profile_key=host.profile, callback=self.showEmptyGroups)
|
||||
self.host.bridge.asyncGetParamA(C.SHOW_OFFLINE_CONTACTS, "General", profile_key=host.profile, callback=self.showOfflineContacts)
|
||||
def __init__(self, host, on_click=None, on_change=None, user_data=None, profile=None):
|
||||
QuickContactList.__init__(self, host, profile)
|
||||
|
||||
#we now build the widget
|
||||
self.host.status_bar = StatusBar(host)
|
||||
self.frame = sat_widgets.FocusFrame(self.__buildList(), None, self.host.status_bar)
|
||||
self.main_widget = sat_widgets.LabelLine(self.frame, sat_widgets.SurroundedText(_("Contacts")))
|
||||
urwid.WidgetWrap.__init__(self, self.main_widget)
|
||||
self.status_bar = StatusBar(host)
|
||||
self.frame = sat_widgets.FocusFrame(self._buildList(), None, self.status_bar)
|
||||
PrimitivusWidget.__init__(self, self.frame, _(u'Contacts'))
|
||||
if on_click:
|
||||
urwid.connect_signal(self, 'click', on_click, user_data)
|
||||
if on_change:
|
||||
|
|
@ -59,16 +48,14 @@ class ContactList(urwid.WidgetWrap, QuickContactList):
|
|||
def update(self):
|
||||
"""Update display, keep focus"""
|
||||
widget, position = self.frame.body.get_focus()
|
||||
self.frame.body = self.__buildList()
|
||||
self.frame.body = self._buildList()
|
||||
if position:
|
||||
try:
|
||||
self.frame.body.focus_position = position
|
||||
except IndexError:
|
||||
pass
|
||||
self.host.redraw()
|
||||
|
||||
def update_jid(self, jid):
|
||||
self.update()
|
||||
self._invalidate()
|
||||
self.host.redraw() # FIXME: check if can be avoided
|
||||
|
||||
def keypress(self, size, key):
|
||||
# FIXME: we have a temporary behaviour here: FOCUS_SWITCH change focus globally in the parent,
|
||||
|
|
@ -81,18 +68,18 @@ class ContactList(urwid.WidgetWrap, QuickContactList):
|
|||
self.show_status = not self.show_status
|
||||
self.update()
|
||||
elif key == a_key['DISCONNECTED_HIDE']: #user wants to (un)hide disconnected contacts
|
||||
self.host.bridge.setParam(C.SHOW_OFFLINE_CONTACTS, C.str(not self.show_disconnected), "General", profile_key=self.host.profile)
|
||||
self.host.bridge.setParam(C.SHOW_OFFLINE_CONTACTS, C.str(not self.show_disconnected), "General", profile_key=self.profile)
|
||||
elif key == a_key['RESOURCES_HIDE']: #user wants to (un)hide contacts resources
|
||||
self.showResources(not self.show_resources)
|
||||
self.update()
|
||||
return super(ContactList, self).keypress(size, key)
|
||||
|
||||
def __contains__(self, jid):
|
||||
for group in self.groups:
|
||||
if jid.bare in self.groups[group][1]:
|
||||
return True
|
||||
return False
|
||||
# modify the contact list
|
||||
|
||||
def setFocus(self, text, select=False):
|
||||
"""give focus to the first element that matches the given text. You can also
|
||||
pass in text a sat_frontends.tools.jid.JID (it's a subclass of unicode).
|
||||
|
||||
@param text: contact group name, contact or muc userhost, muc private dialog jid
|
||||
@param select: if True, the element is also clicked
|
||||
"""
|
||||
|
|
@ -103,12 +90,8 @@ class ContactList(urwid.WidgetWrap, QuickContactList):
|
|||
# contact group
|
||||
value = widget.getValue()
|
||||
elif isinstance(widget, sat_widgets.SelectableText):
|
||||
if widget.data.startswith(C.PRIVATE_PREFIX):
|
||||
# muc private dialog
|
||||
value = widget.getValue()
|
||||
else:
|
||||
# contact or muc
|
||||
value = widget.data
|
||||
# contact or muc
|
||||
value = widget.data
|
||||
else:
|
||||
# Divider instance
|
||||
continue
|
||||
|
|
@ -116,243 +99,214 @@ class ContactList(urwid.WidgetWrap, QuickContactList):
|
|||
if text.strip() == value.strip():
|
||||
self.frame.body.focus_position = idx
|
||||
if select:
|
||||
self.__contactClicked(widget, True)
|
||||
self._contactClicked(False, widget, True)
|
||||
return
|
||||
except AttributeError:
|
||||
pass
|
||||
idx += 1
|
||||
|
||||
def putAlert(self, jid):
|
||||
"""Put an alert on the jid to get attention from user (e.g. for new message)"""
|
||||
self.alert_jid.add(jid.bare)
|
||||
log.debug(u"Not element found for {} in setFocus".format(text))
|
||||
|
||||
def specialResourceVisible(self, entity):
|
||||
"""Assure a resource of a special entity is visible and clickable
|
||||
|
||||
Mainly used to display private conversation in MUC rooms
|
||||
@param entity: full jid of the resource to show
|
||||
"""
|
||||
assert isinstance(entity, jid.JID)
|
||||
if entity not in self._special_extras:
|
||||
self._special_extras.add(entity)
|
||||
self.update()
|
||||
|
||||
# events
|
||||
|
||||
def _groupClicked(self, group_wid):
|
||||
group = group_wid.getValue()
|
||||
data = self.getGroupData(group)
|
||||
data[C.GROUP_DATA_FOLDED] = not data.setdefault(C.GROUP_DATA_FOLDED, False)
|
||||
self.setFocus(group)
|
||||
self.update()
|
||||
|
||||
def __groupClicked(self, group_wid):
|
||||
group = self.groups[group_wid.getValue()]
|
||||
group[0] = not group[0]
|
||||
self.update()
|
||||
self.setFocus(group_wid.getValue())
|
||||
def _contactClicked(self, use_bare_jid, contact_wid, selected):
|
||||
"""Method called when a contact is clicked
|
||||
|
||||
def __contactClicked(self, contact_wid, selected):
|
||||
self.selected = contact_wid.data
|
||||
for widget in self.frame.body.body:
|
||||
if widget.__class__ == sat_widgets.SelectableText:
|
||||
widget.setState(widget.data == self.selected, invisible=True)
|
||||
if self.selected in self.alert_jid:
|
||||
self.alert_jid.remove(self.selected)
|
||||
self.host.modeHint('INSERTION')
|
||||
@param use_bare_jid: True if use_bare_jid is set in self._buildEntityWidget.
|
||||
If True, all jids in self._alerts with the same bare jid has contact_wid.data will be removed
|
||||
@param contact_wid: widget of the contact, must have the entity set in data attribute
|
||||
@param selected: boolean returned by the widget, telling if it is selected
|
||||
"""
|
||||
entity = contact_wid.data
|
||||
if use_bare_jid:
|
||||
to_remove = set()
|
||||
for alert_entity in self._alerts:
|
||||
if alert_entity.bare == entity.bare:
|
||||
to_remove.add(alert_entity)
|
||||
self._alerts.difference_update(to_remove)
|
||||
else:
|
||||
self._alerts.discard(entity)
|
||||
self.host.modeHint(C.MODE_INSERTION)
|
||||
self.update()
|
||||
self._emit('click')
|
||||
self._emit('click', entity)
|
||||
|
||||
def onPresenceUpdate(self, entity, show, priority, statuses, profile):
|
||||
super(ContactList, self).onPresenceUpdate(entity, show, priority, statuses, profile)
|
||||
self.update()
|
||||
|
||||
def onNickUpdate(self, entity, new_nick, profile):
|
||||
self.update()
|
||||
|
||||
# Methods to build the widget
|
||||
|
||||
def _buildEntityWidget(self, entity, keys=None, use_bare_jid=False, with_alert=True, with_show_attr=True, markup_prepend=None, markup_append = None):
|
||||
"""Build one contact markup data
|
||||
|
||||
@param entity (jid.JID): entity to build
|
||||
@param keys (iterable): value to markup, in preferred order.
|
||||
The first available key will be used.
|
||||
If key starts with "cache_", it will be checked in cache,
|
||||
else, getattr will be done on entity with the key (e.g. getattr(entity, 'node')).
|
||||
If nothing full or keys is None, full entity is used.
|
||||
@param use_bare_jid (bool): if True, use bare jid for alerts and selected comparisons
|
||||
@param with_alert (bool): if True, show alert if entity is in self._alerts
|
||||
@param with_show_attr (bool): if True, show color corresponding to presence status
|
||||
@param markup_prepend (list): markup to prepend to the generated one before building the widget
|
||||
@param markup_append (list): markup to append to the generated one before building the widget
|
||||
@return (list): markup data are expected by Urwid text widgets
|
||||
"""
|
||||
markup = []
|
||||
if use_bare_jid:
|
||||
alerts = {entity.bare for entity in self._alerts}
|
||||
selected = {entity.bare for entity in self._selected}
|
||||
else:
|
||||
alerts = self._alerts
|
||||
selected = self._selected
|
||||
if keys is None:
|
||||
entity_txt = entity
|
||||
else:
|
||||
cache = self.getCache(entity)
|
||||
for key in keys:
|
||||
if key.startswith('cache_'):
|
||||
entity_txt = cache.get(key[6:])
|
||||
else:
|
||||
entity_txt = getattr(entity, key)
|
||||
if entity_txt:
|
||||
break
|
||||
if not entity_txt:
|
||||
entity_txt = entity
|
||||
|
||||
if with_show_attr:
|
||||
show = self.getCache(entity, C.PRESENCE_SHOW)
|
||||
if show is None:
|
||||
show = C.PRESENCE_UNAVAILABLE
|
||||
show_icon, entity_attr = C.PRESENCE.get(show, ('', 'default'))
|
||||
markup.insert(0, u"{} ".format(show_icon))
|
||||
else:
|
||||
entity_attr = 'default'
|
||||
|
||||
if with_alert and entity in alerts:
|
||||
entity_attr = 'alert'
|
||||
header = C.ALERT_HEADER
|
||||
else:
|
||||
header = ''
|
||||
|
||||
markup.append((entity_attr, entity_txt))
|
||||
if markup_prepend:
|
||||
markup.insert(0, markup_prepend)
|
||||
if markup_append:
|
||||
markup.extend(markup_append)
|
||||
|
||||
widget = sat_widgets.SelectableText(markup,
|
||||
selected = entity in selected,
|
||||
header = header)
|
||||
widget.data = entity
|
||||
widget.comp = entity_txt.lower() # value to use for sorting
|
||||
urwid.connect_signal(widget, 'change', self._contactClicked, user_args=[use_bare_jid])
|
||||
return widget
|
||||
|
||||
def _buildEntities(self, content, entities):
|
||||
"""Add entity representation in widget list
|
||||
|
||||
def __buildContact(self, content, contacts):
|
||||
"""Add contact representation in widget list
|
||||
@param content: widget list, e.g. SimpleListWalker
|
||||
@param contacts (list): list of JID userhosts"""
|
||||
if not contacts:
|
||||
@param entities (iterable): iterable of JID to display
|
||||
"""
|
||||
if not entities:
|
||||
return
|
||||
widgets = [] # list of built widgets
|
||||
|
||||
for contact in contacts:
|
||||
if contact.startswith(C.PRIVATE_PREFIX):
|
||||
contact_disp = ('alert' if contact in self.alert_jid else "show_normal", unescapePrivate(contact))
|
||||
show_icon = ''
|
||||
status = ''
|
||||
for entity in entities:
|
||||
if entity in self._specials or not self.entityToShow(entity):
|
||||
continue
|
||||
markup_extra = []
|
||||
if self.show_resources:
|
||||
for resource in self.getCache(entity, C.CONTACT_RESOURCES):
|
||||
resource_disp = ('resource_main' if resource == self.getCache(entity, C.CONTACT_MAIN_RESOURCE) else 'resource', "\n " + resource)
|
||||
markup_extra.append(resource_disp)
|
||||
if self.show_status:
|
||||
status = self.getCache(jid.JID('%s/%s' % (entity, resource)), 'status')
|
||||
status_disp = ('status', "\n " + status) if status else ""
|
||||
markup_extra.append(status_disp)
|
||||
|
||||
|
||||
else:
|
||||
jid = JID(contact)
|
||||
name = self.getCache(jid, 'name')
|
||||
nick = self.getCache(jid, 'nick')
|
||||
status = self.getCache(jid, 'status')
|
||||
show = self.getCache(jid, 'show')
|
||||
if show is None:
|
||||
show = "unavailable"
|
||||
if not self.contactToShow(contact):
|
||||
continue
|
||||
show_icon, show_attr = C.PRESENCE.get(show, ('', 'default'))
|
||||
contact_disp = ('alert' if contact in self.alert_jid else show_attr, nick or name or jid.node or jid.bare)
|
||||
display = [show_icon + " ", contact_disp]
|
||||
if self.show_status:
|
||||
status_disp = ('status', "\n " + status) if status else ""
|
||||
display.append(status_disp)
|
||||
header = '(*) ' if contact in self.alert_jid else ''
|
||||
widget = sat_widgets.SelectableText(display,
|
||||
selected=contact == self.selected,
|
||||
header=header)
|
||||
widget.data = contact
|
||||
widget.comp = contact_disp[1].lower() # value to use for sorting
|
||||
if self.show_status:
|
||||
status = self.getCache(entity, 'status')
|
||||
status_disp = ('status', "\n " + status) if status else ""
|
||||
markup_extra.append(status_disp)
|
||||
widget = self._buildEntityWidget(entity, ('cache_nick', 'cache_name', 'node'), use_bare_jid=True, markup_append=markup_extra)
|
||||
widgets.append(widget)
|
||||
|
||||
widgets.sort(key=lambda widget: widget.comp)
|
||||
|
||||
for widget in widgets:
|
||||
content.append(widget)
|
||||
urwid.connect_signal(widget, 'change', self.__contactClicked)
|
||||
|
||||
def __buildSpecials(self, content):
|
||||
def _buildSpecials(self, content):
|
||||
"""Build the special entities"""
|
||||
specials = self.specials.keys()
|
||||
specials = list(self._specials)
|
||||
specials.sort()
|
||||
for special in specials:
|
||||
jid=JID(special)
|
||||
name = self.getCache(jid, 'name')
|
||||
nick = self.getCache(jid, 'nick')
|
||||
special_disp = ('alert' if special in self.alert_jid else 'default', nick or name or jid.node or jid.bare)
|
||||
display = [ " " , special_disp]
|
||||
header = '(*) ' if special in self.alert_jid else ''
|
||||
widget = sat_widgets.SelectableText(display,
|
||||
selected = special==self.selected,
|
||||
header=header)
|
||||
widget.data = special
|
||||
extra_shown = set()
|
||||
for entity in specials:
|
||||
# the special widgets
|
||||
widget = self._buildEntityWidget(entity, ('cache_nick', 'cache_name', 'node'), with_show_attr=False)
|
||||
content.append(widget)
|
||||
urwid.connect_signal(widget, 'change', self.__contactClicked)
|
||||
|
||||
def __buildList(self):
|
||||
# resources which must be displayed (e.g. MUC private conversations)
|
||||
extras = [extra for extra in self._special_extras if extra.bare == entity.bare]
|
||||
extras.sort()
|
||||
for extra in extras:
|
||||
widget = self._buildEntityWidget(extra, ('resource',), markup_prepend = ' ')
|
||||
content.append(widget)
|
||||
extra_shown.add(extra)
|
||||
|
||||
# entities which must be visible but not resource of current special entities
|
||||
for extra in self._special_extras.difference(extra_shown):
|
||||
widget = self._buildEntityWidget(extra, ('resource',))
|
||||
content.append(widget)
|
||||
|
||||
def _buildList(self):
|
||||
"""Build the main contact list widget"""
|
||||
content = urwid.SimpleListWalker([])
|
||||
|
||||
self.__buildSpecials(content)
|
||||
if self.specials:
|
||||
self._buildSpecials(content)
|
||||
if self._specials:
|
||||
content.append(urwid.Divider('='))
|
||||
|
||||
group_keys = self.groups.keys()
|
||||
group_keys.sort(key=lambda x: x.lower() if x else x)
|
||||
for key in group_keys:
|
||||
unfolded = self.groups[key][0]
|
||||
contacts = list(self.groups[key][1])
|
||||
if key is not None and (self.nonEmptyGroup(contacts) or self.show_empty_groups):
|
||||
header = '[-]' if unfolded else '[+]'
|
||||
widget = sat_widgets.ClickableText(key, header=header + ' ')
|
||||
content.append(widget)
|
||||
urwid.connect_signal(widget, 'click', self.__groupClicked)
|
||||
if unfolded:
|
||||
self.__buildContact(content, contacts)
|
||||
return urwid.ListBox(content)
|
||||
|
||||
def contactToShow(self, contact):
|
||||
"""Tell if the contact should be showed or hidden.
|
||||
|
||||
@param contact (str): JID userhost of the contact
|
||||
@return: True if that contact should be showed in the list"""
|
||||
show = self.getCache(JID(contact), 'show')
|
||||
return (show is not None and show != "unavailable") or \
|
||||
self.show_disconnected or contact in self.alert_jid or contact == self.selected
|
||||
|
||||
def nonEmptyGroup(self, contacts):
|
||||
"""Tell if a contact group contains some contacts to show.
|
||||
|
||||
@param contacts (list[str]): list of JID userhosts
|
||||
@return: bool
|
||||
"""
|
||||
for contact in contacts:
|
||||
if self.contactToShow(contact):
|
||||
return True
|
||||
return False
|
||||
|
||||
def unselectAll(self):
|
||||
"""Unselect all contacts"""
|
||||
self.selected = None
|
||||
for widget in self.frame.body.body:
|
||||
if widget.__class__ == sat_widgets.SelectableText:
|
||||
widget.setState(False, invisible=True)
|
||||
|
||||
def getContact(self):
|
||||
"""Return contact currently selected"""
|
||||
return self.selected
|
||||
|
||||
def clearContacts(self):
|
||||
"""clear all the contact list"""
|
||||
QuickContactList.clearContacts(self)
|
||||
self.groups={}
|
||||
self.selected = None
|
||||
self.unselectAll()
|
||||
self.update()
|
||||
|
||||
def replace(self, jid, groups=None, attributes=None):
|
||||
"""Add a contact to the list if doesn't exist, else update it.
|
||||
|
||||
This method can be called with groups=None for the purpose of updating
|
||||
the contact's attributes (e.g. nickname). In that case, the groups
|
||||
attribute must not be set to the default group but ignored. If not,
|
||||
you may move your contact from its actual group(s) to the default one.
|
||||
|
||||
None value for 'groups' has a different meaning than [None] which is for the default group.
|
||||
|
||||
@param jid (JID)
|
||||
@param groups (list): list of groups or None to ignore the groups membership.
|
||||
@param attributes (dict)
|
||||
"""
|
||||
QuickContactList.replace(self, jid, groups, attributes) # eventually change the nickname
|
||||
if jid.bare in self.specials:
|
||||
return
|
||||
if groups is None:
|
||||
self.update()
|
||||
return
|
||||
assert isinstance(jid, JID)
|
||||
assert isinstance(groups, list)
|
||||
if groups == []:
|
||||
groups = [None] # [None] is the default group
|
||||
for group in [group for group in self.groups if group not in groups]:
|
||||
try: # remove the contact from a previous group
|
||||
self.groups[group][1].remove(jid.bare)
|
||||
except KeyError:
|
||||
pass
|
||||
groups = list(self._groups)
|
||||
groups.sort(key=lambda x: x.lower() if x else x)
|
||||
for group in groups:
|
||||
if group not in self.groups:
|
||||
self.groups[group] = [True, set()] # [unfold, list_of_contacts]
|
||||
self.groups[group][1].add(jid.bare)
|
||||
self.update()
|
||||
data = self.getGroupData(group)
|
||||
folded = data.get(C.GROUP_DATA_FOLDED, False)
|
||||
jids = list(data['jids'])
|
||||
if group is not None and (self.anyEntityToShow(jids) or self.show_empty_groups):
|
||||
header = '[-]' if not folded else '[+]'
|
||||
widget = sat_widgets.ClickableText(group, header=header + ' ')
|
||||
content.append(widget)
|
||||
urwid.connect_signal(widget, 'click', self._groupClicked)
|
||||
if not folded:
|
||||
self._buildEntities(content, jids)
|
||||
not_in_roster = set(self._cache).difference(self._roster).difference(self._specials).difference((self.whoami.bare,))
|
||||
if not_in_roster:
|
||||
content.append(urwid.Divider('-'))
|
||||
self._buildEntities(content, not_in_roster)
|
||||
|
||||
def remove(self, jid):
|
||||
"""remove a contact from the list"""
|
||||
QuickContactList.remove(self, jid)
|
||||
groups_to_remove = []
|
||||
for group in self.groups:
|
||||
contacts = self.groups[group][1]
|
||||
if jid.bare in contacts:
|
||||
contacts.remove(jid.bare)
|
||||
if not len(contacts):
|
||||
groups_to_remove.append(group)
|
||||
for group in groups_to_remove:
|
||||
del self.groups[group]
|
||||
self.update()
|
||||
|
||||
def add(self, jid, param_groups=None):
|
||||
"""add a contact to the list"""
|
||||
self.replace(jid, param_groups if param_groups else [None])
|
||||
|
||||
def setSpecial(self, special_jid, special_type, show=False):
|
||||
"""Set entity as a special
|
||||
@param special_jid: jid of the entity
|
||||
@param special_type: special type (e.g.: "MUC")
|
||||
@param show: True to display the dialog to chat with this entity
|
||||
"""
|
||||
QuickContactList.setSpecial(self, special_jid, special_type, show)
|
||||
if None in self.groups:
|
||||
folded, group_jids = self.groups[None]
|
||||
for group_jid in group_jids:
|
||||
if JID(group_jid).bare == special_jid.bare:
|
||||
group_jids.remove(group_jid)
|
||||
break
|
||||
self.update()
|
||||
if show:
|
||||
# also display the dialog for this room
|
||||
self.setFocus(special_jid, True)
|
||||
self.host.redraw()
|
||||
|
||||
def updatePresence(self, jid, show, priority, statuses):
|
||||
#XXX: for the moment, we ignore presence updates for special entities
|
||||
if jid.bare not in self.specials:
|
||||
QuickContactList.updatePresence(self, jid, show, priority, statuses)
|
||||
|
||||
def showOfflineContacts(self, show):
|
||||
show = C.bool(show)
|
||||
if self.show_disconnected == show:
|
||||
return
|
||||
self.show_disconnected = show
|
||||
self.update()
|
||||
|
||||
def showEmptyGroups(self, show):
|
||||
show = C.bool(show)
|
||||
if self.show_empty_groups == show:
|
||||
return
|
||||
self.show_empty_groups = show
|
||||
self.update()
|
||||
return urwid.ListBox(content)
|
||||
|
|
|
|||
348
frontends/src/primitivus/game_tarot.py
Normal file
348
frontends/src/primitivus/game_tarot.py
Normal file
|
|
@ -0,0 +1,348 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Primitivus: 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 urwid
|
||||
from urwid_satext import sat_widgets
|
||||
from sat_frontends.tools.games import TarotCard
|
||||
from sat_frontends.quick_frontend.quick_game_tarot import QuickTarotGame
|
||||
from sat_frontends.primitivus import xmlui
|
||||
from sat_frontends.primitivus.keys import action_key_map as a_key
|
||||
|
||||
|
||||
class CardDisplayer(urwid.Text):
|
||||
"""Show a card"""
|
||||
signals = ['click']
|
||||
|
||||
def __init__(self, card):
|
||||
self.__selected = False
|
||||
self.card = card
|
||||
urwid.Text.__init__(self, card.getAttrText())
|
||||
|
||||
def selectable(self):
|
||||
return True
|
||||
|
||||
def keypress(self, size, key):
|
||||
if key == a_key['CARD_SELECT']:
|
||||
self.select(not self.__selected)
|
||||
self._emit('click')
|
||||
return key
|
||||
|
||||
def mouse_event(self, size, event, button, x, y, focus):
|
||||
if urwid.is_mouse_event(event) and button == 1:
|
||||
self.select(not self.__selected)
|
||||
self._emit('click')
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def select(self, state=True):
|
||||
self.__selected = state
|
||||
attr, txt = self.card.getAttrText()
|
||||
if self.__selected:
|
||||
attr += '_selected'
|
||||
self.set_text((attr, txt))
|
||||
self._invalidate()
|
||||
|
||||
def isSelected(self):
|
||||
return self.__selected
|
||||
|
||||
def getCard(self):
|
||||
return self.card
|
||||
|
||||
def render(self, size, focus=False):
|
||||
canvas = urwid.CompositeCanvas(urwid.Text.render(self, size, focus))
|
||||
if focus:
|
||||
canvas.set_cursor((0, 0))
|
||||
return canvas
|
||||
|
||||
|
||||
class Hand(urwid.WidgetWrap):
|
||||
"""Used to display several cards, and manage a hand"""
|
||||
signals = ['click']
|
||||
|
||||
def __init__(self, hand=[], selectable=False, on_click=None, user_data=None):
|
||||
"""@param hand: list of Card"""
|
||||
self.__selectable = selectable
|
||||
self.columns = urwid.Columns([], dividechars=1)
|
||||
if on_click:
|
||||
urwid.connect_signal(self, 'click', on_click, user_data)
|
||||
if hand:
|
||||
self.update(hand)
|
||||
urwid.WidgetWrap.__init__(self, self.columns)
|
||||
|
||||
def selectable(self):
|
||||
return self.__selectable
|
||||
|
||||
def keypress(self, size, key):
|
||||
|
||||
if CardDisplayer in [wid.__class__ for wid in self.columns.widget_list]:
|
||||
return self.columns.keypress(size, key)
|
||||
else:
|
||||
#No card displayed, we still have to manage the clicks
|
||||
if key == a_key['CARD_SELECT']:
|
||||
self._emit('click', None)
|
||||
return key
|
||||
|
||||
def getSelected(self):
|
||||
"""Return a list of selected cards"""
|
||||
_selected = []
|
||||
for wid in self.columns.widget_list:
|
||||
if isinstance(wid, CardDisplayer) and wid.isSelected():
|
||||
_selected.append(wid.getCard())
|
||||
return _selected
|
||||
|
||||
def update(self, hand):
|
||||
"""Update the hand displayed in this widget
|
||||
@param hand: list of Card"""
|
||||
try:
|
||||
del self.columns.widget_list[:]
|
||||
del self.columns.column_types[:]
|
||||
except IndexError:
|
||||
pass
|
||||
self.columns.contents.append((urwid.Text(''), ('weight', 1, False)))
|
||||
for card in hand:
|
||||
widget = CardDisplayer(card)
|
||||
self.columns.widget_list.append(widget)
|
||||
self.columns.column_types.append(('fixed', 3))
|
||||
urwid.connect_signal(widget, 'click', self.__onClick)
|
||||
self.columns.contents.append((urwid.Text(''), ('weight', 1, False)))
|
||||
self.columns.focus_position = 1
|
||||
|
||||
def __onClick(self, card_wid):
|
||||
self._emit('click', card_wid)
|
||||
|
||||
|
||||
class Card(TarotCard):
|
||||
"""This class is used to represent a card, logically
|
||||
and give a text representation with attributes"""
|
||||
SIZE = 3 # size of a displayed card
|
||||
|
||||
def __init__(self, suit, value):
|
||||
"""@param file: path of the PNG file"""
|
||||
TarotCard.__init__(self, (suit, value))
|
||||
|
||||
def getAttrText(self):
|
||||
"""return text representation of the card with attributes"""
|
||||
try:
|
||||
value = "%02i" % int(self.value)
|
||||
except ValueError:
|
||||
value = self.value[0].upper() + self.value[1]
|
||||
if self.suit == "atout":
|
||||
if self.value == "excuse":
|
||||
suit = 'c'
|
||||
else:
|
||||
suit = 'A'
|
||||
color = 'neutral'
|
||||
elif self.suit == "pique":
|
||||
suit = u'♠'
|
||||
color = 'black'
|
||||
elif self.suit == "trefle":
|
||||
suit = u'♣'
|
||||
color = 'black'
|
||||
elif self.suit == "coeur":
|
||||
suit = u'♥'
|
||||
color = 'red'
|
||||
elif self.suit == "carreau":
|
||||
suit = u'♦'
|
||||
color = 'red'
|
||||
if self.bout:
|
||||
color = 'special'
|
||||
return ('card_%s' % color, u"%s%s" % (value, suit))
|
||||
|
||||
def getWidget(self):
|
||||
"""Return a widget representing the card"""
|
||||
return CardDisplayer(self)
|
||||
|
||||
|
||||
class Table(urwid.FlowWidget):
|
||||
"""Represent the cards currently on the table"""
|
||||
|
||||
def __init__(self):
|
||||
self.top = self.left = self.bottom = self.right = None
|
||||
|
||||
def putCard(self, location, card):
|
||||
"""Put a card on the table
|
||||
@param location: where to put the card (top, left, bottom or right)
|
||||
@param card: Card to play or None"""
|
||||
assert location in ['top', 'left', 'bottom', 'right']
|
||||
assert isinstance(card, Card) or card == None
|
||||
if [getattr(self, place) for place in ['top', 'left', 'bottom', 'right']].count(None) == 0:
|
||||
#If the table is full of card, we remove them
|
||||
self.top = self.left = self.bottom = self.right = None
|
||||
setattr(self, location, card)
|
||||
self._invalidate()
|
||||
|
||||
def rows(self, size, focus=False):
|
||||
return self.display_widget(size, focus).rows(size, focus)
|
||||
|
||||
def render(self, size, focus=False):
|
||||
return self.display_widget(size, focus).render(size, focus)
|
||||
|
||||
def display_widget(self, size, focus):
|
||||
cards = {}
|
||||
max_col, = size
|
||||
separator = " - "
|
||||
margin = max((max_col - Card.SIZE) / 2, 0) * ' '
|
||||
margin_center = max((max_col - Card.SIZE * 2 - len(separator)) / 2, 0) * ' '
|
||||
for location in ['top', 'left', 'bottom', 'right']:
|
||||
card = getattr(self, location)
|
||||
cards[location] = card.getAttrText() if card else Card.SIZE * ' '
|
||||
render_wid = [urwid.Text([margin, cards['top']]),
|
||||
urwid.Text([margin_center, cards['left'], separator, cards['right']]),
|
||||
urwid.Text([margin, cards['bottom']])]
|
||||
return urwid.Pile(render_wid)
|
||||
|
||||
|
||||
class TarotGame(QuickTarotGame, urwid.WidgetWrap):
|
||||
"""Widget for card games"""
|
||||
|
||||
def __init__(self, parent, referee, players):
|
||||
QuickTarotGame.__init__(self, parent, referee, players)
|
||||
self.loadCards()
|
||||
self.top = urwid.Pile([urwid.Padding(urwid.Text(self.top_nick), 'center')])
|
||||
#self.parent.host.debug()
|
||||
self.table = Table()
|
||||
self.center = urwid.Columns([('fixed', len(self.left_nick), urwid.Filler(urwid.Text(self.left_nick))),
|
||||
urwid.Filler(self.table),
|
||||
('fixed', len(self.right_nick), urwid.Filler(urwid.Text(self.right_nick)))
|
||||
])
|
||||
"""urwid.Pile([urwid.Padding(self.top_card_wid,'center'),
|
||||
urwid.Columns([('fixed',len(self.left_nick),urwid.Text(self.left_nick)),
|
||||
urwid.Padding(self.center_cards_wid,'center'),
|
||||
('fixed',len(self.right_nick),urwid.Text(self.right_nick))
|
||||
]),
|
||||
urwid.Padding(self.bottom_card_wid,'center')
|
||||
])"""
|
||||
self.hand_wid = Hand(selectable=True, on_click=self.onClick)
|
||||
self.main_frame = urwid.Frame(self.center, header=self.top, footer=self.hand_wid, focus_part='footer')
|
||||
urwid.WidgetWrap.__init__(self, self.main_frame)
|
||||
self.parent.host.bridge.tarotGameReady(self.player_nick, referee, self.parent.profile)
|
||||
|
||||
def loadCards(self):
|
||||
"""Load all the cards in memory"""
|
||||
QuickTarotGame.loadCards(self)
|
||||
for value in map(str, range(1, 22)) + ['excuse']:
|
||||
card = Card('atout', value)
|
||||
self.cards[card.suit, card.value] = card
|
||||
self.deck.append(card)
|
||||
for suit in ["pique", "coeur", "carreau", "trefle"]:
|
||||
for value in map(str, range(1, 11)) + ["valet", "cavalier", "dame", "roi"]:
|
||||
card = Card(suit, value)
|
||||
self.cards[card.suit, card.value] = card
|
||||
self.deck.append(card)
|
||||
|
||||
def tarotGameNewHandler(self, hand):
|
||||
"""Start a new game, with given hand"""
|
||||
if hand is []: # reset the display after the scores have been showed
|
||||
self.resetRound()
|
||||
for location in ['top', 'left', 'bottom', 'right']:
|
||||
self.table.putCard(location, None)
|
||||
self.parent.host.redraw()
|
||||
self.parent.host.bridge.tarotGameReady(self.player_nick, self.referee, self.parent.profile)
|
||||
return
|
||||
QuickTarotGame.tarotGameNewHandler(self, hand)
|
||||
self.hand_wid.update(self.hand)
|
||||
self.parent.host.redraw()
|
||||
|
||||
def tarotGameChooseContratHandler(self, xml_data):
|
||||
"""Called when the player has to select his contrat
|
||||
@param xml_data: SàT xml representation of the form"""
|
||||
form = xmlui.create(self.parent.host, xml_data, title=_('Please choose your contrat'), flags=['NO_CANCEL'], profile=self.parent.profile)
|
||||
form.show(valign='top')
|
||||
|
||||
def tarotGameShowCardsHandler(self, game_stage, cards, data):
|
||||
"""Display cards in the middle of the game (to show for e.g. chien ou poignée)"""
|
||||
QuickTarotGame.tarotGameShowCardsHandler(self, game_stage, cards, data)
|
||||
self.center.widget_list[1] = urwid.Filler(Hand(self.to_show))
|
||||
self.parent.host.redraw()
|
||||
|
||||
def tarotGameYourTurnHandler(self):
|
||||
QuickTarotGame.tarotGameYourTurnHandler(self)
|
||||
|
||||
def tarotGameScoreHandler(self, xml_data, winners, loosers):
|
||||
"""Called when the round is over, display the scores
|
||||
@param xml_data: SàT xml representation of the form"""
|
||||
if not winners and not loosers:
|
||||
title = _("Draw game")
|
||||
else:
|
||||
title = _('You win \o/') if self.player_nick in winners else _('You loose :(')
|
||||
form = xmlui.create(self.parent.host, xml_data, title=title, flags=['NO_CANCEL'], profile=self.parent.profile)
|
||||
form.show()
|
||||
|
||||
def tarotGameInvalidCardsHandler(self, phase, played_cards, invalid_cards):
|
||||
"""Invalid cards have been played
|
||||
@param phase: phase of the game
|
||||
@param played_cards: all the cards played
|
||||
@param invalid_cards: cards which are invalid"""
|
||||
QuickTarotGame.tarotGameInvalidCardsHandler(self, phase, played_cards, invalid_cards)
|
||||
self.hand_wid.update(self.hand)
|
||||
if self._autoplay == None: # No dialog if there is autoplay
|
||||
self.parent.host.notify(_('Cards played are invalid !'))
|
||||
self.parent.host.redraw()
|
||||
|
||||
def tarotGameCardsPlayedHandler(self, player, cards):
|
||||
"""A card has been played by player"""
|
||||
QuickTarotGame.tarotGameCardsPlayedHandler(self, player, cards)
|
||||
self.table.putCard(self.getPlayerLocation(player), self.played[player])
|
||||
self._checkState()
|
||||
self.parent.host.redraw()
|
||||
|
||||
def _checkState(self):
|
||||
if isinstance(self.center.widget_list[1].original_widget, Hand): # if we have a hand displayed
|
||||
self.center.widget_list[1] = urwid.Filler(self.table) # we show again the table
|
||||
if self.state == "chien":
|
||||
self.to_show = []
|
||||
self.state = "wait"
|
||||
elif self.state == "wait_for_ecart":
|
||||
self.state = "ecart"
|
||||
self.hand.extend(self.to_show)
|
||||
self.hand.sort()
|
||||
self.to_show = []
|
||||
self.hand_wid.update(self.hand)
|
||||
|
||||
##EVENTS##
|
||||
def onClick(self, hand, card_wid):
|
||||
"""Called when user do an action on the hand"""
|
||||
if not self.state in ['play', 'ecart', 'wait_for_ecart']:
|
||||
#it's not our turn, we ignore the click
|
||||
card_wid.select(False)
|
||||
return
|
||||
self._checkState()
|
||||
if self.state == "ecart":
|
||||
if len(self.hand_wid.getSelected()) == 6:
|
||||
pop_up_widget = sat_widgets.ConfirmDialog(_("Do you put these cards in chien ?"), yes_cb=self.onEcartDone, no_cb=self.parent.host.removePopUp)
|
||||
self.parent.host.showPopUp(pop_up_widget)
|
||||
elif self.state == "play":
|
||||
card = card_wid.getCard()
|
||||
self.parent.host.bridge.tarotGamePlayCards(self.player_nick, self.referee, [(card.suit, card.value)], self.parent.profile)
|
||||
self.hand.remove(card)
|
||||
self.hand_wid.update(self.hand)
|
||||
self.state = "wait"
|
||||
|
||||
def onEcartDone(self, button):
|
||||
"""Called when player has finished his écart"""
|
||||
ecart = []
|
||||
for card in self.hand_wid.getSelected():
|
||||
ecart.append((card.suit, card.value))
|
||||
self.hand.remove(card)
|
||||
self.hand_wid.update(self.hand)
|
||||
self.parent.host.bridge.tarotGamePlayCards(self.player_nick, self.referee, ecart, self.parent.profile)
|
||||
self.state = "wait"
|
||||
self.parent.host.removePopUp()
|
||||
|
|
@ -42,15 +42,18 @@ action_key_map.update(
|
|||
("menu_global", "APP_QUIT"): 'ctrl x',
|
||||
("menu_global", "ROOM_JOIN"): 'meta j',
|
||||
|
||||
# primitivus widgets
|
||||
("primitivus_widget", "DECORATION_HIDE"): "meta l",
|
||||
|
||||
# contact list
|
||||
("contact_list", "STATUS_HIDE"): "meta s",
|
||||
("contact_list", "DISCONNECTED_HIDE"): "meta d",
|
||||
("contact_list", "RESOURCES_HIDE"): "meta r",
|
||||
|
||||
# chat panel
|
||||
("chat_panel", "OCCUPANTS_HIDE"): "meta p",
|
||||
("chat_panel", "TIMESTAMP_HIDE"): "meta t",
|
||||
("chat_panel", "SHORT_NICKNAME"): "meta n",
|
||||
("chat_panel", "DECORATION_HIDE"): "meta l",
|
||||
("chat_panel", "SUBJECT_SWITCH"): "meta s",
|
||||
|
||||
#card game
|
||||
|
|
|
|||
|
|
@ -27,9 +27,10 @@ log = logging.getLogger(__name__)
|
|||
import urwid
|
||||
from urwid_satext import sat_widgets
|
||||
from urwid_satext.files_management import FileDialog
|
||||
from sat_frontends.bridge.DBus import DBusBridgeFrontend
|
||||
from sat_frontends.quick_frontend.quick_app import QuickApp
|
||||
from sat_frontends.quick_frontend.quick_chat_list import QuickChatList
|
||||
from sat_frontends.quick_frontend.quick_utils import getNewPath, unescapePrivate
|
||||
from sat_frontends.quick_frontend import quick_utils
|
||||
from sat_frontends.quick_frontend import quick_chat
|
||||
from sat_frontends.primitivus.profile_manager import ProfileManager
|
||||
from sat_frontends.primitivus.contact_list import ContactList
|
||||
from sat_frontends.primitivus.chat import Chat
|
||||
|
|
@ -39,35 +40,27 @@ from sat_frontends.primitivus.notify import Notify
|
|||
from sat_frontends.primitivus.keys import action_key_map as a_key
|
||||
from sat_frontends.primitivus import config
|
||||
from sat_frontends.tools.misc import InputHistory
|
||||
from sat_frontends.constants import Const as commonConst # FIXME
|
||||
from sat_frontends.tools.jid import JID
|
||||
from sat_frontends.tools import jid
|
||||
from os.path import join
|
||||
import signal
|
||||
|
||||
|
||||
class ChatList(QuickChatList):
|
||||
"""This class manage the list of chat windows"""
|
||||
|
||||
def createChat(self, target):
|
||||
return Chat(target, self.host)
|
||||
|
||||
|
||||
class EditBar(sat_widgets.ModalEdit):
|
||||
"""
|
||||
The modal edit bar where you would enter messages and commands.
|
||||
"""
|
||||
|
||||
def __init__(self, app):
|
||||
modes = {None: ('NORMAL', u''),
|
||||
a_key['MODE_INSERTION']: ('INSERTION', u'> '),
|
||||
a_key['MODE_COMMAND']: ('COMMAND', u':')} #XXX: captions *MUST* be unicode
|
||||
def __init__(self, host):
|
||||
modes = {None: (C.MODE_NORMAL, u''),
|
||||
a_key['MODE_INSERTION']: (C.MODE_INSERTION, u'> '),
|
||||
a_key['MODE_COMMAND']: (C.MODE_COMMAND, u':')} #XXX: captions *MUST* be unicode
|
||||
super(EditBar, self).__init__(modes)
|
||||
self.app = app
|
||||
self.host = host
|
||||
self.setCompletionMethod(self._text_completion)
|
||||
urwid.connect_signal(self, 'click', self.onTextEntered)
|
||||
|
||||
def _text_completion(self, text, completion_data, mode):
|
||||
if mode == 'INSERTION':
|
||||
if mode == C.MODE_INSERTION:
|
||||
return self._nick_completion(text, completion_data)
|
||||
else:
|
||||
return text
|
||||
|
|
@ -75,41 +68,42 @@ class EditBar(sat_widgets.ModalEdit):
|
|||
def _nick_completion(self, text, completion_data):
|
||||
"""Completion method which complete pseudo in group chat
|
||||
for params, see AdvancedEdit"""
|
||||
contact = self.app.contact_list.getContact() ###Based on the fact that there is currently only one contact selectable at once
|
||||
if contact:
|
||||
chat = self.app.chat_wins[contact]
|
||||
if chat.type != "group":
|
||||
return text
|
||||
space = text.rfind(" ")
|
||||
start = text[space+1:]
|
||||
nicks = list(chat.occupants)
|
||||
nicks = []
|
||||
for profile, clist in self.host.contact_lists.iteritems():
|
||||
for contact in clist.getContacts():
|
||||
chat = self.host.widgets.getWidget(quick_chat.QuickChat, contact, profile)
|
||||
if chat.type != C.CHAT_GROUP:
|
||||
continue
|
||||
space = text.rfind(" ")
|
||||
start = text[space + 1:]
|
||||
nicks.extend(list(chat.occupants))
|
||||
if nicks:
|
||||
nicks.sort()
|
||||
try:
|
||||
start_idx=nicks.index(completion_data['last_nick'])+1
|
||||
start_idx = nicks.index(completion_data['last_nick']) + 1
|
||||
if start_idx == len(nicks):
|
||||
start_idx = 0
|
||||
except (KeyError,ValueError):
|
||||
except (KeyError, ValueError):
|
||||
start_idx = 0
|
||||
for idx in range(start_idx,len(nicks)) + range(0,start_idx):
|
||||
for idx in range(start_idx, len(nicks)) + range(0, start_idx):
|
||||
if nicks[idx].lower().startswith(start.lower()):
|
||||
completion_data['last_nick'] = nicks[idx]
|
||||
return text[:space+1] + nicks[idx] + (': ' if space < 0 else '')
|
||||
return text[:space + 1] + nicks[idx] + (': ' if space < 0 else '')
|
||||
return text
|
||||
|
||||
def onTextEntered(self, editBar):
|
||||
"""Called when text is entered in the main edit bar"""
|
||||
if self.mode == 'INSERTION':
|
||||
contact = self.app.contact_list.getContact() ###Based on the fact that there is currently only one contact selectableat once
|
||||
if contact:
|
||||
chat = self.app.chat_wins[contact]
|
||||
self.app.sendMessage(contact,
|
||||
if self.mode == C.MODE_INSERTION:
|
||||
if isinstance(self.host.selected_widget, quick_chat.QuickChat):
|
||||
chat_widget = self.host.selected_widget
|
||||
self.host.sendMessage(chat_widget.target,
|
||||
editBar.get_edit_text(),
|
||||
mess_type = "groupchat" if chat.type == 'group' else "chat",
|
||||
errback=lambda failure: self.app.notify(_("Error while sending message (%s)") % failure),
|
||||
profile_key=self.app.profile
|
||||
mess_type = "groupchat" if chat_widget.type == 'group' else "chat", # TODO: put this in QuickChat
|
||||
errback=lambda failure: self.host.notify(_("Error while sending message ({})").format(failure)),
|
||||
profile_key=chat_widget.profile
|
||||
)
|
||||
editBar.set_edit_text('')
|
||||
elif self.mode == 'COMMAND':
|
||||
elif self.mode == C.MODE_COMMAND:
|
||||
self.commandHandler()
|
||||
|
||||
def commandHandler(self):
|
||||
|
|
@ -118,42 +112,44 @@ class EditBar(sat_widgets.ModalEdit):
|
|||
tokens = self.get_edit_text().split(' ')
|
||||
command, args = tokens[0], tokens[1:]
|
||||
if command == 'quit':
|
||||
self.app.onExit()
|
||||
self.host.onExit()
|
||||
raise urwid.ExitMainLoop()
|
||||
elif command == 'messages':
|
||||
wid = sat_widgets.GenericList(logging.memoryGet())
|
||||
self.app.addWindow(wid)
|
||||
elif command == 'presence':
|
||||
values = [value for value in commonConst.PRESENCE.keys()]
|
||||
values = [value if value else 'online' for value in values] # the empty value actually means 'online'
|
||||
if args and args[0] in values:
|
||||
presence = '' if args[0] == 'online' else args[0]
|
||||
self.app.status_bar.onChange(user_data=sat_widgets.ClickableText(commonConst.PRESENCE[presence]))
|
||||
else:
|
||||
self.app.status_bar.onPresenceClick()
|
||||
elif command == 'status':
|
||||
if args:
|
||||
self.app.status_bar.onChange(user_data=sat_widgets.AdvancedEdit(args[0]))
|
||||
else:
|
||||
self.app.status_bar.onStatusClick()
|
||||
self.host.selectWidget(wid)
|
||||
# elif command == 'presence':
|
||||
# values = [value for value in commonConst.PRESENCE.keys()]
|
||||
# values = [value if value else 'online' for value in values] # the empty value actually means 'online'
|
||||
# if args and args[0] in values:
|
||||
# presence = '' if args[0] == 'online' else args[0]
|
||||
# self.host.status_bar.onChange(user_data=sat_widgets.ClickableText(commonConst.PRESENCE[presence]))
|
||||
# else:
|
||||
# self.host.status_bar.onPresenceClick()
|
||||
# elif command == 'status':
|
||||
# if args:
|
||||
# self.host.status_bar.onChange(user_data=sat_widgets.AdvancedEdit(args[0]))
|
||||
# else:
|
||||
# self.host.status_bar.onStatusClick()
|
||||
elif command == 'history':
|
||||
try:
|
||||
limit = int(args[0])
|
||||
except (IndexError, ValueError):
|
||||
limit = 50
|
||||
win = self.app.chat_wins[JID(self.app.contact_list.selected).bare]
|
||||
win.clearHistory()
|
||||
if limit > 0:
|
||||
win.historyPrint(size=limit, profile=self.app.profile)
|
||||
widget = self.host.selected_widget
|
||||
if isinstance(widget, quick_chat.QuickChat):
|
||||
try:
|
||||
limit = int(args[0])
|
||||
except (IndexError, ValueError):
|
||||
limit = 50
|
||||
widget.clearHistory()
|
||||
if limit > 0:
|
||||
widget.historyPrint(size=limit, profile=widget.profile)
|
||||
elif command == 'search':
|
||||
pattern = " ".join(args)
|
||||
if not pattern:
|
||||
self.app.notif_bar.addMessage(D_("Please specify the globbing pattern to search for"))
|
||||
win = self.app.chat_wins[JID(self.app.contact_list.selected).bare]
|
||||
win.clearHistory()
|
||||
win.printInfo(D_("Results for searching the globbing pattern: %s") % pattern, timestamp=0)
|
||||
win.historyPrint(size=C.HISTORY_LIMIT_NONE, search=pattern, profile=self.app.profile)
|
||||
win.printInfo(D_("Type ':history <lines>' to reset the chat history"))
|
||||
widget = self.host.selected_widget
|
||||
if isinstance(widget, quick_chat.QuickChat):
|
||||
pattern = " ".join(args)
|
||||
if not pattern:
|
||||
self.host.notif_bar.addMessage(D_("Please specify the globbing pattern to search for"))
|
||||
widget.clearHistory()
|
||||
widget.printInfo(D_("Results for searching the globbing pattern: %s") % pattern, timestamp=0)
|
||||
widget.historyPrint(size=C.HISTORY_LIMIT_NONE, search=pattern, profile=widget.profile)
|
||||
widget.printInfo(D_("Type ':history <lines>' to reset the chat history"))
|
||||
else:
|
||||
return
|
||||
self.set_edit_text('')
|
||||
|
|
@ -167,22 +163,24 @@ class EditBar(sat_widgets.ModalEdit):
|
|||
and move the index of the temporary history stack."""
|
||||
if key == a_key['MODAL_ESCAPE']:
|
||||
# first save the text to the current mode, then change to NORMAL
|
||||
self.app._updateInputHistory(self.get_edit_text(), mode=self.mode)
|
||||
self.app._updateInputHistory(mode='NORMAL')
|
||||
if self._mode == 'NORMAL' and key in self._modes:
|
||||
self.app._updateInputHistory(mode=self._modes[key][0])
|
||||
self.host._updateInputHistory(self.get_edit_text(), mode=self.mode)
|
||||
self.host._updateInputHistory(mode=C.MODE_NORMAL)
|
||||
if self._mode == C.MODE_NORMAL and key in self._modes:
|
||||
self.host._updateInputHistory(mode=self._modes[key][0])
|
||||
if key == a_key['HISTORY_PREV']:
|
||||
self.app._updateInputHistory(self.get_edit_text(), -1, self._historyCb, self.mode)
|
||||
self.host._updateInputHistory(self.get_edit_text(), -1, self._historyCb, self.mode)
|
||||
return
|
||||
elif key == a_key['HISTORY_NEXT']:
|
||||
self.app._updateInputHistory(self.get_edit_text(), +1, self._historyCb, self.mode)
|
||||
self.host._updateInputHistory(self.get_edit_text(), +1, self._historyCb, self.mode)
|
||||
return
|
||||
elif key == a_key['EDIT_ENTER']:
|
||||
self.app._updateInputHistory(self.get_edit_text(), mode=self.mode)
|
||||
self.host._updateInputHistory(self.get_edit_text(), mode=self.mode)
|
||||
else:
|
||||
contact = self.app.contact_list.getContact()
|
||||
if contact:
|
||||
self.app.bridge.chatStateComposing(unescapePrivate(contact), self.app.profile)
|
||||
if (self._mode == C.MODE_INSERTION
|
||||
and isinstance(self.host.selected_widget, quick_chat.QuickChat)
|
||||
and key not in sat_widgets.FOCUS_KEYS):
|
||||
self.host.bridge.chatStateComposing(self.host.selected_widget.target, self.host.selected_widget.profile)
|
||||
|
||||
return super(EditBar, self).keypress(size, key)
|
||||
|
||||
|
||||
|
|
@ -281,18 +279,19 @@ class PrimitivusTopWidget(sat_widgets.FocusPile):
|
|||
class PrimitivusApp(QuickApp, InputHistory):
|
||||
|
||||
def __init__(self):
|
||||
QuickApp.__init__(self)
|
||||
QuickApp.__init__(self, create_bridge=DBusBridgeFrontend, check_options=quick_utils.check_options)
|
||||
|
||||
## main loop setup ##
|
||||
self.main_widget = ProfileManager(self)
|
||||
self.loop = urwid.MainLoop(self.main_widget, C.PALETTE, event_loop=urwid.GLibEventLoop(), input_filter=self.inputFilter, unhandled_input=self.keyHandler)
|
||||
|
||||
##misc setup##
|
||||
self.chat_wins = ChatList(self)
|
||||
self._visible_widgets = set()
|
||||
self.notif_bar = sat_widgets.NotificationBar()
|
||||
urwid.connect_signal(self.notif_bar, 'change', self.onNotification)
|
||||
self.progress_wid = Progress(self)
|
||||
urwid.connect_signal(self.notif_bar.progress, 'click', lambda x: self.addWindow(self.progress_wid))
|
||||
|
||||
self.progress_wid = self.widgets.getOrCreateWidget(Progress, None, on_new_widget=None)
|
||||
urwid.connect_signal(self.notif_bar.progress, 'click', lambda x: self.selectWidget(self.progress_wid))
|
||||
self.__saved_overlay = None
|
||||
|
||||
self.x_notify = Notify()
|
||||
|
|
@ -300,6 +299,10 @@ class PrimitivusApp(QuickApp, InputHistory):
|
|||
# we already manage exit with a_key['APP_QUIT'], so we don't want C-c
|
||||
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
||||
|
||||
@property
|
||||
def visible_widgets(self):
|
||||
return self._visible_widgets
|
||||
|
||||
@property
|
||||
def mode(self):
|
||||
return self.editBar.mode
|
||||
|
|
@ -351,7 +354,7 @@ class PrimitivusApp(QuickApp, InputHistory):
|
|||
self._early_popup = popup
|
||||
else:
|
||||
self.showPopUp(popup)
|
||||
super(PrimitivusApp, self).postInit()
|
||||
super(PrimitivusApp, self).postInit(self.main_widget)
|
||||
|
||||
def inputFilter(self, input_, raw):
|
||||
if self.__saved_overlay and input_ != a_key['OVERLAY_HIDE']:
|
||||
|
|
@ -387,14 +390,14 @@ class PrimitivusApp(QuickApp, InputHistory):
|
|||
|
||||
elif input_ == a_key['DEBUG'] and 'D' in self.bridge.getVersion(): #Debug only for dev versions
|
||||
self.debug()
|
||||
elif input_ == a_key['CONTACTS_HIDE']: #user wants to (un)hide the contact_list
|
||||
elif input_ == a_key['CONTACTS_HIDE']: #user wants to (un)hide the contact lists
|
||||
try:
|
||||
for wid, options in self.center_part.contents:
|
||||
if self.contact_list is wid:
|
||||
if self.contact_lists_pile is wid:
|
||||
self.center_part.contents.remove((wid, options))
|
||||
break
|
||||
else:
|
||||
self.center_part.contents.insert(0, (self.contact_list, ('weight', 2, False)))
|
||||
self.center_part.contents.insert(0, (self.contact_lists_pile, ('weight', 2, False)))
|
||||
except AttributeError:
|
||||
#The main widget is not built (probably in Profile Manager)
|
||||
pass
|
||||
|
|
@ -413,17 +416,18 @@ class PrimitivusApp(QuickApp, InputHistory):
|
|||
except AttributeError:
|
||||
return input_
|
||||
|
||||
def addMenus(self, menu, type_, menu_data=None):
|
||||
def addMenus(self, menu, type_filter, menu_data=None):
|
||||
"""Add cached menus to instance
|
||||
@param menu: sat_widgets.Menu instance
|
||||
@param type_: menu type like is sat.core.sat_main.importMenu
|
||||
@param type_filter: menu type like is sat.core.sat_main.importMenu
|
||||
@param menu_data: data to send with these menus
|
||||
|
||||
"""
|
||||
menus = self.profiles[self.profile]['menus'].get(type_,[])
|
||||
def add_menu_cb(callback_id):
|
||||
self.launchAction(callback_id, menu_data, profile_key = self.profile)
|
||||
for id_, path, path_i18n in menus:
|
||||
self.launchAction(callback_id, menu_data, profile=self.current_profile)
|
||||
for id_, type_, path, path_i18n, extra in self.bridge.getMenus("", C.NO_SECURITY_LIMIT ): # TODO: manage extra
|
||||
if type_ != type_filter:
|
||||
continue
|
||||
if len(path) != 2:
|
||||
raise NotImplementedError("Menu with a path != 2 are not implemented yet")
|
||||
menu.addMenu(path_i18n[0], path_i18n[1], lambda dummy,id_=id_: add_menu_cb(id_))
|
||||
|
|
@ -445,23 +449,27 @@ class PrimitivusApp(QuickApp, InputHistory):
|
|||
#FIXME: do this in a more generic way (in quickapp)
|
||||
self.addMenus(menu, C.MENU_GLOBAL)
|
||||
|
||||
menu_roller = sat_widgets.MenuRoller([(_('Main menu'),menu)])
|
||||
menu_roller = sat_widgets.MenuRoller([(_('Main menu'), menu, C.MENU_ID_MAIN)])
|
||||
return menu_roller
|
||||
|
||||
def _buildMainWidget(self):
|
||||
self.contact_list = ContactList(self, on_click=self.contactSelected, on_change=lambda w: self.redraw())
|
||||
#self.center_part = urwid.Columns([('weight',2,self.contact_list),('weight',8,Chat('',self))])
|
||||
self.center_part = urwid.Columns([('weight', 2, self.contact_list), ('weight', 8, urwid.Filler(urwid.Text('')))])
|
||||
self.contact_lists_pile = urwid.Pile([])
|
||||
#self.center_part = urwid.Columns([('weight',2,self.contact_lists[profile]),('weight',8,Chat('',self))])
|
||||
self.center_part = urwid.Columns([('weight', 2, self.contact_lists_pile), ('weight', 8, urwid.Filler(urwid.Text('')))])
|
||||
|
||||
self.editBar = EditBar(self)
|
||||
self.menu_roller = self._buildMenuRoller()
|
||||
self.main_widget = PrimitivusTopWidget(self.center_part, self.menu_roller, self.notif_bar, self.editBar)
|
||||
return self.main_widget
|
||||
|
||||
def plug_profile_1(self, profile_key='@DEFAULT@'):
|
||||
def addContactList(self, profile):
|
||||
contact_list = ContactList(self, on_click=self.contactSelected, on_change=lambda w: self.redraw(), profile=profile)
|
||||
self.contact_lists_pile.contents.append((contact_list, ('weight', 1)))
|
||||
return contact_list
|
||||
|
||||
def plugging_profiles(self):
|
||||
self.loop.widget = self._buildMainWidget()
|
||||
self.redraw()
|
||||
QuickApp.plug_profile_1(self, profile_key)
|
||||
try:
|
||||
# if a popup arrived before main widget is build, we need to show it now
|
||||
self.showPopUp(self._early_popup)
|
||||
|
|
@ -492,21 +500,38 @@ class PrimitivusApp(QuickApp, InputHistory):
|
|||
self.notif_bar.addMessage(message)
|
||||
self.redraw()
|
||||
|
||||
def addWindow(self, widget):
|
||||
"""Display a window if possible,
|
||||
def newWidget(self, widget):
|
||||
self.selectWidget(widget)
|
||||
|
||||
def selectWidget(self, widget):
|
||||
"""Display a widget if possible,
|
||||
|
||||
else add it in the notification bar queue
|
||||
@param widget: BoxWidget"""
|
||||
assert(len(self.center_part.widget_list)<=2)
|
||||
@param widget: BoxWidget
|
||||
"""
|
||||
assert len(self.center_part.widget_list)<=2
|
||||
wid_idx = len(self.center_part.widget_list)-1
|
||||
self.center_part.widget_list[wid_idx] = widget
|
||||
self.menu_roller.removeMenu(_('Chat menu'))
|
||||
self.contact_list.unselectAll()
|
||||
try:
|
||||
self.menu_roller.removeMenu(C.MENU_ID_WIDGET)
|
||||
except KeyError:
|
||||
log.debug("No menu to delete")
|
||||
self.selected_widget = widget
|
||||
self._visible_widgets = set([widget]) # XXX: we can only have one widget visible at the time for now
|
||||
for contact_list in self.contact_lists.itervalues():
|
||||
contact_list.unselectAll()
|
||||
|
||||
for wid in self.visible_widgets:
|
||||
if isinstance(wid, Chat):
|
||||
contact_list = self.contact_lists[wid.profile]
|
||||
contact_list.select(wid.target)
|
||||
|
||||
self.redraw()
|
||||
|
||||
def removeWindow(self):
|
||||
"""Remove window showed on the right column"""
|
||||
#TODO: to a better Window management than this crappy hack
|
||||
assert(len(self.center_part.widget_list)<=2)
|
||||
#TODO: better Window management than this hack
|
||||
assert len(self.center_part.widget_list) <= 2
|
||||
wid_idx = len(self.center_part.widget_list)-1
|
||||
self.center_part.widget_list[wid_idx] = urwid.Filler(urwid.Text(''))
|
||||
self.center_part.focus_position = 0
|
||||
|
|
@ -522,23 +547,14 @@ class PrimitivusApp(QuickApp, InputHistory):
|
|||
"""Set the progression shown in notification bar"""
|
||||
self.notif_bar.setProgress(percentage)
|
||||
|
||||
def contactSelected(self, contact_list):
|
||||
contact = contact_list.getContact()
|
||||
if contact:
|
||||
assert(len(self.center_part.widget_list)==2)
|
||||
self.center_part.widget_list[1] = self.chat_wins[contact]
|
||||
self.menu_roller.addMenu(_('Chat menu'), self.chat_wins[contact].getMenu())
|
||||
|
||||
def newMessageHandler(self, from_jid, to_jid, msg, _type, extra, profile):
|
||||
QuickApp.newMessageHandler(self, from_jid, to_jid, msg, _type, extra, profile)
|
||||
|
||||
if not from_jid in self.contact_list and from_jid.bare != self.profiles[profile]['whoami'].bare:
|
||||
#XXX: needed to show entities which haven't sent any
|
||||
# presence information and which are not in roster
|
||||
self.contact_list.replace(from_jid, [C.GROUP_NOT_IN_ROSTER])
|
||||
|
||||
if self.contact_list.selected is None or JID(self.contact_list.selected).bare != from_jid.bare:
|
||||
self.contact_list.putAlert(from_jid)
|
||||
def contactSelected(self, contact_list, entity):
|
||||
if entity.resource:
|
||||
# we have clicked on a private MUC conversation
|
||||
chat_widget = self.widgets.getOrCreateWidget(Chat, entity, on_new_widget=None, force_hash = Chat.getPrivateHash(contact_list.profile, entity), profile=contact_list.profile)
|
||||
else:
|
||||
chat_widget = self.widgets.getOrCreateWidget(Chat, entity, on_new_widget=None, profile=contact_list.profile)
|
||||
self.selectWidget(chat_widget)
|
||||
self.menu_roller.addMenu(_('Chat menu'), chat_widget.getMenu(), C.MENU_ID_WIDGET)
|
||||
|
||||
def _dialogOkCb(self, widget, data):
|
||||
self.removePopUp()
|
||||
|
|
@ -552,7 +568,6 @@ class PrimitivusApp(QuickApp, InputHistory):
|
|||
answer_data = [data[1]] if data[1] else []
|
||||
answer_cb(False, *answer_data)
|
||||
|
||||
|
||||
def showDialog(self, message, title="", type_="info", answer_cb = None, answer_data = None):
|
||||
if type_ == 'info':
|
||||
popup = sat_widgets.Alert(unicode(title), unicode(message), ok_cb=answer_cb or self.removePopUp) #FIXME: remove unicode here when DBus Bridge will no return dbus.String anymore
|
||||
|
|
@ -578,11 +593,15 @@ class PrimitivusApp(QuickApp, InputHistory):
|
|||
else:
|
||||
self.main_widget.show('notif_bar')
|
||||
|
||||
def launchAction(self, callback_id, data=None, profile_key="@NONE@"):
|
||||
def launchAction(self, callback_id, data=None, callback=None, profile=C.PROF_KEY_NONE):
|
||||
""" Launch a dynamic action
|
||||
@param callback_id: id of the action to launch
|
||||
@param data: data needed only for certain actions
|
||||
@param profile_key: %(doc_profile_key)s
|
||||
@param callback: if not None and 'validated' key is present, it will be called with the following parameters:
|
||||
- callback_id
|
||||
- data
|
||||
- profile
|
||||
@param profile: %(doc_profile)s
|
||||
|
||||
"""
|
||||
if data is None:
|
||||
|
|
@ -593,31 +612,27 @@ class PrimitivusApp(QuickApp, InputHistory):
|
|||
# action was a one shot, nothing to do
|
||||
pass
|
||||
elif "xmlui" in data:
|
||||
ui = xmlui.create(self, xml_data=data['xmlui'])
|
||||
ui = xmlui.create(self, xml_data=data['xmlui'], callback=callback, profile=profile)
|
||||
ui.show()
|
||||
elif "authenticated_profile" in data:
|
||||
assert("caller" in data)
|
||||
if data["caller"] == "profile_manager":
|
||||
assert(isinstance(self.main_widget, ProfileManager))
|
||||
self.main_widget.getXMPPParams(data['authenticated_profile'])
|
||||
elif data["caller"] == "plug_profile":
|
||||
self.plug_profile_1(data['authenticated_profile'])
|
||||
else:
|
||||
raise NotImplementedError
|
||||
elif 'validated' in data:
|
||||
pass # this key is managed below
|
||||
else:
|
||||
self.showPopUp(sat_widgets.Alert(_("Error"), _(u"Unmanaged action result"), ok_cb=self.removePopUp))
|
||||
|
||||
if callback and 'validated' in data:
|
||||
callback(callback_id, data, profile)
|
||||
|
||||
def action_eb(failure):
|
||||
self.showPopUp(sat_widgets.Alert(failure.fullname, failure.message, ok_cb=self.removePopUp))
|
||||
|
||||
self.bridge.launchAction(callback_id, data, profile_key, callback=action_cb, errback=action_eb)
|
||||
self.bridge.launchAction(callback_id, data, profile, callback=action_cb, errback=action_eb)
|
||||
|
||||
def askConfirmationHandler(self, confirmation_id, confirmation_type, data, profile):
|
||||
answer_data={}
|
||||
|
||||
def dir_selected_cb(path):
|
||||
dest_path = join(path, data['filename'])
|
||||
answer_data["dest_path"] = getNewPath(dest_path)
|
||||
answer_data["dest_path"] = quick_utils.getNewPath(dest_path)
|
||||
self.addProgress(confirmation_id, dest_path)
|
||||
accept_cb(None)
|
||||
|
||||
|
|
@ -660,7 +675,7 @@ class PrimitivusApp(QuickApp, InputHistory):
|
|||
title = _('Registration')
|
||||
misc['target'] = data['target']
|
||||
misc['action_back'] = self.bridge.gatewayRegister
|
||||
ui = xmlui.create(self, title=title, xml_data = data['xml'], misc = misc)
|
||||
ui = xmlui.create(self, title=title, xml_data=data['xml'], misc=misc, profile=profile)
|
||||
if data['type'] == 'registration':
|
||||
ui.show('popup')
|
||||
else:
|
||||
|
|
@ -684,32 +699,39 @@ class PrimitivusApp(QuickApp, InputHistory):
|
|||
log.error (_("FIXME FIXME FIXME: type [%s] not implemented") % type_)
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def roomJoinedHandler(self, room_jid_s, room_nicks, user_nick, profile):
|
||||
super(PrimitivusApp, self).roomJoinedHandler(room_jid_s, room_nicks, user_nick, profile)
|
||||
self.contact_lists[profile].setFocus(jid.JID(room_jid_s), True)
|
||||
|
||||
|
||||
|
||||
##DIALOGS CALLBACKS##
|
||||
def onJoinRoom(self, button, edit):
|
||||
self.removePopUp()
|
||||
room_jid = JID(edit.get_edit_text())
|
||||
room_jid = jid.JID(edit.get_edit_text())
|
||||
if room_jid.is_valid():
|
||||
self.bridge.joinMUC(room_jid, self.profiles[self.profile]['whoami'].node, {}, self.profile)
|
||||
self.bridge.joinMUC(room_jid, self.profiles[self.current_profile].whoami.node, {}, self.current_profile)
|
||||
else:
|
||||
message = _("'%s' is an invalid JID !") % room_jid
|
||||
message = _("'%s' is an invalid jid.JID !") % room_jid
|
||||
log.error (message)
|
||||
self.showPopUp(sat_widgets.Alert(_("Error"), message, ok_cb=self.removePopUp))
|
||||
|
||||
#MENU EVENTS#
|
||||
def onConnectRequest(self, menu):
|
||||
QuickApp.asyncConnect(self, self.profile)
|
||||
QuickApp.asyncConnect(self, self.current_profile)
|
||||
|
||||
def onDisconnectRequest(self, menu):
|
||||
self.bridge.disconnect(self.profile)
|
||||
self.disconnect(self.current_profile)
|
||||
|
||||
def onParam(self, menu):
|
||||
def success(params):
|
||||
ui = xmlui.create(self, xml_data=params)
|
||||
ui = xmlui.create(self, xml_data=params, profile=self.current_profile)
|
||||
ui.show()
|
||||
|
||||
def failure(error):
|
||||
self.showPopUp(sat_widgets.Alert(_("Error"), _("Can't get parameters (%s)") % error, ok_cb=self.removePopUp))
|
||||
self.bridge.getParamsUI(app=C.APP_NAME, profile_key=self.profile, callback=success, errback=failure)
|
||||
self.bridge.getParamsUI(app=C.APP_NAME, profile_key=self.current_profile, callback=success, errback=failure)
|
||||
|
||||
def onExitRequest(self, menu):
|
||||
QuickApp.onExit(self)
|
||||
|
|
@ -725,12 +747,12 @@ class PrimitivusApp(QuickApp, InputHistory):
|
|||
|
||||
#MISC CALLBACKS#
|
||||
|
||||
def setStatusOnline(self, online=True, show="", statuses={}):
|
||||
def setStatusOnline(self, online=True, show="", statuses={}, profile=C.PROF_KEY_NONE):
|
||||
if not online or not statuses:
|
||||
self.status_bar.setPresenceStatus(show if online else 'unavailable', '')
|
||||
self.contact_lists[profile].status_bar.setPresenceStatus(show if online else 'unavailable', '')
|
||||
return
|
||||
try:
|
||||
self.status_bar.setPresenceStatus(show, statuses['default'])
|
||||
self.contact_lists[profile].status_bar.setPresenceStatus(show, statuses['default'])
|
||||
except (KeyError, TypeError):
|
||||
pass
|
||||
|
||||
|
|
|
|||
|
|
@ -18,29 +18,28 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from sat.core.i18n import _
|
||||
from sat.core import log as logging
|
||||
log = logging.getLogger(__name__)
|
||||
from sat_frontends.quick_frontend.quick_profile_manager import QuickProfileManager
|
||||
from sat_frontends.primitivus.constants import Const as C
|
||||
import urwid
|
||||
from urwid_satext.sat_widgets import AdvancedEdit, Password, List, InputDialog, ConfirmDialog, Alert
|
||||
from sat_frontends.primitivus.keys import action_key_map as a_key
|
||||
from urwid_satext import sat_widgets
|
||||
import urwid
|
||||
|
||||
|
||||
class ProfileManager(urwid.WidgetWrap):
|
||||
class ProfileManager(QuickProfileManager, urwid.WidgetWrap):
|
||||
|
||||
def __init__(self, host):
|
||||
self.host = host
|
||||
#profiles list
|
||||
profiles = self.host.bridge.getProfilesList()
|
||||
profiles.sort()
|
||||
def __init__(self, host, autoconnect=None):
|
||||
QuickProfileManager.__init__(self, host, autoconnect)
|
||||
|
||||
#login & password box must be created before list because of onProfileChange
|
||||
self.login_wid = AdvancedEdit(_('Login:'), align='center')
|
||||
self.pass_wid = Password(_('Password:'), align='center')
|
||||
self.login_wid = sat_widgets.AdvancedEdit(_('Login:'), align='center')
|
||||
self.pass_wid = sat_widgets.Password(_('Password:'), align='center')
|
||||
|
||||
self.selected_profile = None # allow to reselect the previous selection until the profile is authenticated
|
||||
style = ['single']
|
||||
if self.host.options.profile:
|
||||
style.append('no_first_select')
|
||||
self.list_profile = List(profiles, style=style, align='center', on_change=self.onProfileChange)
|
||||
style = ['no_first_select']
|
||||
profiles = host.bridge.getProfilesList()
|
||||
profiles.sort()
|
||||
self.list_profile = sat_widgets.List(profiles, style=style, align='center', on_change=self.onProfileChange)
|
||||
|
||||
#new & delete buttons
|
||||
buttons = [urwid.Button(_("New"), self.onNewProfile),
|
||||
|
|
@ -51,15 +50,18 @@ class ProfileManager(urwid.WidgetWrap):
|
|||
divider = urwid.Divider('-')
|
||||
|
||||
#connect button
|
||||
connect_button = urwid.Button(_("Connect"), self.onConnectProfile)
|
||||
connect_button = sat_widgets.CustomButton(_("Connect"), self.onConnectProfiles, align='center')
|
||||
|
||||
#we now build the widget
|
||||
list_walker = urwid.SimpleFocusListWalker([buttons_flow,self.list_profile,divider,self.login_wid, self.pass_wid, connect_button])
|
||||
list_walker = urwid.SimpleFocusListWalker([buttons_flow,self.list_profile, divider, self.login_wid, self.pass_wid, connect_button])
|
||||
frame_body = urwid.ListBox(list_walker)
|
||||
frame = urwid.Frame(frame_body,urwid.AttrMap(urwid.Text(_("Profile Manager"),align='center'),'title'))
|
||||
self.main_widget = urwid.LineBox(frame)
|
||||
urwid.WidgetWrap.__init__(self, self.main_widget)
|
||||
|
||||
self.go(autoconnect)
|
||||
|
||||
|
||||
def keypress(self, size, key):
|
||||
if key == a_key['APP_QUIT']:
|
||||
self.host.onExit()
|
||||
|
|
@ -80,117 +82,96 @@ class ProfileManager(urwid.WidgetWrap):
|
|||
return
|
||||
return super(ProfileManager, self).keypress(size, key)
|
||||
|
||||
def __refillProfiles(self):
|
||||
"""Update the list of profiles"""
|
||||
profiles = self.host.bridge.getProfilesList()
|
||||
profiles.sort()
|
||||
self.list_profile.changeValues(profiles)
|
||||
|
||||
def cancelDialog(self, button):
|
||||
self.host.removePopUp()
|
||||
|
||||
def newProfile(self, button, edit):
|
||||
"""Create the profile"""
|
||||
name = edit.get_edit_text()
|
||||
self.host.bridge.asyncCreateProfile(name, callback=lambda: self._newProfileCreated(name), errback=self._profileCreationFailure)
|
||||
self.host.bridge.asyncCreateProfile(name, callback=lambda: self.newProfileCreated(name), errback=self.profileCreationFailure)
|
||||
|
||||
def _newProfileCreated(self, name):
|
||||
self.__refillProfiles()
|
||||
#We select the profile created in the list
|
||||
self.list_profile.selectValue(name)
|
||||
def newProfileCreated(self, profile):
|
||||
self.host.removePopUp()
|
||||
self.refillProfiles()
|
||||
self.list_profile.selectValue(profile)
|
||||
self.current.profile=profile
|
||||
self.getConnectionParams(profile)
|
||||
self.host.redraw()
|
||||
|
||||
def _profileCreationFailure(self, reason):
|
||||
def profileCreationFailure(self, reason):
|
||||
self.host.removePopUp()
|
||||
if reason == "ConflictError":
|
||||
message = _("A profile with this name already exists")
|
||||
elif reason == "CancelError":
|
||||
message = _("Profile creation cancelled by backend")
|
||||
else:
|
||||
message = _("Unknown reason (%s)") % reason
|
||||
popup = Alert(_("Can't create profile"), message, ok_cb=self.host.removePopUp)
|
||||
self.host.showPopUp(popup)
|
||||
message = self._getErrorMessage(reason)
|
||||
self.alert(_("Can't create profile"), message)
|
||||
|
||||
def deleteProfile(self, button):
|
||||
profile_name = self.list_profile.getSelectedValue()
|
||||
if profile_name:
|
||||
self.host.bridge.asyncDeleteProfile(profile_name, callback=self.__refillProfiles)
|
||||
self._deleteProfile()
|
||||
self.host.removePopUp()
|
||||
|
||||
def onNewProfile(self, e):
|
||||
pop_up_widget = InputDialog(_("New profile"), _("Please enter a new profile name"), cancel_cb=self.cancelDialog, ok_cb=self.newProfile)
|
||||
pop_up_widget = sat_widgets.InputDialog(_("New profile"), _("Please enter a new profile name"), cancel_cb=self.cancelDialog, ok_cb=self.newProfile)
|
||||
self.host.showPopUp(pop_up_widget)
|
||||
|
||||
def onDeleteProfile(self, e):
|
||||
pop_up_widget = ConfirmDialog(_("Are you sure you want to delete the profile %s ?") % self.list_profile.getSelectedValue(), no_cb=self.cancelDialog, yes_cb=self.deleteProfile)
|
||||
self.host.showPopUp(pop_up_widget)
|
||||
if self.current.profile:
|
||||
pop_up_widget = sat_widgets.ConfirmDialog(_("Are you sure you want to delete the profile {} ?").format(self.current.profile), no_cb=self.cancelDialog, yes_cb=self.deleteProfile)
|
||||
self.host.showPopUp(pop_up_widget)
|
||||
|
||||
def getXMPPParams(self, profile):
|
||||
"""This is called from PrimitivusApp.launchAction when the profile has been authenticated.
|
||||
def onConnectProfiles(self, button):
|
||||
"""Connect the profiles and start the main widget
|
||||
|
||||
@param profile: %(doc_profile)s
|
||||
@param button: the connect button
|
||||
"""
|
||||
def setJID(jabberID):
|
||||
self.login_wid.set_edit_text(jabberID)
|
||||
self.host.redraw()
|
||||
self._onConnectProfiles()
|
||||
|
||||
def setPassword(password):
|
||||
self.pass_wid.set_edit_text(password)
|
||||
self.host.redraw()
|
||||
def resetFields(self):
|
||||
"""Set profile to None, and reset fields"""
|
||||
super(ProfileManager, self).resetFields()
|
||||
self.list_profile.unselectAll(invisible=True)
|
||||
|
||||
self.list_profile.selectValue(profile, move_focus=False)
|
||||
self.selected_profile = profile
|
||||
self.host.bridge.asyncGetParamA("JabberID", "Connection", profile_key=profile, callback=setJID, errback=self.getParamError)
|
||||
self.host.bridge.asyncGetParamA("Password", "Connection", profile_key=profile, callback=setPassword, errback=self.getParamError)
|
||||
def setProfiles(self, profiles):
|
||||
"""Update the list of profiles"""
|
||||
self.list_profile.changeValues(profiles)
|
||||
self.host.redraw()
|
||||
|
||||
def getProfiles(self):
|
||||
return self.list_profile.getSelectedValues()
|
||||
|
||||
def getJID(self):
|
||||
return self.login_wid.get_edit_text()
|
||||
|
||||
def getPassword(self):
|
||||
return self.pass_wid.get_edit_text()
|
||||
|
||||
def setJID(self, jid_):
|
||||
self.login_wid.set_edit_text(jid_)
|
||||
self.current.login = jid_
|
||||
self.host.redraw() # FIXME: redraw should be avoided
|
||||
|
||||
def setPassword(self, password):
|
||||
self.pass_wid.set_edit_text(password)
|
||||
self.current.password = password
|
||||
self.host.redraw()
|
||||
|
||||
def alert(self, title, message):
|
||||
popup = sat_widgets.Alert(title, message, ok_cb=self.host.removePopUp)
|
||||
self.host.showPopUp(popup)
|
||||
|
||||
def onProfileChange(self, list_wid):
|
||||
"""This is called when a profile is selected in the profile list.
|
||||
|
||||
@param list_wid: the List widget who sent the event
|
||||
"""
|
||||
profile_name = list_wid.getSelectedValue()
|
||||
if not profile_name or profile_name == self.selected_profile:
|
||||
return # avoid infinite loop
|
||||
if self.selected_profile:
|
||||
list_wid.selectValue(self.selected_profile, move_focus=False)
|
||||
else:
|
||||
list_wid.unselectAll(invisible=True)
|
||||
self.host.redraw()
|
||||
self.host.profile = profile_name # FIXME: EXTREMELY DIRTY, needed for sat_frontends.tools.xmlui.XMLUI._xmluiLaunchAction
|
||||
self.host.launchAction(C.AUTHENTICATE_PROFILE_ID, {'caller': 'profile_manager'}, profile_key=profile_name)
|
||||
self.updateConnectionParams()
|
||||
focused = list_wid.focus
|
||||
selected = focused.getState()
|
||||
if not selected: # profile was just unselected
|
||||
return
|
||||
focused.setState(False, invisible=True) # we don't want the widget to be selected until we are sure we can access it
|
||||
def authenticate_cb(callback_id, data, profile):
|
||||
if C.bool(data['validated']):
|
||||
self.current.profile = profile
|
||||
focused.setState(True, invisible=True)
|
||||
self.getConnectionParams(profile)
|
||||
self.host.redraw()
|
||||
self.host.launchAction(C.AUTHENTICATE_PROFILE_ID, callback=authenticate_cb, profile=focused.text)
|
||||
|
||||
def onConnectProfile(self, button):
|
||||
profile_name = self.list_profile.getSelectedValue()
|
||||
assert(profile_name == self.selected_profile) # if not, there's a bug somewhere...
|
||||
if not profile_name:
|
||||
pop_up_widget = Alert(_('No profile selected'), _('You need to create and select a profile before connecting'), ok_cb=self.cancelDialog)
|
||||
self.host.showPopUp(pop_up_widget)
|
||||
elif profile_name[0] == '@':
|
||||
pop_up_widget = Alert(_('Bad profile name'), _("A profile name can't start with a @"), ok_cb=self.cancelDialog)
|
||||
self.host.showPopUp(pop_up_widget)
|
||||
else:
|
||||
profile = self.host.bridge.getProfileName(profile_name)
|
||||
assert(profile)
|
||||
#TODO: move this to quick_app
|
||||
self.host.bridge.asyncGetParamA("JabberID", "Connection", profile_key=profile,
|
||||
callback=lambda old_jid: self.__old_jidReceived(old_jid, profile), errback=self.getParamError)
|
||||
|
||||
def __old_jidReceived(self, old_jid, profile):
|
||||
self.host.bridge.asyncGetParamA("Password", "Connection", profile_key=profile,
|
||||
callback=lambda old_pass: self.__old_passReceived(old_jid, old_pass, profile), errback=self.getParamError)
|
||||
|
||||
def __old_passReceived(self, old_jid, old_pass, profile):
|
||||
"""Check if we have new jid/pass, save them if it is the case, and plug profile"""
|
||||
new_jid = self.login_wid.get_edit_text()
|
||||
new_pass = self.pass_wid.get_edit_text()
|
||||
|
||||
if old_jid != new_jid:
|
||||
self.host.bridge.setParam("JabberID", new_jid, "Connection", profile_key=profile)
|
||||
if old_pass != new_pass:
|
||||
self.host.bridge.setParam("Password", new_pass, "Connection", profile_key=profile)
|
||||
self.host.plug_profile(profile)
|
||||
|
||||
def getParamError(self, ignore):
|
||||
popup = Alert("Error", _("Can't get profile parameter"), ok_cb=self.host.removePopUp)
|
||||
self.host.showPopUp(popup)
|
||||
|
|
|
|||
|
|
@ -20,11 +20,15 @@
|
|||
from sat.core.i18n import _
|
||||
import urwid
|
||||
from urwid_satext import sat_widgets
|
||||
from sat_frontends.quick_frontend import quick_widgets
|
||||
|
||||
|
||||
class Progress(urwid.WidgetWrap):
|
||||
class Progress(urwid.WidgetWrap, quick_widgets.QuickWidget):
|
||||
PROFILES_ALLOW_NONE = True
|
||||
|
||||
def __init__(self, host):
|
||||
def __init__(self, host, target, profiles):
|
||||
assert target is None and profiles is None
|
||||
quick_widgets.QuickWidget.__init__(self, host, target)
|
||||
self.host = host
|
||||
self.progress_list = urwid.SimpleListWalker([])
|
||||
self.progress_dict = {}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
from sat.core.i18n import _
|
||||
import urwid
|
||||
from urwid_satext import sat_widgets
|
||||
from sat_frontends.constants import Const as commonConst
|
||||
from sat_frontends.quick_frontend.constants import Const as commonConst
|
||||
from sat_frontends.primitivus.constants import Const
|
||||
|
||||
|
||||
|
|
@ -39,7 +39,7 @@ class StatusBar(urwid.Columns):
|
|||
urwid.connect_signal(self.status, 'click', self.onStatusClick)
|
||||
|
||||
def onPresenceClick(self, sender=None):
|
||||
if not self.host.bridge.isConnected(self.host.profile):
|
||||
if not self.host.bridge.isConnected(self.host.current_profile): # FIXME: manage multi-profiles
|
||||
return
|
||||
options = [commonConst.PRESENCE[presence] for presence in commonConst.PRESENCE]
|
||||
list_widget = sat_widgets.GenericList(options=options, option_type=sat_widgets.ClickableText, on_click=self.onChange)
|
||||
|
|
@ -47,7 +47,7 @@ class StatusBar(urwid.Columns):
|
|||
self.host.showPopUp(decorated)
|
||||
|
||||
def onStatusClick(self, sender=None):
|
||||
if not self.host.bridge.isConnected(self.host.profile):
|
||||
if not self.host.bridge.isConnected(self.host.current_profile): # FIXME: manage multi-profiles
|
||||
return
|
||||
pop_up_widget = sat_widgets.InputDialog(_('Set your status'), _('New status'), default_txt=self.status.get_text(),
|
||||
cancel_cb=self.host.removePopUp, ok_cb=self.onChange)
|
||||
|
|
@ -61,7 +61,8 @@ class StatusBar(urwid.Columns):
|
|||
elif isinstance(user_data, sat_widgets.AdvancedEdit):
|
||||
new = (previous[0], new_value[0])
|
||||
if new != previous:
|
||||
self.host.bridge.setPresence(show=new[0], statuses={'default': new[1]}, profile_key=self.host.profile) #FIXME: manage multilingual statuses
|
||||
for profile in self.host.profiles: # FIXME: for now all the profiles share the same status
|
||||
self.host.bridge.setPresence(show=new[0], statuses={'default': new[1]}, profile_key=profile) # FIXME: manage multilingual statuses
|
||||
self.setPresenceStatus(new[0], new[1])
|
||||
self.host.removePopUp()
|
||||
|
||||
|
|
|
|||
102
frontends/src/primitivus/widget.py
Normal file
102
frontends/src/primitivus/widget.py
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Primitivus: 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 import log as logging
|
||||
log = logging.getLogger(__name__)
|
||||
import urwid
|
||||
from urwid_satext import sat_widgets
|
||||
from sat_frontends.primitivus.keys import action_key_map as a_key
|
||||
|
||||
|
||||
class PrimitivusWidget(urwid.WidgetWrap):
|
||||
"""Base widget for Primitivus"""
|
||||
|
||||
def __init__(self, w, title=''):
|
||||
self._title = title
|
||||
self._title_dynamic = None
|
||||
self._original_widget = w
|
||||
urwid.WidgetWrap.__init__(self, self._getDecoration(w))
|
||||
|
||||
@property
|
||||
def title(self):
|
||||
"""Text shown in title bar of the widget"""
|
||||
|
||||
# profiles currently managed by frontend
|
||||
try:
|
||||
all_profiles = self.host.profiles
|
||||
except AttributeError:
|
||||
all_profiles = []
|
||||
|
||||
# profiles managed by the widget
|
||||
try:
|
||||
profiles = self.profiles
|
||||
except AttributeError:
|
||||
try:
|
||||
profiles = [self.profile]
|
||||
except AttributeError:
|
||||
profiles = []
|
||||
|
||||
title_elts = []
|
||||
if self._title:
|
||||
title_elts.append(self._title)
|
||||
if self._title_dynamic:
|
||||
title_elts.append(self._title_dynamic)
|
||||
if len(all_profiles)>1 and profiles:
|
||||
title_elts.append(u'[{}]'.format(u', '.join(profiles)))
|
||||
return sat_widgets.SurroundedText(u' '.join(title_elts))
|
||||
|
||||
@title.setter
|
||||
def title(self, value):
|
||||
self._title = value
|
||||
if self.decorationVisible:
|
||||
self.showDecoration()
|
||||
|
||||
@property
|
||||
def title_dynamic(self):
|
||||
"""Dynamic part of title"""
|
||||
return self._title_dynamic
|
||||
|
||||
@title_dynamic.setter
|
||||
def title_dynamic(self, value):
|
||||
self._title_dynamic = value
|
||||
if self.decorationVisible:
|
||||
self.showDecoration()
|
||||
|
||||
@property
|
||||
def decorationVisible(self):
|
||||
"""True if the decoration is visible"""
|
||||
return isinstance(self._w, sat_widgets.LabelLine)
|
||||
|
||||
|
||||
def keypress(self, size, key):
|
||||
if key == a_key['DECORATION_HIDE']: #user wants to (un)hide widget decoration
|
||||
show = not self.decorationVisible
|
||||
self.showDecoration(show)
|
||||
else:
|
||||
return super(PrimitivusWidget, self).keypress(size, key)
|
||||
|
||||
def _getDecoration(self, widget):
|
||||
return sat_widgets.LabelLine(widget, self.title)
|
||||
|
||||
def showDecoration(self, show=True):
|
||||
"""Show/Hide the decoration around the window"""
|
||||
self._w = self._getDecoration(self._original_widget) if show else self._original_widget
|
||||
|
||||
def getMenu(self):
|
||||
raise NotImplementedError
|
||||
|
|
@ -26,6 +26,7 @@ from urwid_satext import files_management
|
|||
from sat.core.log import getLogger
|
||||
log = getLogger(__name__)
|
||||
from sat_frontends.primitivus.constants import Const as C
|
||||
from sat_frontends.primitivus.widget import PrimitivusWidget
|
||||
from sat_frontends.tools import xmlui
|
||||
|
||||
|
||||
|
|
@ -355,14 +356,14 @@ class WidgetFactory(GenericFactory):
|
|||
return cls
|
||||
|
||||
|
||||
class XMLUIPanel(xmlui.XMLUIPanel, urwid.WidgetWrap):
|
||||
class XMLUIPanel(xmlui.XMLUIPanel, PrimitivusWidget):
|
||||
widget_factory = WidgetFactory()
|
||||
|
||||
def __init__(self, host, parsed_xml, title = None, flags = None):
|
||||
def __init__(self, host, parsed_xml, title=None, flags=None, callback=None, profile=C.PROF_KEY_NONE):
|
||||
self.widget_factory._xmlui_main = self
|
||||
self._dest = None
|
||||
xmlui.XMLUIPanel.__init__(self, host, parsed_xml, title, flags)
|
||||
urwid.WidgetWrap.__init__(self, self.main_cont)
|
||||
xmlui.XMLUIPanel.__init__(self, host, parsed_xml, title, flags, callback, profile)
|
||||
PrimitivusWidget.__init__(self, self.main_cont, self.xmlui_title)
|
||||
|
||||
def constructUI(self, parsed_dom):
|
||||
def postTreat():
|
||||
|
|
@ -412,11 +413,10 @@ class XMLUIPanel(xmlui.XMLUIPanel, urwid.WidgetWrap):
|
|||
raise ValueError('Invalid show_type [%s]' % show_type)
|
||||
|
||||
self._dest = show_type
|
||||
decorated = sat_widgets.LabelLine(self, sat_widgets.SurroundedText(self.title or ''))
|
||||
if show_type == 'popup':
|
||||
self.host.showPopUp(decorated, valign=valign)
|
||||
self.host.showPopUp(self, valign=valign)
|
||||
elif show_type == 'window':
|
||||
self.host.addWindow(decorated)
|
||||
self.host.newWidget(self)
|
||||
else:
|
||||
assert(False)
|
||||
self.host.redraw()
|
||||
|
|
|
|||
|
|
@ -17,9 +17,58 @@
|
|||
# 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_frontends import constants
|
||||
from sat.core import constants
|
||||
from sat.core.i18n import _
|
||||
from collections import OrderedDict # only available from python 2.7
|
||||
|
||||
|
||||
class Const(constants.Const):
|
||||
|
||||
PRIVATE_PREFIX = "@private@"
|
||||
PRESENCE = OrderedDict([("", _("Online")),
|
||||
("chat", _("Free for chat")),
|
||||
("away", _("Away from keyboard")),
|
||||
("dnd", _("Do not disturb")),
|
||||
("xa", _("Extended away"))])
|
||||
|
||||
# from plugin_misc_text_syntaxes
|
||||
SYNTAX_XHTML = "XHTML"
|
||||
SYNTAX_CURRENT = "@CURRENT@"
|
||||
SYNTAX_TEXT = "text"
|
||||
|
||||
# XMLUI
|
||||
SAT_FORM_PREFIX = "SAT_FORM_"
|
||||
SAT_PARAM_SEPARATOR = "_XMLUI_PARAM_" # used to have unique elements names
|
||||
XMLUI_STATUS_VALIDATED = "validated"
|
||||
XMLUI_STATUS_CANCELLED = constants.Const.XMLUI_DATA_CANCELLED
|
||||
|
||||
# MUC
|
||||
ALL_OCCUPANTS = 1
|
||||
MUC_USER_STATES = {
|
||||
"active": u'✔',
|
||||
"inactive": u'☄',
|
||||
"gone": u'✈',
|
||||
"composing": u'✎',
|
||||
"paused": u"⦷"
|
||||
}
|
||||
|
||||
# Roster
|
||||
CONTACT_GROUPS = 'groups'
|
||||
CONTACT_RESOURCES = 'resources'
|
||||
CONTACT_MAIN_RESOURCE = 'main_resource'
|
||||
CONTACT_SPECIAL = 'special'
|
||||
CONTACT_SPECIAL_GROUP = 'group' # group chat special entity
|
||||
CONTACT_SPECIAL_ALLOWED = (CONTACT_SPECIAL_GROUP,) # set of allowed values for special flag
|
||||
CONTACT_DATA_FORBIDDEN = {CONTACT_GROUPS, CONTACT_RESOURCES, CONTACT_MAIN_RESOURCE} # set of forbidden names for contact data
|
||||
|
||||
# Chats
|
||||
CHAT_ONE2ONE = 'one2one'
|
||||
CHAT_GROUP = 'group'
|
||||
|
||||
# Widgets management
|
||||
# FIXME: should be in quick_frontend.constant, but Libervia doesn't inherit from it
|
||||
WIDGET_NEW = 'NEW'
|
||||
WIDGET_KEEP = 'KEEP'
|
||||
WIDGET_RAISE = 'RAISE'
|
||||
WIDGET_RECREATE = 'RECREATE'
|
||||
|
||||
LISTENERS = {'avatar', 'nick', 'presence', 'profilePlugged', 'disconnect', 'gotMenus', 'menu'}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue