#!/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 . 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.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 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.sendMessage(chat_widget.target, editBar.get_edit_text(), 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 ' 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 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() 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 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 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: 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) self.contact_lists[profile].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): self.contact_lists[profile].status_bar.setPresenceStatus(show, status) sat = PrimitivusApp() sat.start()