mirror of git://git.psyced.org/git/pypsyc
384 lines
14 KiB
Python
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()
|