pypsyc/mjacob2/pypsyc/client/controller.py

384 lines
14 KiB
Python

"""
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()