changeset 755:e3ad48a2aab2

core, frontends: callMenu is now async and don't use callback_id anymore
author Goffi <goffi@goffi.org>
date Tue, 24 Dec 2013 15:18:31 +0100
parents f021bf27a557
children efa0e0f57950
files frontends/src/bridge/DBus.py frontends/src/constants.py frontends/src/primitivus/primitivus frontends/src/wix/main_window.py src/bridge/DBus.py src/bridge/bridge_constructor/bridge_template.ini src/core/sat_main.py
diffstat 7 files changed, 93 insertions(+), 56 deletions(-) [+]
line wrap: on
line diff
--- a/frontends/src/bridge/DBus.py	Tue Dec 24 01:31:18 2013 +0100
+++ b/frontends/src/bridge/DBus.py	Tue Dec 24 15:18:31 2013 +0100
@@ -97,6 +97,9 @@
     def addContact(self, entity_jid, profile_key="@DEFAULT@"):
         return self.db_core_iface.addContact(entity_jid, profile_key)
 
+    def asyncCallMenu(self, category, name, menu_type, profile_key, callback=None, errback=None):
+        return unicode(self.db_core_iface.asyncCallMenu(category, name, menu_type, profile_key, reply_handler=callback, error_handler=lambda err:errback(err._dbus_error_name[len(const_ERROR_PREFIX)+1:])))
+
     def asyncConnect(self, profile_key="@DEFAULT@", callback=None, errback=None):
         return self.db_core_iface.asyncConnect(profile_key, reply_handler=callback, error_handler=lambda err:errback(err._dbus_error_name[len(const_ERROR_PREFIX)+1:]))
 
@@ -106,9 +109,6 @@
     def asyncGetParamA(self, name, category, attribute="value", security_limit=-1, profile_key="@DEFAULT@", callback=None, errback=None):
         return unicode(self.db_core_iface.asyncGetParamA(name, category, attribute, security_limit, profile_key, reply_handler=callback, error_handler=lambda err:errback(err._dbus_error_name[len(const_ERROR_PREFIX)+1:])))
 
-    def callMenu(self, category, name, menu_type, profile_key):
-        return unicode(self.db_core_iface.callMenu(category, name, menu_type, profile_key))
-
     def confirmationAnswer(self, id, accepted, data, profile):
         return self.db_core_iface.confirmationAnswer(id, accepted, data, profile)
 
--- a/frontends/src/constants.py	Tue Dec 24 01:31:18 2013 +0100
+++ b/frontends/src/constants.py	Tue Dec 24 15:18:31 2013 +0100
@@ -56,6 +56,8 @@
 class Const(object):
     PRESENCE = getPresence()
 
+    MENU_NORMAL = "NORMAL"
+
     # from plugin_misc_text_syntaxes
     _SYNTAX_XHTML = "XHTML"
     _SYNTAX_CURRENT = "@CURRENT@"
--- a/frontends/src/primitivus/primitivus	Tue Dec 24 01:31:18 2013 +0100
+++ b/frontends/src/primitivus/primitivus	Tue Dec 24 15:18:31 2013 +0100
@@ -281,8 +281,15 @@
         except AttributeError:
             return input
 
+    def _dynamicMenuCb(self, xmlui):
+        misc = {}
+        ui = XMLUI(self, xml_data = xmlui)
+        ui.show('popup')
 
-    def __buildMenuRoller(self):
+    def _dynamicMenuEb(self, failure):
+        self.showDialog(_(u"Error while calling menu"), type="error")
+
+    def _buildMenuRoller(self):
         menu = sat_widgets.Menu(self.loop)
         general = _("General")
         menu.addMenu(general, _("Connect"), self.onConnectRequest)
@@ -301,29 +308,28 @@
         #FIXME: do this in a more generic way (in quickapp)
         add_menus = self.bridge.getMenus()
         def add_menu_cb(menu):
