Mercurial > libervia-backend
view frontends/src/primitivus/primitivus @ 1955:633b5c21aefd
backend, frontend: messages refactoring (huge commit, not finished):
/!\ database schema has been modified, do a backup before updating
message have been refactored, here are the main changes:
- languages are now handled
- all messages have an uid (internal to SàT)
- message updating is anticipated
- subject is now first class
- new naming scheme is used newMessage => messageNew, getHistory => historyGet, sendMessage => messageSend
- minimal compatibility refactoring in quick_frontend/Primitivus, better refactoring should follow
- threads handling
- delayed messages are saved into history
- info messages may also be saved in history (e.g. to keep track of people joining/leaving a room)
- duplicate messages should be avoided
- historyGet return messages in right order, no need to sort again
- plugins have been updated to follow new features, some of them need to be reworked (e.g. OTR)
- XEP-0203 (Delayed Delivery) is now fully handled in core, the plugin just handle disco and creation of a delay element
- /!\ jp and Libervia are currently broken, as some features of Primitivus
It has been put in one huge commit to avoid breaking messaging between changes.
This is the main part of message refactoring, other commits will follow to take profit of the new features/behaviour.
author | Goffi <goffi@goffi.org> |
---|---|
date | Tue, 24 May 2016 22:11:04 +0200 |
parents | 011eff37e21d |
children | a2bc5089c2eb |
line wrap: on
line source
#!/usr/bin/env python2 # -*- coding: utf-8 -*- # Primitivus: a SAT frontend # Copyright (C) 2009-2016 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 _, D_ from sat_frontends.primitivus.constants import Const as C from sat.core import log_config log_config.satConfigure(C.LOG_BACKEND_STANDARD, C) from sat.core import log as logging 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 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 from sat_frontends.primitivus import xmlui from sat_frontends.primitivus.progress import Progress 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.tools import jid from os.path import join import signal class EditBar(sat_widgets.ModalEdit): """ The modal edit bar where you would enter messages and commands. """ 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.host = host self.setCompletionMethod(self._text_completion) urwid.connect_signal(self, 'click', self.onTextEntered) def _text_completion(self, text, completion_data, mode): if mode == C.MODE_INSERTION: return self._nick_completion(text, completion_data) else: return text def _nick_completion(self, text, completion_data): """Completion method which complete pseudo in group chat for params, see AdvancedEdit""" nicks = [] for profile, clist in self.host.contact_lists.iteritems(): for contact in clist.selected: 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 if start_idx == len(nicks): start_idx = 0 except (KeyError, ValueError): start_idx = 0 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 def onTextEntered(self, editBar): """Called when text is entered in the main edit bar""" if self.mode == C.MODE_INSERTION: if isinstance(self.host.selected_widget, quick_chat.QuickChat): chat_widget = self.host.selected_widget self.host.messageSend( chat_widget.target, {'': editBar.get_edit_text()}, # TODO: handle language 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 == C.MODE_COMMAND: self.commandHandler() def commandHandler(self): #TODO: separate class with auto documentation (with introspection) # and completion method tokens = self.get_edit_text().split(' ') command, args = tokens[0], tokens[1:] if command == 'quit': self.host.onExit() raise urwid.ExitMainLoop() elif command == 'messages': wid = sat_widgets.GenericList(logging.memoryGet()) self.host.selectWidget(wid) # FIXME: reactivate the command # 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': 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': 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, extra={'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('') def _historyCb(self, text): self.set_edit_text(text) self.set_edit_pos(len(text)) def keypress(self, size, key): """Callback when a key is pressed. Send "composing" states 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.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.host._updateInputHistory(self.get_edit_text(), -1, self._historyCb, self.mode) return elif key == a_key['HISTORY_NEXT']: self.host._updateInputHistory(self.get_edit_text(), +1, self._historyCb, self.mode) return elif key == a_key['EDIT_ENTER']: self.host._updateInputHistory(self.get_edit_text(), mode=self.mode) else: 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) class PrimitivusTopWidget(sat_widgets.FocusPile): """Top most widget used in Primitivus""" _focus_inversed = True positions = ('menu', 'body', 'notif_bar', 'edit_bar') can_hide = ('menu', 'notif_bar') def __init__(self, body, menu, notif_bar, edit_bar): self._body = body self._menu = menu self._notif_bar = notif_bar self._edit_bar = edit_bar self._hidden = {'notif_bar'} self._focus_extra = False super(PrimitivusTopWidget, self).__init__([('pack', self._menu), self._body, ('pack', self._edit_bar)]) for position in self.positions: setattr(self, position, property(lambda: self, self.widgetGet(position=position), lambda pos, new_wid: self.widgetSet(new_wid, position=pos)) ) self.focus_position = len(self.contents)-1 def getVisiblePositions(self, keep=None): """Return positions that are not hidden in the right order @param keep: if not None, this position will be keep in the right order, even if it's hidden (can be useful to find its index) @return (list): list of visible positions """ return [pos for pos in self.positions if (keep and pos == keep) or pos not in self._hidden] def keypress(self, size, key): """Manage FOCUS keys that focus directly a main part (one of self.positions) To avoid key conflicts, a combinaison must be made with FOCUS_EXTRA then an other key """ if key == a_key['FOCUS_EXTRA']: self._focus_extra = True return if self._focus_extra: self._focus_extra = False if key in ('m', '1'): focus = 'menu' elif key in ('b', '2'): focus = 'body' elif key in ('n', '3'): focus = 'notif_bar' elif key in ('e', '4'): focus = 'edit_bar' else: return super(PrimitivusTopWidget, self).keypress(size, key) if focus in self._hidden: return self.focus_position = self.getVisiblePositions().index(focus) return return super(PrimitivusTopWidget, self).keypress(size, key) def widgetGet(self, position): if not position in self.positions: raise ValueError("Unknown position {}".format(position)) return getattr(self, "_{}".format(position)) def widgetSet(self, widget, position): if not position in self.positions: raise ValueError("Unknown position {}".format(position)) return setattr(self, "_{}".format(position), widget) def hideSwitch(self, position): if not position in self.can_hide: raise ValueError("Can't switch position {}".format(position)) hide = not position in self._hidden widget = self.widgetGet(position) idx = self.getVisiblePositions(position).index(position) if hide: del self.contents[idx] self._hidden.add(position) else: self.contents.insert(idx, (widget, ('pack', None))) self._hidden.remove(position) def show(self, position): if position in self._hidden: self.hideSwitch(position) def hide(self, position): if not position in self._hidden: self.hideSwitch(position) class PrimitivusApp(QuickApp, InputHistory): MB_HANDLE = False def __init__(self): QuickApp.__init__(self, create_bridge=DBusBridgeFrontend, xmlui=xmlui, 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._visible_widgets = set() self.notif_bar = sat_widgets.NotificationBar() urwid.connect_signal(self.notif_bar, 'change', self.onNotification) 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() # 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 @mode.setter def mode(self, value): self.editBar.mode = value def modeHint(self, value): """Change mode if make sens (i.e.: if there is nothing in the editBar)""" if not self.editBar.get_edit_text(): self.mode = value def debug(self): """convenient method to reset screen and launch (i)p(u)db""" log.info('Entered debug mode') try: import pudb pudb.set_trace() except ImportError: import os os.system('reset') try: import ipdb ipdb.set_trace() except ImportError: import pdb pdb.set_trace() def redraw(self): """redraw the screen""" try: self.loop.draw_screen() except AttributeError: pass def start(self): self.i = 0 self.loop.set_alarm_in(0,lambda a,b: self.postInit()) self.loop.run() def postInit(self): try: config.applyConfig(self) except Exception as e: log.error(u"configuration error: {}".format(e)) popup = self.alert(_(u"Configuration Error"), _(u"Something went wrong while reading the configuration, please check :messages")) if self.options.profile: self._early_popup = popup else: self.showPopUp(popup) super(PrimitivusApp, self).postInit(self.main_widget) def inputFilter(self, input_, raw): if self.__saved_overlay and input_ != a_key['OVERLAY_HIDE']: return for i in input_: if isinstance(i,tuple): if i[0] == 'mouse press': if i[1] == 4: #Mouse wheel up input_[input_.index(i)] = a_key['HISTORY_PREV'] if i[1] == 5: #Mouse wheel down input_[input_.index(i)] = a_key['HISTORY_NEXT'] return input_ def keyHandler(self, input_): if input_ == a_key['MENU_HIDE']: """User want to (un)hide the menu roller""" try: self.main_widget.hideSwitch('menu') except AttributeError: pass elif input_ == a_key['NOTIFICATION_NEXT']: """User wants to see next notification""" self.notif_bar.showNext() elif input_ == a_key['OVERLAY_HIDE']: """User wants to (un)hide overlay window""" if isinstance(self.loop.widget,urwid.Overlay): self.__saved_overlay = self.loop.widget self.loop.widget = self.main_widget else: if self.__saved_overlay: self.loop.widget = self.__saved_overlay self.__saved_overlay = None 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 lists try: for wid, options in self.center_part.contents: if self.contact_lists_pile is wid: self.center_part.contents.remove((wid, options)) break else: 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 elif input_ == 'window resize': width,height = self.loop.screen_size if height<=5 and width<=35: if not 'save_main_widget' in dir(self): self.save_main_widget = self.loop.widget self.loop.widget = urwid.Filler(urwid.Text(_("Pleeeeasse, I can't even breathe !"))) else: if 'save_main_widget' in dir(self): self.loop.widget = self.save_main_widget del self.save_main_widget try: return self.menu_roller.checkShortcuts(input_) except AttributeError: return input_ def addMenus(self, menu, type_filter, menu_data=None): """Add cached menus to instance @param menu: sat_widgets.Menu instance @param type_filter: menu type like is sat.core.sat_main.importMenu @param menu_data: data to send with these menus """ def add_menu_cb(callback_id): 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_)) def _buildMenuRoller(self): menu = sat_widgets.Menu(self.loop) general = _("General") menu.addMenu(general, _("Connect"), self.onConnectRequest) menu.addMenu(general, _("Disconnect"), self.onDisconnectRequest) menu.addMenu(general, _("Parameters"), self.onParam) menu.addMenu(general, _("About"), self.onAboutRequest) menu.addMenu(general, _("Exit"), self.onExitRequest, a_key['APP_QUIT']) menu.addMenu(_("Contacts")) # add empty menu to save the place in the menu order groups = _("Groups") menu.addMenu(groups) menu.addMenu(groups, _("Join room"), self.onJoinRoomRequest, a_key['ROOM_JOIN']) #additionals menus #FIXME: do this in a more generic way (in quickapp) self.addMenus(menu, C.MENU_GLOBAL) menu_roller = sat_widgets.MenuRoller([(_('Main menu'), menu, C.MENU_ID_MAIN)]) return menu_roller def _buildMainWidget(self): 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 plugging_profiles(self): self.loop.widget = self._buildMainWidget() self.redraw() try: # if a popup arrived before main widget is build, we need to show it now self.showPopUp(self._early_popup) except AttributeError: pass else: del self._early_popup def profilePlugged(self, profile): QuickApp.profilePlugged(self, profile) contact_list = self.widgets.getOrCreateWidget(ContactList, None, on_new_widget=None, 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 isHidden(self): """Tells if the frontend window is hidden. @return bool """ return False # FIXME: implement when necessary def alert(self, title, message): """Shortcut method to create an alert message Alert will have an "OK" button, which remove it if pressed @param title(unicode): title of the dialog @param message(unicode): body of the dialog @return (urwid_satext.Alert): the created Alert instance """ popup = sat_widgets.Alert(title, message) popup.setCallback('ok', lambda dummy: self.removePopUp(popup)) self.showPopUp(popup) return popup def removePopUp(self, widget=None): """Remove current pop-up, and if there is other in queue, show it @param widget(None, urwid.Widget): if not None remove this popup from front or queue """ # TODO: refactor popup management in a cleaner way if widget is not None: if isinstance(self.loop.widget, urwid.Overlay): current_popup = self.loop.widget.top_w if not current_popup == widget: try: self.notif_bar.removePopUp(widget) except ValueError: log.warning(u"Trying to remove an unknown widget {}".format(widget)) return self.loop.widget = self.main_widget next_popup = self.notif_bar.getNextPopup() if next_popup: #we still have popup to show, we display it self.showPopUp(next_popup) else: self.redraw() def showPopUp(self, pop_up_widget, perc_width=40, perc_height=40, align='center', valign='middle'): "Show a pop-up window if possible, else put it in queue" if not isinstance(self.loop.widget, urwid.Overlay): display_widget = urwid.Overlay(pop_up_widget, self.main_widget, align, ('relative', perc_width), valign, ('relative', perc_height)) self.loop.widget = display_widget self.redraw() else: self.notif_bar.addPopUp(pop_up_widget) def notify(self, message): """"Notify message to user via notification bar""" self.notif_bar.addMessage(message) self.redraw() 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 wid_idx = len(self.center_part.widget_list)-1 self.center_part.widget_list[wid_idx] = widget 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 self.contact_lists.select(None) for wid in self.visible_widgets: # FIXME: check if widgets.getWidgets is not more appropriate 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: 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 self.redraw() def addProgress(self, pid, message, profile): """Follow a SàT progression @param pid: progression id @param message: message to show to identify the progression """ self.progress_wid.add(pid, message, profile) def setProgress(self, percentage): """Set the progression shown in notification bar""" self.notif_bar.setProgress(percentage) 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): popup, answer_cb, answer_data = data self.removePopUp(popup) if answer_cb is not None: answer_cb(True, answer_data) def _dialogCancelCb(self, widget, data): popup, answer_cb, answer_data = data self.removePopUp(popup) if answer_cb is not None: 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(title, message, ok_cb=answer_cb) if answer_cb is None: popup.setCallback('ok', lambda dummy: self.removePopUp(popup)) elif type_ == 'error': popup = sat_widgets.Alert(title, message, ok_cb=answer_cb) if answer_cb is None: popup.setCallback('ok', lambda dummy: self.removePopUp(popup)) elif type_ == 'yes/no': popup = sat_widgets.ConfirmDialog(message) popup.setCallback('yes', self._dialogOkCb, (popup, answer_cb, answer_data)) popup.setCallback('no', self._dialogCancelCb, (popup, answer_cb, answer_data)) else: popup = sat_widgets.Alert(title, message, ok_cb=answer_cb) if answer_cb is None: popup.setCallback('ok', lambda dummy: self.removePopUp(popup)) log.error(u'unmanaged dialog type: {}'.format(type_)) self.showPopUp(popup) def dialogFailure(self, failure): """Show a failure that has been returned by an asynchronous bridge method. @param failure (defer.Failure): Failure instance """ self.alert(failure.classname, failure.message) def onNotification(self, notif_bar): """Called when a new notification has been received""" if not isinstance(self.main_widget, PrimitivusTopWidget): #if we are not in the main configuration, we ignore the notifications bar return if self.notif_bar.canHide(): #No notification left, we can hide the bar self.main_widget.hide('notif_bar') else: self.main_widget.show('notif_bar') self.redraw() # FIXME: invalidate cache in a more efficient way def _actionManagerUnknownError(self): self.alert(_("Error"), _(u"Unmanaged action")) 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"] = quick_utils.getNewPath(dest_path) self.addProgress(confirmation_id, dest_path) accept_cb(None) def accept_file_transfer_cb(widget): self.removePopUp() pop_up_widget = FileDialog(dir_selected_cb, refuse_cb, title=_(u"Where do you want to save the file ?"), style=['dir']) self.showPopUp(pop_up_widget) def accept_cb(widget): self.removePopUp() self.bridge.confirmationAnswer(confirmation_id, True, answer_data, profile) def refuse_cb(widget): self.removePopUp() self.bridge.confirmationAnswer(confirmation_id, False, answer_data, profile) if confirmation_type == "FILE_TRANSFER": pop_up_widget = sat_widgets.ConfirmDialog(_("The contact %(jid)s wants to send you the file %(filename)s\nDo you accept ?") % {'jid':data["from"], 'filename':data["filename"]}, no_cb=refuse_cb, yes_cb=accept_file_transfer_cb) self.showPopUp(pop_up_widget) elif confirmation_type == "YES/NO": pop_up_widget = sat_widgets.ConfirmDialog(data["message"], no_cb=refuse_cb, yes_cb=accept_cb) self.showPopUp(pop_up_widget) def actionResultHandler(self, type_, id, data, profile): # FIXME: to be removed if not id in self.current_action_ids: log.debug (_('unknown id, ignoring')) return if type_ == "SUPPRESS": self.current_action_ids.remove(id) elif type_ == "XMLUI": self.current_action_ids.remove(id) log.debug (_("XML user interface received")) misc = {} #FIXME FIXME FIXME: must clean all this crap ! title = _('Form') if data['type'] == 'registration': title = _('Registration') misc['target'] = data['target'] misc['action_back'] = self.bridge.gatewayRegister ui = xmlui.create(self, title=title, xml_data=data['xml'], misc=misc, profile=profile) if data['type'] == 'registration': ui.show('popup') else: ui.show('window') elif type_ == "ERROR": self.current_action_ids.remove(id) self.alert(_("Error"), unicode(data["message"])) #FIXME: remove unicode here when DBus Bridge will no return dbus.String anymore elif type_ == "RESULT": self.current_action_ids.remove(id) if self.current_action_ids_cb.has_key(id): callback = self.current_action_ids_cb[id] del self.current_action_ids_cb[id] callback(data) elif type_ == "DICT_DICT": self.current_action_ids.remove(id) if self.current_action_ids_cb.has_key(id): callback = self.current_action_ids_cb[id] del self.current_action_ids_cb[id] callback(data) else: 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) for contact_list in self.widgets.getWidgets(ContactList): if profile in contact_list.profiles: contact_list.setFocus(jid.JID(room_jid_s), True) def progressStartedHandler(self, pid, metadata, profile): super(PrimitivusApp, self).progressStartedHandler(pid, metadata, profile) self.addProgress(pid, metadata.get('name', _(u'unkown')), profile) def progressFinishedHandler(self, pid, metadata, profile): log.info(u"Progress {} finished".format(pid)) super(PrimitivusApp, self).progressFinishedHandler(pid, metadata, profile) def progressErrorHandler(self, pid, err_msg, profile): log.warning(u"Progress {pid} error: {err_msg}".format(pid=pid, err_msg=err_msg)) super(PrimitivusApp, self).progressErrorHandler(pid, err_msg, profile) ##DIALOGS CALLBACKS## def onJoinRoom(self, button, edit): self.removePopUp() room_jid = jid.JID(edit.get_edit_text()) self.bridge.joinMUC(room_jid, self.profiles[self.current_profile].whoami.node, {}, self.current_profile, callback=lambda dummy: None, errback=self.dialogFailure) #MENU EVENTS# def onConnectRequest(self, menu): QuickApp.asyncConnect(self, self.current_profile) def onDisconnectRequest(self, menu): self.disconnect(self.current_profile) def onParam(self, menu): def success(params): ui = xmlui.create(self, xml_data=params, profile=self.current_profile) ui.show() def failure(error): self.alert(_("Error"), _("Can't get parameters (%s)") % error) self.bridge.getParamsUI(app=C.APP_NAME, profile_key=self.current_profile, callback=success, errback=failure) def onExitRequest(self, menu): QuickApp.onExit(self) raise urwid.ExitMainLoop() def onJoinRoomRequest(self, menu): """User wants to join a MUC room""" pop_up_widget = sat_widgets.InputDialog(_("Entering a MUC room"), _("Please enter MUC's JID"), default_txt=self.bridge.getDefaultMUC(), ok_cb=self.onJoinRoom) pop_up_widget.setCallback('cancel', lambda dummy: self.removePopUp(pop_up_widget)) self.showPopUp(pop_up_widget) def onAboutRequest(self, menu): self.alert(_("About"), C.APP_NAME + " v" + self.bridge.getVersion()) #MISC CALLBACKS# def setPresenceStatus(self, show='', status=None, profile=C.PROF_KEY_NONE): contact_list_wid = self.widgets.getWidget(ContactList, profiles=profile) if contact_list_wid is not None: contact_list_wid.status_bar.setPresenceStatus(show, status) else: log.warning(u"No ContactList widget found for profile {}".format(profile)) sat = PrimitivusApp() sat.start()