""" pypsyc.client.controller ~~~~~~~~~~~~~~~~~~~~~~~~ Classes which connect the models with the views. They observe the models and update the view. They are injected into the views and have methods that can change the models after user input validation. :copyright: 2010 by Manuel Jacob :license: MIT """ from functools import partial from pypsyc.client.model import Account from pypsyc.client.observable import ObsList from pypsyc.util import schedule def _observe_list(obs_list, insert, delitem, update, *args): if insert: for i, v in enumerate(obs_list): insert(i, v, *args) obs_list.insert_evt.add_observer(insert, *args) if delitem is not None: obs_list.delitem_evt.add_observer(delitem) if update is not None: obs_list.update_evt += update def _observe_dict(obs_dict, setitem, delitem, update, *args): if setitem is not None: for k, v in obs_dict.iteritems(): setitem(k, None, v, *args) obs_dict.setitem_evt.add_observer(setitem, *args) if delitem is not None: obs_dict.delitem_evt += delitem if update is not None: obs_dict.update_evt += update def _unobserve_list(obs_list, insert, delitem, update): if insert is not None: obs_list.insert_evt -= insert if delitem is not None: obs_list.delitem_evt -= delitem for v in obs_list: delitem(0, v) if update is not None: obs_list.update_evt -= update def _unobserve_dict(obs_dict, setitem, delitem, update): if setitem is not None: obs_dict.setitem_evt -= setitem if delitem is not None: obs_dict.delitem_evt -= delitem for k, v in obs_dict.iteritems(): delitem(k, v) if update is not None: obs_dict.update_evt -= update class ListBinding(object): def __init__(self, model_list, view_list, make_row): self.model_list = model_list self.view_list = view_list self.make_row = make_row _observe_list(model_list, self._add, self._del, self._update) def unbind(self): _unobserve_list(self.model_list, self._add, self._del, self._update) def _add(self, index, obj): self.view_list.append(self.make_row(obj)) def _del(self, index, obj): del self.view_list[index] def _update(self, index, obj): self.view_list[index] = self.make_row(obj) class AccountsController(object): def __init__(self, client, view): self.client = client self.view = view view.controller = self self.binding = ListBinding(self.client.accounts, view.accounts, lambda obj: (obj.uni, obj.active)) def add_account(self): account = Account(self.client, "", "", "", False, False) self.view.show_addedit_dialog(account.__dict__, True, partial(self._saved, account, True)) def edit_account(self, pos): account = self.client.accounts[pos] self.view.show_addedit_dialog(account.__dict__, False, partial(self._saved, account, False)) def _saved(self, account, add): if add: self.client.accounts.append(account) else: self.client.accounts.updated_item(account) self.client.save_accounts() def remove_account(self, pos): del self.client.accounts[pos] self.client.save_accounts() def set_active(self, pos, active): account = self.client.accounts[pos] account.active = active self.client.accounts.updated_item(account) self.client.save_accounts() def closed(self): self.binding.unbind() class DumpController(object): def __init__(self, client, view): self.view = view view.controller = self self.accounts = client.accounts _observe_list(self.accounts, self._add, self._del, None) def _add(self, index, account): self._update(None, getattr(account, 'circuit', None), account.uni) account.update_evt['circuit'].add_observer(self._update, account.uni) def _del(self, index, account): account.update_evt['circuit'] -= self._update if hasattr(account, 'circuit'): account.circuit.dump_evt -= self.view.show_line def _update(self, old, value, account_uni): if old is not None: old.dump_evt -= self.view.show_line if value is not None: value.dump_evt.add_observer(self.view.show_line, account_uni) def closed(self): _unobserve_list(self.accounts, self._add, self._del, None) class ConversationController(object): def __init__(self, tabs_controller, conversation, view): self.tabs_controller = tabs_controller self.conversation = conversation self.view = view view.controller = self conversation.unknown_target_evt += view.show_unknown_target conversation.delivery_failed_evt += view.show_delivery_failed _observe_list(conversation.messages, self.add_message, None, None) def add_message(self, index, message): line = "(%s) <%s> %s" % (message.time.strftime("%H:%M:%S"), message.source, message.message) self.view.show_message(line) def enter(self, text): if text.startswith('/'): self.view.show_unknown_command() else: self.conversation.send_message(text) def closed(self): _unobserve_list(self.conversation.messages, self.add_message, None, None) self.conversation.unknown_target_evt -= self.view.show_unknown_target self.conversation.delivery_failed_evt -= self.view.show_delivery_failed class ConferenceController(ConversationController): def __init__(self, tabs_controller, conference, view): self.conference = conference ConversationController.__init__(self, tabs_controller, conference, view) self.binding = ListBinding(conference.members, view.members, lambda member: (member.uni, member.nick)) def open_conversation(self, pos): account = self.conference.conferencing.account member = self.conference.members[pos] conversation = account.get_conversation(member.uni) self.tabs_controller.focus_conversation(conversation) def closed(self): self.binding.unbind() ConversationController.closed(self) class TabsController(object): def __init__(self, client, view): self.view = view view.controller = self self.view_to_model = {} self.model_to_controller = {} _observe_list(client.accounts, self._add_account, self._del_account, None) def _add_account(self, index, account): _observe_dict(account.conversations, self._add_conversation, self._del_conversation, None, account.conversations, self.view.show_conversation, ConversationController) _observe_dict(account.conferences, self._add_conversation, self._del_conversation, None, account.conferences, self.view.show_conference, ConferenceController) def _del_account(self, index, account): _unobserve_dict(account.conversations, self._add_conversation, self._del_conversation, None) _unobserve_dict(account.conferences, self._add_conversation, self._del_conversation, None) def _add_conversation(self, uni, old, conversation, list, show, Class): conv_view = show(conversation.uni) controller = Class(self, conversation, conv_view) self.view_to_model[conv_view] = list, conversation.uni self.model_to_controller[conversation] = controller, conv_view def _del_conversation(self, uni, conversation): controller, view = self.model_to_controller.pop(conversation) del self.view_to_model[view] self.view.remove_tab(view) controller.closed() def focus_conversation(self, conversation): self.view.focus_tab(self.model_to_controller[conversation][1]) def close_tab(self, view): list, uni = self.view_to_model[view] del list[uni] class FriendListController(object): def __init__(self, client, view, tabs_controller): self.view = view view.controller = self self.tabs_controller = tabs_controller self.friends = ObsList() self.binding = ListBinding(self.friends, view.friends, self._make_row) _observe_list(client.accounts, self._add_account, self._del_account, None) def _add_account(self, index, account): _observe_dict(account.friends, self._add_friend, self._del_friend, self._update_friend) def _del_account(self, index, account): _unobserve_dict(account.friends, self._add_friend, self._del_friend, self._update_friend) def _add_friend(self, uni, old, friend): self.friends.append(friend) def _del_friend(self, uni, friend): self.friends.remove(friend) def _update_friend(self, uni, friend): self.friends.updated_item(friend) def _make_row(self, friend): uni = friend.uni if uni.startswith('psyc://' + friend.account.server + '/'): uni = uni.rpartition('/~')[2] return (uni, friend.presence.availability > 1, friend.state) def open_conversation(self, pos): friend = self.friends[pos] model = friend.account.get_conversation(friend.uni) self.tabs_controller.focus_conversation(model) def accept_friendship(self, pos): friend = self.friends[pos] schedule(friend.account.add_friend, friend.uni) def cancel_friendship(self, pos): friend = self.friends[pos] schedule(friend.account.remove_friend, friend.uni) class MainController(object): def __init__(self, client, view): self.client = client self.view = view view.controller = self self.active_accounts = set() self.tabs_controller = TabsController(client, view.tabs_view) FriendListController(client, view.friends_view, self.tabs_controller) _observe_list(client.accounts, self._add_account, self._del_account, self._update_account) def _add_account(self, index, account): account.no_password_evt.add_observer(self._no_password, account) account.connection_error_evt.add_observer(self._conn_error, account) account.no_such_user_evt += self._no_such_user account.auth_error_evt.add_observer(self._auth_error, account) if account.active: self._account_activated(account) def _del_account(self, index, account): account.no_password_evt -= self._no_password account.connection_error_evt -= self._conn_error account.no_such_user_evt -= self._no_such_user account.auth_error_evt -= self._auth_error if account.active: self._account_deactivated(account) def _update_account(self, index, account): if account.active: self._account_activated(account) else: self._account_deactivated(account) def _account_activated(self, account): had_active_accounts = bool(self.active_accounts) self.active_accounts.add(account) if not had_active_accounts: self.view.show_active_accounts(True) def _account_deactivated(self, account): had_active_accounts = bool(self.active_accounts) self.active_accounts.discard(account) if had_active_accounts and not self.active_accounts: self.view.show_active_accounts(False) def _no_password(self, account): def callback(): account.active = True self.client.accounts.updated_item(account) self.client.save_accounts() self.view.show_password_dialog(account.uni, account.__dict__, callback) def _conn_error(self, error, account): self.view.show_conn_error(account.uni, error.args[0]) def _no_such_user(self, error): self.view.show_no_such_user(error.args[0]) def _auth_error(self, error, account): self.view.show_auth_error(account.uni, error.args[0]) def open_accounts(self): AccountsController(self.client, self.view.show_accounts()) def open_dump(self): DumpController(self.client, self.view.show_dump_win()) def open_conversation(self): accounts = [acc for acc in self.client.accounts if acc.active] def callback(account, server, person): uni = 'psyc://%s/~%s' % (server, person) model = accounts[account].get_conversation(uni) self.tabs_controller.focus_conversation(model) self.view.show_open_conv_dialog((account.uni for account in accounts), callback) def open_conference(self): accounts = [acc for acc in self.client.accounts if acc.active] def callback(account, server, place): uni = 'psyc://%s/@%s' % (server, place) model = accounts[account].get_conference(uni, subscribe=True) self.tabs_controller.focus_conversation(model) self.view.show_open_conf_dialog((account.uni for account in accounts), callback) def add_friend(self): accounts = [acc for acc in self.client.accounts if acc.active] def callback(account, server, person): uni = 'psyc://%s/~%s' % (server, person) schedule(accounts[account].add_friend, uni) self.view.show_add_friend_dialog((account.uni for account in accounts), callback) def quit(self): _unobserve_list(self.client.accounts, self._add_account, self._del_account, self._update_account) self.client.quit()