-            category, item = menu
-            id = self.bridge.callMenu(category, item, "NORMAL", self.profile)
-            self.current_action_ids.add(id)
+            category, name = menu
+            self.bridge.asyncCallMenu(category, name, Const.MENU_NORMAL, self.profile, callback=self._dynamicMenuCb, errback=self._dynamicMenuEb)
         for new_menu in add_menus:
-            category,item,type = new_menu
-            assert(type=="NORMAL") #TODO: manage other types
-            menu.addMenu(unicode(category), unicode(item), add_menu_cb)
+            type_, category, name = new_menu
+            assert(type_=="NORMAL") #TODO: manage other types
+            menu.addMenu(unicode(category), unicode(name), add_menu_cb)
 
         menu_roller = sat_widgets.MenuRoller([(_('Main menu'),menu)])
         return menu_roller
 
-    def __buildMainWidget(self):
+    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.editBar = EditBar(self)
-        self.menu_roller = self.__buildMenuRoller()
+        self.menu_roller = self._buildMenuRoller()
         self.main_widget = sat_widgets.FocusFrame(self.center_part, header=self.menu_roller, footer=self.editBar, focus_part='footer')
         return self.main_widget
 
     def plug_profile(self, profile_key='@DEFAULT@'):
-        self.loop.widget = self.__buildMainWidget()
+        self.loop.widget = self._buildMainWidget()
         self.redraw()
         QuickApp.plug_profile(self, profile_key)
 
--- a/frontends/src/wix/main_window.py	Tue Dec 24 01:31:18 2013 +0100
+++ b/frontends/src/wix/main_window.py	Tue Dec 24 15:18:31 2013 +0100
@@ -115,6 +115,17 @@
             self.menuBar.EnableTop(i, True)
         super(MainWindow, self).plug_profile(profile_key)
 
+    def _dynamicMenuCb(self, xmlui):
+        XMLUI(self, xml_data = xmlui)
+
+    def _dynamicMenuEb(self, failure):
+        dlg = wx.MessageDialog(self, _(u"Error while calling menu"),
+                               _('Error'),
+                               wx.OK | wx.ICON_ERROR
+                              )
+        dlg.ShowModal()
+        dlg.Destroy()
+
     def createMenus(self):
         info(_("Creating menus"))
         connectMenu = wx.Menu()
@@ -142,8 +153,8 @@
         #FIXME: do this in a more generic way (in quickapp)
         add_menus = self.bridge.getMenus()
         for menu in add_menus:
-            category,item,type = menu
-            assert(type=="NORMAL") #TODO: manage other types
+            type_,category,name = menu
+            assert(type_=="NORMAL") #TODO: manage other types
             menu_idx = self.menuBar.FindMenu(category)
             current_menu = None
             if menu_idx == wx.NOT_FOUND:
@@ -154,12 +165,11 @@
                 current_menu = self.menuBar.GetMenu(menu_idx)
             assert(current_menu != None)
             item_id = wx.NewId()
-            help_string = self.bridge.getMenuHelp(category, item, type)
-            current_menu.Append(item_id, item, help = help_string)
+            help_string = self.bridge.getMenuHelp(category, name, type_)
+            current_menu.Append(item_id, name, help = help_string)
             #now we register the event
             def event_answer(e):
-                id = self.bridge.callMenu(category, item, type, self.profile)
-                self.current_action_ids.add(id)
+                self.bridge.asyncCallMenu(category, name, Const.MENU_NORMAL, self.profile, callback=self._dynamicMenuCb, errback=self._dynamicMenuEb)
             wx.EVT_MENU(self, item_id, event_answer)
 
 
--- a/src/bridge/DBus.py	Tue Dec 24 01:31:18 2013 +0100
+++ b/src/bridge/DBus.py	Tue Dec 24 15:18:31 2013 +0100
@@ -192,6 +192,12 @@
         return self._callback("addContact", unicode(entity_jid), unicode(profile_key))
 
     @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
