# HG changeset patch # User Goffi # Date 1387894711 -3600 # Node ID e3ad48a2aab2c41afd9bd4b0cd8cc3a09cf028c8 # Parent f021bf27a55745f023cbab7ada2fe4615246c42f core, frontends: callMenu is now async and don't use callback_id anymore diff -r f021bf27a557 -r e3ad48a2aab2 frontends/src/bridge/DBus.py --- 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) diff -r f021bf27a557 -r e3ad48a2aab2 frontends/src/constants.py --- 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@" diff -r f021bf27a557 -r e3ad48a2aab2 frontends/src/primitivus/primitivus --- 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) diff -r f021bf27a557 -r e3ad48a2aab2 frontends/src/wix/main_window.py --- 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) diff -r f021bf27a557 -r e3ad48a2aab2 src/bridge/DBus.py --- 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): diff -r f021bf27a557 -r e3ad48a2aab2 src/bridge/bridge_constructor/bridge_template.ini --- 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 diff -r f021bf27a557 -r e3ad48a2aab2 src/core/sat_main.py --- 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})