Mercurial > libervia-backend
comparison frontends/src/primitivus/primitivus @ 1265:e3a9ea76de35 frontends_multi_profiles
quick_frontend, primitivus: multi-profiles refactoring part 1 (big commit, sorry :p):
This refactoring allow primitivus to manage correctly several profiles at once, with various other improvments:
- profile_manager can now plug several profiles at once, requesting password when needed. No more profile plug specific method is used anymore in backend, instead a "validated" key is used in actions
- Primitivus widget are now based on a common "PrimitivusWidget" classe which mainly manage the decoration so far
- all widgets are treated in the same way (contactList, Chat, Progress, etc), no more chat_wins specific behaviour
- widgets are created in a dedicated manager, with facilities to react on new widget creation or other events
- quick_frontend introduce a new QuickWidget class, which aims to be as generic and flexible as possible. It can manage several targets (jids or something else), and several profiles
- each widget class return a Hash according to its target. For example if given a target jid and a profile, a widget class return a hash like (target.bare, profile), the same widget will be used for all resources of the same jid
- better management of CHAT_GROUP mode for Chat widgets
- some code moved from Primitivus to QuickFrontend, the final goal is to have most non backend code in QuickFrontend, and just graphic code in subclasses
- no more (un)escapePrivate/PRIVATE_PREFIX
- contactList improved a lot: entities not in roster and special entities (private MUC conversations) are better managed
- resources can be displayed in Primitivus, and their status messages
- profiles are managed in QuickFrontend with dedicated managers
This is work in progress, other frontends are broken. Urwid SàText need to be updated. Most of features of Primitivus should work as before (or in a better way ;))
author | Goffi <goffi@goffi.org> |
---|---|
date | Wed, 10 Dec 2014 19:00:09 +0100 |
parents | 82dabb442e2e |
children | faa1129559b8 |
comparison
equal
deleted
inserted
replaced
1264:60dfa2f5d61f | 1265:e3a9ea76de35 |
---|---|
26 log = logging.getLogger(__name__) | 26 log = logging.getLogger(__name__) |
27 import urwid | 27 import urwid |
28 from urwid_satext import sat_widgets | 28 from urwid_satext import sat_widgets |
29 from urwid_satext.files_management import FileDialog | 29 from urwid_satext.files_management import FileDialog |
30 from sat_frontends.quick_frontend.quick_app import QuickApp | 30 from sat_frontends.quick_frontend.quick_app import QuickApp |
31 from sat_frontends.quick_frontend.quick_chat_list import QuickChatList | 31 from sat_frontends.quick_frontend.quick_utils import getNewPath |
32 from sat_frontends.quick_frontend.quick_utils import getNewPath, unescapePrivate | 32 from sat_frontends.quick_frontend import quick_chat |
33 from sat_frontends.primitivus.profile_manager import ProfileManager | 33 from sat_frontends.primitivus.profile_manager import ProfileManager |
34 from sat_frontends.primitivus.contact_list import ContactList | 34 from sat_frontends.primitivus.contact_list import ContactList |
35 from sat_frontends.primitivus.chat import Chat | 35 from sat_frontends.primitivus.chat import Chat |
36 from sat_frontends.primitivus import xmlui | 36 from sat_frontends.primitivus import xmlui |
37 from sat_frontends.primitivus.progress import Progress | 37 from sat_frontends.primitivus.progress import Progress |
38 from sat_frontends.primitivus.notify import Notify | 38 from sat_frontends.primitivus.notify import Notify |
39 from sat_frontends.primitivus.keys import action_key_map as a_key | 39 from sat_frontends.primitivus.keys import action_key_map as a_key |
40 from sat_frontends.primitivus import config | 40 from sat_frontends.primitivus import config |
41 from sat_frontends.tools.misc import InputHistory | 41 from sat_frontends.tools.misc import InputHistory |
42 from sat_frontends.constants import Const as commonConst # FIXME | 42 from sat_frontends.tools import jid |
43 from sat_frontends.tools.jid import JID | |
44 from os.path import join | 43 from os.path import join |
45 import signal | 44 import signal |
46 | 45 |
47 | |
48 class ChatList(QuickChatList): | |
49 """This class manage the list of chat windows""" | |
50 | |
51 def createChat(self, target): | |
52 return Chat(target, self.host) | |
53 | 46 |
54 | 47 |
55 class EditBar(sat_widgets.ModalEdit): | 48 class EditBar(sat_widgets.ModalEdit): |
56 """ | 49 """ |
57 The modal edit bar where you would enter messages and commands. | 50 The modal edit bar where you would enter messages and commands. |
58 """ | 51 """ |
59 | 52 |
60 def __init__(self, app): | 53 def __init__(self, host): |
61 modes = {None: ('NORMAL', u''), | 54 modes = {None: (C.MODE_NORMAL, u''), |
62 a_key['MODE_INSERTION']: ('INSERTION', u'> '), | 55 a_key['MODE_INSERTION']: (C.MODE_INSERTION, u'> '), |
63 a_key['MODE_COMMAND']: ('COMMAND', u':')} #XXX: captions *MUST* be unicode | 56 a_key['MODE_COMMAND']: (C.MODE_COMMAND, u':')} #XXX: captions *MUST* be unicode |
64 super(EditBar, self).__init__(modes) | 57 super(EditBar, self).__init__(modes) |
65 self.app = app | 58 self.host = host |
66 self.setCompletionMethod(self._text_completion) | 59 self.setCompletionMethod(self._text_completion) |
67 urwid.connect_signal(self, 'click', self.onTextEntered) | 60 urwid.connect_signal(self, 'click', self.onTextEntered) |
68 | 61 |
69 def _text_completion(self, text, completion_data, mode): | 62 def _text_completion(self, text, completion_data, mode): |
70 if mode == 'INSERTION': | 63 if mode == C.MODE_INSERTION: |
71 return self._nick_completion(text, completion_data) | 64 return self._nick_completion(text, completion_data) |
72 else: | 65 else: |
73 return text | 66 return text |
74 | 67 |
75 def _nick_completion(self, text, completion_data): | 68 def _nick_completion(self, text, completion_data): |
76 """Completion method which complete pseudo in group chat | 69 """Completion method which complete pseudo in group chat |
77 for params, see AdvancedEdit""" | 70 for params, see AdvancedEdit""" |
78 contact = self.app.contact_list.getContact() ###Based on the fact that there is currently only one contact selectable at once | 71 contact = self.host.contact_list.getContact() ###Based on the fact that there is currently only one contact selectable at once |
79 if contact: | 72 if contact: |
80 chat = self.app.chat_wins[contact] | 73 chat = self.host.chat_wins[contact] |
81 if chat.type != "group": | 74 if chat.type != "group": |
82 return text | 75 return text |
83 space = text.rfind(" ") | 76 space = text.rfind(" ") |
84 start = text[space+1:] | 77 start = text[space+1:] |
85 nicks = list(chat.occupants) | 78 nicks = list(chat.occupants) |
96 return text[:space+1] + nicks[idx] + (': ' if space < 0 else '') | 89 return text[:space+1] + nicks[idx] + (': ' if space < 0 else '') |
97 return text | 90 return text |
98 | 91 |
99 def onTextEntered(self, editBar): | 92 def onTextEntered(self, editBar): |
100 """Called when text is entered in the main edit bar""" | 93 """Called when text is entered in the main edit bar""" |
101 if self.mode == 'INSERTION': | 94 if self.mode == C.MODE_INSERTION: |
102 contact = self.app.contact_list.getContact() ###Based on the fact that there is currently only one contact selectableat once | 95 if isinstance(self.host.selected_widget, quick_chat.QuickChat): |
103 if contact: | 96 chat_widget = self.host.selected_widget |
104 chat = self.app.chat_wins[contact] | 97 self.host.sendMessage(chat_widget.target, |
105 self.app.sendMessage(contact, | |
106 editBar.get_edit_text(), | 98 editBar.get_edit_text(), |
107 mess_type = "groupchat" if chat.type == 'group' else "chat", | 99 mess_type = "groupchat" if chat_widget.type == 'group' else "chat", # TODO: put this in QuickChat |
108 errback=lambda failure: self.app.notify(_("Error while sending message (%s)") % failure), | 100 errback=lambda failure: self.host.notify(_("Error while sending message ({})").format(failure)), |
109 profile_key=self.app.profile | 101 profile_key=chat_widget.profile |
110 ) | 102 ) |
111 editBar.set_edit_text('') | 103 editBar.set_edit_text('') |
112 elif self.mode == 'COMMAND': | 104 elif self.mode == C.MODE_COMMAND: |
113 self.commandHandler() | 105 self.commandHandler() |
114 | 106 |
115 def commandHandler(self): | 107 def commandHandler(self): |
116 #TODO: separate class with auto documentation (with introspection) | 108 #TODO: separate class with auto documentation (with introspection) |
117 # and completion method | 109 # and completion method |
118 tokens = self.get_edit_text().split(' ') | 110 tokens = self.get_edit_text().split(' ') |
119 command, args = tokens[0], tokens[1:] | 111 command, args = tokens[0], tokens[1:] |
120 if command == 'quit': | 112 if command == 'quit': |
121 self.app.onExit() | 113 self.host.onExit() |
122 raise urwid.ExitMainLoop() | 114 raise urwid.ExitMainLoop() |
123 elif command == 'messages': | 115 elif command == 'messages': |
124 wid = sat_widgets.GenericList(logging.memoryGet()) | 116 wid = sat_widgets.GenericList(logging.memoryGet()) |
125 self.app.addWindow(wid) | 117 self.host.selectWidget(wid) |
126 elif command == 'presence': | 118 # elif command == 'presence': |
127 values = [value for value in commonConst.PRESENCE.keys()] | 119 # values = [value for value in commonConst.PRESENCE.keys()] |
128 values = [value if value else 'online' for value in values] # the empty value actually means 'online' | 120 # values = [value if value else 'online' for value in values] # the empty value actually means 'online' |
129 if args and args[0] in values: | 121 # if args and args[0] in values: |
130 presence = '' if args[0] == 'online' else args[0] | 122 # presence = '' if args[0] == 'online' else args[0] |
131 self.app.status_bar.onChange(user_data=sat_widgets.ClickableText(commonConst.PRESENCE[presence])) | 123 # self.host.status_bar.onChange(user_data=sat_widgets.ClickableText(commonConst.PRESENCE[presence])) |
132 else: | 124 # else: |
133 self.app.status_bar.onPresenceClick() | 125 # self.host.status_bar.onPresenceClick() |
134 elif command == 'status': | 126 # elif command == 'status': |
135 if args: | 127 # if args: |
136 self.app.status_bar.onChange(user_data=sat_widgets.AdvancedEdit(args[0])) | 128 # self.host.status_bar.onChange(user_data=sat_widgets.AdvancedEdit(args[0])) |
137 else: | 129 # else: |
138 self.app.status_bar.onStatusClick() | 130 # self.host.status_bar.onStatusClick() |
139 elif command == 'history': | 131 elif command == 'history': |
140 try: | 132 widget = self.host.selected_widget |
141 limit = int(args[0]) | 133 if isinstance(widget, quick_chat.QuickChat): |
142 except (IndexError, ValueError): | 134 try: |
143 limit = 50 | 135 limit = int(args[0]) |
144 win = self.app.chat_wins[JID(self.app.contact_list.selected).bare] | 136 except (IndexError, ValueError): |
145 win.clearHistory() | 137 limit = 50 |
146 if limit > 0: | 138 widget.clearHistory() |
147 win.historyPrint(size=limit, profile=self.app.profile) | 139 if limit > 0: |
140 widget.historyPrint(size=limit, profile=widget.profile) | |
148 elif command == 'search': | 141 elif command == 'search': |
149 pattern = " ".join(args) | 142 widget = self.host.selected_widget |
150 if not pattern: | 143 if isinstance(widget, quick_chat.QuickChat): |
151 self.app.notif_bar.addMessage(D_("Please specify the globbing pattern to search for")) | 144 pattern = " ".join(args) |
152 win = self.app.chat_wins[JID(self.app.contact_list.selected).bare] | 145 if not pattern: |
153 win.clearHistory() | 146 self.host.notif_bar.addMessage(D_("Please specify the globbing pattern to search for")) |
154 win.printInfo(D_("Results for searching the globbing pattern: %s") % pattern, timestamp=0) | 147 widget.clearHistory() |
155 win.historyPrint(size=C.HISTORY_LIMIT_NONE, search=pattern, profile=self.app.profile) | 148 widget.printInfo(D_("Results for searching the globbing pattern: %s") % pattern, timestamp=0) |
156 win.printInfo(D_("Type ':history <lines>' to reset the chat history")) | 149 widget.historyPrint(size=C.HISTORY_LIMIT_NONE, search=pattern, profile=widget.profile) |
150 widget.printInfo(D_("Type ':history <lines>' to reset the chat history")) | |
157 else: | 151 else: |
158 return | 152 return |
159 self.set_edit_text('') | 153 self.set_edit_text('') |
160 | 154 |
161 def _historyCb(self, text): | 155 def _historyCb(self, text): |
165 def keypress(self, size, key): | 159 def keypress(self, size, key): |
166 """Callback when a key is pressed. Send "composing" states | 160 """Callback when a key is pressed. Send "composing" states |
167 and move the index of the temporary history stack.""" | 161 and move the index of the temporary history stack.""" |
168 if key == a_key['MODAL_ESCAPE']: | 162 if key == a_key['MODAL_ESCAPE']: |
169 # first save the text to the current mode, then change to NORMAL | 163 # first save the text to the current mode, then change to NORMAL |
170 self.app._updateInputHistory(self.get_edit_text(), mode=self.mode) | 164 self.host._updateInputHistory(self.get_edit_text(), mode=self.mode) |
171 self.app._updateInputHistory(mode='NORMAL') | 165 self.host._updateInputHistory(mode=C.MODE_NORMAL) |
172 if self._mode == 'NORMAL' and key in self._modes: | 166 if self._mode == C.MODE_NORMAL and key in self._modes: |
173 self.app._updateInputHistory(mode=self._modes[key][0]) | 167 self.host._updateInputHistory(mode=self._modes[key][0]) |
174 if key == a_key['HISTORY_PREV']: | 168 if key == a_key['HISTORY_PREV']: |
175 self.app._updateInputHistory(self.get_edit_text(), -1, self._historyCb, self.mode) | 169 self.host._updateInputHistory(self.get_edit_text(), -1, self._historyCb, self.mode) |
176 return | 170 return |
177 elif key == a_key['HISTORY_NEXT']: | 171 elif key == a_key['HISTORY_NEXT']: |
178 self.app._updateInputHistory(self.get_edit_text(), +1, self._historyCb, self.mode) | 172 self.host._updateInputHistory(self.get_edit_text(), +1, self._historyCb, self.mode) |
179 return | 173 return |
180 elif key == a_key['EDIT_ENTER']: | 174 elif key == a_key['EDIT_ENTER']: |
181 self.app._updateInputHistory(self.get_edit_text(), mode=self.mode) | 175 self.host._updateInputHistory(self.get_edit_text(), mode=self.mode) |
182 else: | 176 else: |
183 contact = self.app.contact_list.getContact() | 177 if (self._mode == C.MODE_INSERTION |
184 if contact: | 178 and isinstance(self.host.selected_widget, quick_chat.QuickChat) |
185 self.app.bridge.chatStateComposing(unescapePrivate(contact), self.app.profile) | 179 and key not in sat_widgets.FOCUS_KEYS): |
180 self.host.bridge.chatStateComposing(self.host.selected_widget.target, self.host.selected_widget.profile) | |
181 | |
186 return super(EditBar, self).keypress(size, key) | 182 return super(EditBar, self).keypress(size, key) |
187 | 183 |
188 | 184 |
189 class PrimitivusTopWidget(sat_widgets.FocusPile): | 185 class PrimitivusTopWidget(sat_widgets.FocusPile): |
190 """Top most widget used in Primitivus""" | 186 """Top most widget used in Primitivus""" |
285 | 281 |
286 ## main loop setup ## | 282 ## main loop setup ## |
287 self.main_widget = ProfileManager(self) | 283 self.main_widget = ProfileManager(self) |
288 self.loop = urwid.MainLoop(self.main_widget, C.PALETTE, event_loop=urwid.GLibEventLoop(), input_filter=self.inputFilter, unhandled_input=self.keyHandler) | 284 self.loop = urwid.MainLoop(self.main_widget, C.PALETTE, event_loop=urwid.GLibEventLoop(), input_filter=self.inputFilter, unhandled_input=self.keyHandler) |
289 | 285 |
286 | |
290 ##misc setup## | 287 ##misc setup## |
291 self.chat_wins = ChatList(self) | |
292 self.notif_bar = sat_widgets.NotificationBar() | 288 self.notif_bar = sat_widgets.NotificationBar() |
293 urwid.connect_signal(self.notif_bar, 'change', self.onNotification) | 289 urwid.connect_signal(self.notif_bar, 'change', self.onNotification) |
294 self.progress_wid = Progress(self) | 290 |
295 urwid.connect_signal(self.notif_bar.progress, 'click', lambda x: self.addWindow(self.progress_wid)) | 291 self.progress_wid = self.widgets.getOrCreateWidget(Progress, None, on_new_widget=None) |
292 urwid.connect_signal(self.notif_bar.progress, 'click', lambda x: self.selectWidget(self.progress_wid)) | |
296 self.__saved_overlay = None | 293 self.__saved_overlay = None |
297 | 294 |
298 self.x_notify = Notify() | 295 self.x_notify = Notify() |
299 | 296 |
300 # we already manage exit with a_key['APP_QUIT'], so we don't want C-c | 297 # we already manage exit with a_key['APP_QUIT'], so we don't want C-c |
349 popup = sat_widgets.Alert(_("Configuration Error"), _("Something went wrong while reading the configuration, please check :messages"), ok_cb=self.removePopUp) | 346 popup = sat_widgets.Alert(_("Configuration Error"), _("Something went wrong while reading the configuration, please check :messages"), ok_cb=self.removePopUp) |
350 if self.options.profile: | 347 if self.options.profile: |
351 self._early_popup = popup | 348 self._early_popup = popup |
352 else: | 349 else: |
353 self.showPopUp(popup) | 350 self.showPopUp(popup) |
354 super(PrimitivusApp, self).postInit() | 351 super(PrimitivusApp, self).postInit(self.main_widget) |
355 | 352 |
356 def inputFilter(self, input_, raw): | 353 def inputFilter(self, input_, raw): |
357 if self.__saved_overlay and input_ != a_key['OVERLAY_HIDE']: | 354 if self.__saved_overlay and input_ != a_key['OVERLAY_HIDE']: |
358 return | 355 return |
359 for i in input_: | 356 for i in input_: |
385 self.loop.widget = self.__saved_overlay | 382 self.loop.widget = self.__saved_overlay |
386 self.__saved_overlay = None | 383 self.__saved_overlay = None |
387 | 384 |
388 elif input_ == a_key['DEBUG'] and 'D' in self.bridge.getVersion(): #Debug only for dev versions | 385 elif input_ == a_key['DEBUG'] and 'D' in self.bridge.getVersion(): #Debug only for dev versions |
389 self.debug() | 386 self.debug() |
390 elif input_ == a_key['CONTACTS_HIDE']: #user wants to (un)hide the contact_list | 387 elif input_ == a_key['CONTACTS_HIDE']: #user wants to (un)hide the contact lists |
391 try: | 388 try: |
392 for wid, options in self.center_part.contents: | 389 for wid, options in self.center_part.contents: |
393 if self.contact_list is wid: | 390 if self.contact_lists_pile is wid: |
394 self.center_part.contents.remove((wid, options)) | 391 self.center_part.contents.remove((wid, options)) |
395 break | 392 break |
396 else: | 393 else: |
397 self.center_part.contents.insert(0, (self.contact_list, ('weight', 2, False))) | 394 self.center_part.contents.insert(0, (self.contact_lists_pile, ('weight', 2, False))) |
398 except AttributeError: | 395 except AttributeError: |
399 #The main widget is not built (probably in Profile Manager) | 396 #The main widget is not built (probably in Profile Manager) |
400 pass | 397 pass |
401 elif input_ == 'window resize': | 398 elif input_ == 'window resize': |
402 width,height = self.loop.screen_size | 399 width,height = self.loop.screen_size |
411 try: | 408 try: |
412 return self.menu_roller.checkShortcuts(input_) | 409 return self.menu_roller.checkShortcuts(input_) |
413 except AttributeError: | 410 except AttributeError: |
414 return input_ | 411 return input_ |
415 | 412 |
416 def addMenus(self, menu, type_, menu_data=None): | 413 def addMenus(self, menu, type_filter, menu_data=None): |
417 """Add cached menus to instance | 414 """Add cached menus to instance |
418 @param menu: sat_widgets.Menu instance | 415 @param menu: sat_widgets.Menu instance |
419 @param type_: menu type like is sat.core.sat_main.importMenu | 416 @param type_filter: menu type like is sat.core.sat_main.importMenu |
420 @param menu_data: data to send with these menus | 417 @param menu_data: data to send with these menus |
421 | 418 |
422 """ | 419 """ |
423 menus = self.profiles[self.profile]['menus'].get(type_,[]) | |
424 def add_menu_cb(callback_id): | 420 def add_menu_cb(callback_id): |
425 self.launchAction(callback_id, menu_data, profile_key = self.profile) | 421 self.launchAction(callback_id, menu_data, profile=self.current_profile) |
426 for id_, path, path_i18n in menus: | 422 for id_, type_, path, path_i18n in self.bridge.getMenus("", C.NO_SECURITY_LIMIT ): |
423 if type_ != type_filter: | |
424 continue | |
427 if len(path) != 2: | 425 if len(path) != 2: |
428 raise NotImplementedError("Menu with a path != 2 are not implemented yet") | 426 raise NotImplementedError("Menu with a path != 2 are not implemented yet") |
429 menu.addMenu(path_i18n[0], path_i18n[1], lambda dummy,id_=id_: add_menu_cb(id_)) | 427 menu.addMenu(path_i18n[0], path_i18n[1], lambda dummy,id_=id_: add_menu_cb(id_)) |
430 | 428 |
431 | 429 |
443 menu.addMenu(communication, _("Join room"), self.onJoinRoomRequest, a_key['ROOM_JOIN']) | 441 menu.addMenu(communication, _("Join room"), self.onJoinRoomRequest, a_key['ROOM_JOIN']) |
444 #additionals menus | 442 #additionals menus |
445 #FIXME: do this in a more generic way (in quickapp) | 443 #FIXME: do this in a more generic way (in quickapp) |
446 self.addMenus(menu, C.MENU_GLOBAL) | 444 self.addMenus(menu, C.MENU_GLOBAL) |
447 | 445 |
448 menu_roller = sat_widgets.MenuRoller([(_('Main menu'),menu)]) | 446 menu_roller = sat_widgets.MenuRoller([(_('Main menu'), menu, C.MENU_ID_MAIN)]) |
449 return menu_roller | 447 return menu_roller |
450 | 448 |
451 def _buildMainWidget(self): | 449 def _buildMainWidget(self): |
452 self.contact_list = ContactList(self, on_click=self.contactSelected, on_change=lambda w: self.redraw()) | 450 self.contact_lists_pile = urwid.Pile([]) |
453 #self.center_part = urwid.Columns([('weight',2,self.contact_list),('weight',8,Chat('',self))]) | 451 #self.center_part = urwid.Columns([('weight',2,self.contact_lists[profile]),('weight',8,Chat('',self))]) |
454 self.center_part = urwid.Columns([('weight', 2, self.contact_list), ('weight', 8, urwid.Filler(urwid.Text('')))]) | 452 self.center_part = urwid.Columns([('weight', 2, self.contact_lists_pile), ('weight', 8, urwid.Filler(urwid.Text('')))]) |
455 | 453 |
456 self.editBar = EditBar(self) | 454 self.editBar = EditBar(self) |
457 self.menu_roller = self._buildMenuRoller() | 455 self.menu_roller = self._buildMenuRoller() |
458 self.main_widget = PrimitivusTopWidget(self.center_part, self.menu_roller, self.notif_bar, self.editBar) | 456 self.main_widget = PrimitivusTopWidget(self.center_part, self.menu_roller, self.notif_bar, self.editBar) |
459 return self.main_widget | 457 return self.main_widget |
460 | 458 |
461 def plug_profile_1(self, profile_key='@DEFAULT@'): | 459 def addContactList(self, profile): |
460 contact_list = ContactList(self, on_click=self.contactSelected, on_change=lambda w: self.redraw(), profile=profile) | |
461 self.contact_lists_pile.contents.append((contact_list, ('weight', 1))) | |
462 self.contact_lists[profile] = contact_list | |
463 return contact_list | |
464 | |
465 def plugging_profiles(self): | |
462 self.loop.widget = self._buildMainWidget() | 466 self.loop.widget = self._buildMainWidget() |
463 self.redraw() | 467 self.redraw() |
464 QuickApp.plug_profile_1(self, profile_key) | |
465 try: | 468 try: |
466 # if a popup arrived before main widget is build, we need to show it now | 469 # if a popup arrived before main widget is build, we need to show it now |
467 self.showPopUp(self._early_popup) | 470 self.showPopUp(self._early_popup) |
468 except AttributeError: | 471 except AttributeError: |
469 pass | 472 pass |
490 def notify(self, message): | 493 def notify(self, message): |
491 """"Notify message to user via notification bar""" | 494 """"Notify message to user via notification bar""" |
492 self.notif_bar.addMessage(message) | 495 self.notif_bar.addMessage(message) |
493 self.redraw() | 496 self.redraw() |
494 | 497 |
495 def addWindow(self, widget): | 498 def newWidget(self, widget): |
496 """Display a window if possible, | 499 """Display a widget if possible, |
500 | |
497 else add it in the notification bar queue | 501 else add it in the notification bar queue |
498 @param widget: BoxWidget""" | 502 @param widget: BoxWidget |
499 assert(len(self.center_part.widget_list)<=2) | 503 """ |
504 self.selectWidget(widget) | |
505 | |
506 def selectWidget(self, widget): | |
507 assert len(self.center_part.widget_list)<=2 | |
500 wid_idx = len(self.center_part.widget_list)-1 | 508 wid_idx = len(self.center_part.widget_list)-1 |
501 self.center_part.widget_list[wid_idx] = widget | 509 self.center_part.widget_list[wid_idx] = widget |
502 self.menu_roller.removeMenu(_('Chat menu')) | 510 try: |
503 self.contact_list.unselectAll() | 511 self.menu_roller.removeMenu(C.MENU_ID_WIDGET) |
512 except KeyError: | |
513 log.debug("No menu to delete") | |
514 self.selected_widget = widget | |
515 self.visible_widgets = set([widget]) # XXX: we can only have one widget visible at the time for now | |
516 for contact_list in self.contact_lists.itervalues(): | |
517 contact_list.unselectAll() | |
518 | |
519 for wid in self.visible_widgets: | |
520 if isinstance(wid, Chat): | |
521 contact_list = self.contact_lists[wid.profile] | |
522 contact_list.select(wid.target) | |
523 | |
504 self.redraw() | 524 self.redraw() |
505 | 525 |
506 def removeWindow(self): | 526 def removeWindow(self): |
507 """Remove window showed on the right column""" | 527 """Remove window showed on the right column""" |
508 #TODO: to a better Window management than this crappy hack | 528 #TODO: to a better Window management than this crappy hack |
520 | 540 |
521 def setProgress(self, percentage): | 541 def setProgress(self, percentage): |
522 """Set the progression shown in notification bar""" | 542 """Set the progression shown in notification bar""" |
523 self.notif_bar.setProgress(percentage) | 543 self.notif_bar.setProgress(percentage) |
524 | 544 |
525 def contactSelected(self, contact_list): | 545 def contactSelected(self, contact_list, entity): |
526 contact = contact_list.getContact() | 546 if entity.resource: |
527 if contact: | 547 # we have clicked on a private MUC conversation |
528 assert(len(self.center_part.widget_list)==2) | 548 chat_widget = self.widgets.getOrCreateWidget(Chat, entity, on_new_widget=None, force_hash = Chat.getPrivateHash(contact_list.profile, entity), profile=contact_list.profile) |
529 self.center_part.widget_list[1] = self.chat_wins[contact] | 549 else: |
530 self.menu_roller.addMenu(_('Chat menu'), self.chat_wins[contact].getMenu()) | 550 chat_widget = self.widgets.getOrCreateWidget(Chat, entity, on_new_widget=None, profile=contact_list.profile) |
531 | 551 self.selectWidget(chat_widget) |
532 def newMessageHandler(self, from_jid, to_jid, msg, _type, extra, profile): | 552 self.menu_roller.addMenu(_('Chat menu'), chat_widget.getMenu(), C.MENU_ID_WIDGET) |
533 QuickApp.newMessageHandler(self, from_jid, to_jid, msg, _type, extra, profile) | 553 |
534 | 554 def newMessageHandler(self, from_jid, to_jid, msg, type_, extra, profile): |
535 if not from_jid in self.contact_list and from_jid.bare != self.profiles[profile]['whoami'].bare: | 555 QuickApp.newMessageHandler(self, from_jid, to_jid, msg, type_, extra, profile) |
556 | |
557 if not from_jid in self.contact_lists[profile] and from_jid.bare != self.profiles[profile].whoami.bare: | |
536 #XXX: needed to show entities which haven't sent any | 558 #XXX: needed to show entities which haven't sent any |
537 # presence information and which are not in roster | 559 # presence information and which are not in roster |
538 self.contact_list.replace(from_jid, [C.GROUP_NOT_IN_ROSTER]) | 560 self.contact_lists[profile].setContact(from_jid) |
539 | 561 visible = False |
540 if self.contact_list.selected is None or JID(self.contact_list.selected).bare != from_jid.bare: | 562 for widget in self.visible_widgets: |
541 self.contact_list.putAlert(from_jid) | 563 if isinstance(widget, Chat) and widget.manageMessage(from_jid, type_): |
564 visible = True | |
565 break | |
566 if not visible: | |
567 self.contact_lists[profile].setAlert(from_jid.bare if type_ == C.MESS_TYPE_GROUPCHAT else from_jid) | |
542 | 568 |
543 def _dialogOkCb(self, widget, data): | 569 def _dialogOkCb(self, widget, data): |
544 self.removePopUp() | 570 self.removePopUp() |
545 answer_cb = data[0] | 571 answer_cb = data[0] |
546 answer_data = [data[1]] if data[1] else [] | 572 answer_data = [data[1]] if data[1] else [] |
549 def _dialogCancelCb(self, widget, data): | 575 def _dialogCancelCb(self, widget, data): |
550 self.removePopUp() | 576 self.removePopUp() |
551 answer_cb = data[0] | 577 answer_cb = data[0] |
552 answer_data = [data[1]] if data[1] else [] | 578 answer_data = [data[1]] if data[1] else [] |
553 answer_cb(False, *answer_data) | 579 answer_cb(False, *answer_data) |
554 | |
555 | 580 |
556 def showDialog(self, message, title="", type_="info", answer_cb = None, answer_data = None): | 581 def showDialog(self, message, title="", type_="info", answer_cb = None, answer_data = None): |
557 if type_ == 'info': | 582 if type_ == 'info': |
558 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 | 583 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 |
559 elif type_ == 'error': | 584 elif type_ == 'error': |
576 #No notification left, we can hide the bar | 601 #No notification left, we can hide the bar |
577 self.main_widget.hide('notif_bar') | 602 self.main_widget.hide('notif_bar') |
578 else: | 603 else: |
579 self.main_widget.show('notif_bar') | 604 self.main_widget.show('notif_bar') |
580 | 605 |
581 def launchAction(self, callback_id, data=None, profile_key="@NONE@"): | 606 def launchAction(self, callback_id, data=None, callback=None, profile=C.PROF_KEY_NONE): |
582 """ Launch a dynamic action | 607 """ Launch a dynamic action |
583 @param callback_id: id of the action to launch | 608 @param callback_id: id of the action to launch |
584 @param data: data needed only for certain actions | 609 @param data: data needed only for certain actions |
585 @param profile_key: %(doc_profile_key)s | 610 @param callback: if not None and 'validated' key is present, it will be called with the following parameters: |
611 - callback_id | |
612 - data | |
613 - profile | |
614 @param profile: %(doc_profile)s | |
586 | 615 |
587 """ | 616 """ |
588 if data is None: | 617 if data is None: |
589 data = dict() | 618 data = dict() |
590 | 619 |
591 def action_cb(data): | 620 def action_cb(data): |
592 if not data: | 621 if not data: |
593 # action was a one shot, nothing to do | 622 # action was a one shot, nothing to do |
594 pass | 623 pass |
595 elif "xmlui" in data: | 624 elif "xmlui" in data: |
596 ui = xmlui.create(self, xml_data=data['xmlui']) | 625 ui = xmlui.create(self, xml_data=data['xmlui'], callback=callback, profile=profile) |
597 ui.show() | 626 ui.show() |
598 elif "authenticated_profile" in data: | 627 elif 'validated' in data: |
599 assert("caller" in data) | 628 pass # this key is managed below |
600 if data["caller"] == "profile_manager": | |
601 assert(isinstance(self.main_widget, ProfileManager)) | |
602 self.main_widget.getXMPPParams(data['authenticated_profile']) | |
603 elif data["caller"] == "plug_profile": | |
604 self.plug_profile_1(data['authenticated_profile']) | |
605 else: | |
606 raise NotImplementedError | |
607 else: | 629 else: |
608 self.showPopUp(sat_widgets.Alert(_("Error"), _(u"Unmanaged action result"), ok_cb=self.removePopUp)) | 630 self.showPopUp(sat_widgets.Alert(_("Error"), _(u"Unmanaged action result"), ok_cb=self.removePopUp)) |
609 | 631 |
632 if callback and 'validated' in data: | |
633 callback(callback_id, data, profile) | |
634 | |
610 def action_eb(failure): | 635 def action_eb(failure): |
611 self.showPopUp(sat_widgets.Alert(failure.fullname, failure.message, ok_cb=self.removePopUp)) | 636 self.showPopUp(sat_widgets.Alert(failure.fullname, failure.message, ok_cb=self.removePopUp)) |
612 | 637 |
613 self.bridge.launchAction(callback_id, data, profile_key, callback=action_cb, errback=action_eb) | 638 self.bridge.launchAction(callback_id, data, profile, callback=action_cb, errback=action_eb) |
614 | 639 |
615 def askConfirmationHandler(self, confirmation_id, confirmation_type, data, profile): | 640 def askConfirmationHandler(self, confirmation_id, confirmation_type, data, profile): |
616 answer_data={} | 641 answer_data={} |
617 | 642 |
618 def dir_selected_cb(path): | 643 def dir_selected_cb(path): |
682 callback(data) | 707 callback(data) |
683 else: | 708 else: |
684 log.error (_("FIXME FIXME FIXME: type [%s] not implemented") % type_) | 709 log.error (_("FIXME FIXME FIXME: type [%s] not implemented") % type_) |
685 raise NotImplementedError | 710 raise NotImplementedError |
686 | 711 |
712 | |
713 def roomJoinedHandler(self, room_jid_s, room_nicks, user_nick, profile): | |
714 super(PrimitivusApp, self).roomJoinedHandler(room_jid_s, room_nicks, user_nick, profile) | |
715 self.contact_lists[profile].setFocus(jid.JID(room_jid_s), True) | |
716 | |
717 | |
718 | |
687 ##DIALOGS CALLBACKS## | 719 ##DIALOGS CALLBACKS## |
688 def onJoinRoom(self, button, edit): | 720 def onJoinRoom(self, button, edit): |
689 self.removePopUp() | 721 self.removePopUp() |
690 room_jid = JID(edit.get_edit_text()) | 722 room_jid = jid.JID(edit.get_edit_text()) |
691 if room_jid.is_valid(): | 723 if room_jid.is_valid(): |
692 self.bridge.joinMUC(room_jid, self.profiles[self.profile]['whoami'].node, {}, self.profile) | 724 self.bridge.joinMUC(room_jid, self.profiles[self.current_profile].whoami.node, {}, self.current_profile) |
693 else: | 725 else: |
694 message = _("'%s' is an invalid JID !") % room_jid | 726 message = _("'%s' is an invalid jid.JID !") % room_jid |
695 log.error (message) | 727 log.error (message) |
696 self.showPopUp(sat_widgets.Alert(_("Error"), message, ok_cb=self.removePopUp)) | 728 self.showPopUp(sat_widgets.Alert(_("Error"), message, ok_cb=self.removePopUp)) |
697 | 729 |
698 #MENU EVENTS# | 730 #MENU EVENTS# |
699 def onConnectRequest(self, menu): | 731 def onConnectRequest(self, menu): |
700 QuickApp.asyncConnect(self, self.profile) | 732 QuickApp.asyncConnect(self, self.current_profile) |
701 | 733 |
702 def onDisconnectRequest(self, menu): | 734 def onDisconnectRequest(self, menu): |
703 self.bridge.disconnect(self.profile) | 735 self.bridge.disconnect(self.current_profile) |
704 | 736 |
705 def onParam(self, menu): | 737 def onParam(self, menu): |
706 def success(params): | 738 def success(params): |
707 ui = xmlui.create(self, xml_data=params) | 739 ui = xmlui.create(self, xml_data=params) |
708 ui.show() | 740 ui.show() |
709 | 741 |
710 def failure(error): | 742 def failure(error): |
711 self.showPopUp(sat_widgets.Alert(_("Error"), _("Can't get parameters (%s)") % error, ok_cb=self.removePopUp)) | 743 self.showPopUp(sat_widgets.Alert(_("Error"), _("Can't get parameters (%s)") % error, ok_cb=self.removePopUp)) |
712 self.bridge.getParamsUI(app=C.APP_NAME, profile_key=self.profile, callback=success, errback=failure) | 744 self.bridge.getParamsUI(app=C.APP_NAME, profile_key=self.current_profile, callback=success, errback=failure) |
713 | 745 |
714 def onExitRequest(self, menu): | 746 def onExitRequest(self, menu): |
715 QuickApp.onExit(self) | 747 QuickApp.onExit(self) |
716 raise urwid.ExitMainLoop() | 748 raise urwid.ExitMainLoop() |
717 | 749 |
723 def onAboutRequest(self, menu): | 755 def onAboutRequest(self, menu): |
724 self.showPopUp(sat_widgets.Alert(_("About"), C.APP_NAME + " v" + self.bridge.getVersion(), ok_cb=self.removePopUp)) | 756 self.showPopUp(sat_widgets.Alert(_("About"), C.APP_NAME + " v" + self.bridge.getVersion(), ok_cb=self.removePopUp)) |
725 | 757 |
726 #MISC CALLBACKS# | 758 #MISC CALLBACKS# |
727 | 759 |
728 def setStatusOnline(self, online=True, show="", statuses={}): | 760 def setStatusOnline(self, online=True, show="", statuses={}, profile=C.PROF_KEY_NONE): |
729 if not online or not statuses: | 761 if not online or not statuses: |
730 self.status_bar.setPresenceStatus(show if online else 'unavailable', '') | 762 self.contact_lists[profile].status_bar.setPresenceStatus(show if online else 'unavailable', '') |
731 return | 763 return |
732 try: | 764 try: |
733 self.status_bar.setPresenceStatus(show, statuses['default']) | 765 self.contact_lists[profile].status_bar.setPresenceStatus(show, statuses['default']) |
734 except (KeyError, TypeError): | 766 except (KeyError, TypeError): |
735 pass | 767 pass |
736 | 768 |
737 sat = PrimitivusApp() | 769 sat = PrimitivusApp() |
738 sat.start() | 770 sat.start() |