+                         in_signature='ssss', out_signature='s',
+                         async_callbacks=('callback', 'errback'))
+    def asyncCallMenu(self, category, name, menu_type, profile_key, callback=None, errback=None):
+        return self._callback("asyncCallMenu", unicode(category), unicode(name), unicode(menu_type), unicode(profile_key), callback=callback, errback=errback)
+
+    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
                          in_signature='s', out_signature='',
                          async_callbacks=('callback', 'errback'))
     def asyncConnect(self, profile_key="@DEFAULT@", callback=None, errback=None):
@@ -210,12 +216,6 @@
         return self._callback("asyncGetParamA", unicode(name), unicode(category), unicode(attribute), security_limit, unicode(profile_key), callback=callback, errback=errback)
 
     @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='ssss', out_signature='s',
-                         async_callbacks=None)
-    def callMenu(self, category, name, menu_type, profile_key):
-        return self._callback("callMenu", unicode(category), unicode(name), unicode(menu_type), unicode(profile_key))
-
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
                          in_signature='sba{ss}s', out_signature='',
                          async_callbacks=None)
     def confirmationAnswer(self, id, accepted, data, profile):
--- a/src/bridge/bridge_constructor/bridge_template.ini	Tue Dec 24 01:31:18 2013 +0100
+++ b/src/bridge/bridge_constructor/bridge_template.ini	Tue Dec 24 15:18:31 2013 +0100
@@ -574,10 +574,10 @@
 sig_out=a(sss)
 doc=Get all additional menus
 doc_return=list of tuple with the following value:
+ - menu_type: Type which can be:
+    * NORMAL: Classical application menu
  - category: Category of the menu
  - name: Name of the menu
- - menu_type: Type which can be:
-    * NORMAL: Classical application menu
 
 [getMenuHelp]
 type=method
@@ -591,7 +591,8 @@
 doc_param_2=menu_type: Type of the menu as in [getMenus] return value
 doc_return=Help string
 
-[callMenu]
+[asyncCallMenu]
+async=
 type=method
 category=core
 sig_in=ssss
@@ -601,4 +602,4 @@
 doc_param_1=name: as in [getMenuHelp]
 doc_param_2=menu_type: as in [getMenuHelp]
 doc_param_3=%(doc_profile_key)s
-doc_return=return an actionId or the empty string if something went wrong
+doc_return=return a XMLUI or empty string if it is a one shot action
--- a/src/core/sat_main.py	Tue Dec 24 01:31:18 2013 +0100
+++ b/src/core/sat_main.py	Tue Dec 24 15:18:31 2013 +0100
@@ -109,7 +109,7 @@
         self.__private_data = {}  # used for internal callbacks (key = id)
         self.profiles = {}
         self.plugins = {}
-        self.menus = {}  # used to know which new menus are wanted by plugins
+        self.menus = {}  # dynamic menus. key: (type, category, name), value: menu data (dictionnary)
 
         self.memory = Memory(self)
 
@@ -162,7 +162,7 @@
         self.bridge.register("getProgress", self.getProgress)
         self.bridge.register("getMenus", self.getMenus)
         self.bridge.register("getMenuHelp", self.getMenuHelp)
-        self.bridge.register("callMenu", self.callMenu)
+        self.bridge.register("asyncCallMenu", self.callMenu)
 
         self.memory.initialized.addCallback(self._postMemoryInit)
 
@@ -213,11 +213,11 @@
             self.plugins[import_name].is_handler = False
         #TODO: test xmppclient presence and register handler parent
 
-    def connect(self, profile_key='@DEFAULT@'):
+    def connect(self, profile_key='@NONE@'):
         """Connect to jabber server"""
         self.asyncConnect(profile_key)
 
-    def asyncConnect(self, profile_key='@DEFAULT@'):
+    def asyncConnect(self, profile_key='@NONE@'):
         """Connect to jabber server with asynchronous reply
         @param profile_key: %(doc_profile)s
         """
@@ -376,7 +376,7 @@
             return None
         return self.profiles[profile].getHostJid()
 
-    def registerNewAccount(self, login, password, email, server, port=5222, id=None, profile_key='@DEFAULT@'):
+    def registerNewAccount(self, login, password, email, server, port=5222, id=None, profile_key='@NONE@'):
         """Connect to a server and create a new account using in-band registration"""
         profile = self.memory.getProfileName(profile_key)
         assert(profile)
@@ -884,38 +884,56 @@
 
     #Menus management
 
-    def importMenu(self, category, name, callback, help_string="", type="NORMAL"):
+    def importMenu(self, category, name, callback, callback_args=None, callback_kwargs=None, help_string="", type_="NORMAL"):
         """register a new menu for frontends
         @param category: category of the menu
         @param name: menu item entry
-        @param callback: method to be called when menuitem is selected"""
-        if (category, name) in self.menus:
-            error("Want to register a menu which already existe")
-            return
-        self.menus[(category, name, type)] = {'callback': callback, 'help_string': help_string, 'type': type}
+        @param callback: method to be called when menuitem is selected
+        @param callback_args: optional arguments to forward to callback
+        @param callback_kwargs: optional keywords arguments to forward to callback
+        """
+        # TODO: manage translations
+        if (type_, category, name) in self.menus:
+            raise exceptions.ConflictError("Menu already exists")
+        menu_data = {'callback': callback, 'help_string': help_string}
+        if callback_args is not None:
+            assert(isinstance(callback_args, list))
+            menu_data['callback_args'] = callback_args
+        if callback_kwargs is not None:
+            assert(isinstance(callback_kwargs, dict))
+            menu_data['callback_kwargs'] = callback_kwargs
+        self.menus[(type_, category, name)] = menu_data
 
     def getMenus(self):
         """Return all menus registered"""
+        # TODO: manage translations
         return self.menus.keys()
 
-    def getMenuHelp(self, category, name, type="NORMAL"):
+    def getMenuHelp(self, category, name, type_="NORMAL"):
         """return the help string of the menu"""
+        # TODO: manage translations
         try:
-            return self.menus[(category, name, type)]['help_string']
+            return self.menus[(type_, category, name)]['help_string']
         except KeyError:
-            error(_("Trying to access an unknown menu"))
-            return ""
+            raise exceptions.DataError("Trying to access an unknown menu")
 
-    def callMenu(self, category, name, type="NORMAL", profile_key='@DEFAULT@'):
-        """return the id of the action"""
+    def callMenu(self, category, name, type_="NORMAL", profile_key='@NONE@'):
+        """ Call a dynamic menu
+        @param category: category of the menu to call
+        @param name: name of the menu to call
+        @param type_: type of the menu to call
+        @param profile_key: %(doc_profile_key)s
+        @return: XMLUI or empty string if it's a one shot menu
+        """
         profile = self.memory.getProfileName(profile_key)
-        if not profile_key:
-            error(_('Non-exsitant profile'))
-            return ""
-        if (category, name, type) in self.menus:
-            id = self.get_next_id()
-            self.menus[(category, name, type)]['callback'](id, profile)
-            return id
-        else:
-            error(_("Trying to access an unknown menu (%(category)s/%(name)s/%(type)s)") % {'category': category, 'name': name, 'type': type})
-            return ""
+        if not profile:
+            raise exceptions.ProfileUnknownError
+        menu_data = self.menus[(type_, category, name)]
+        callback = menu_data['callback']
+        args = menu_data.get('callback_args', ())
+        kwargs = menu_data.get('callback_kwargs', {}).copy()
+        kwargs["profile"] = profile
+        try:
+            return defer.maybeDeferred(callback, *args, **kwargs)
+        except KeyError:
+            raise exceptions.DataError("Trying to access an unknown menu (%(type)s/%(category)s/%(name)s)" % {'type': type_, 'category': category, 'name': name})