From a6c56682b74acb7ef568913bbc964b4f17cf3202 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Sun, 10 Apr 2011 03:52:46 +0200 Subject: Implement paused chate state. fixed #2124 --- src/core.py | 2 +- src/keyboard.py | 2 +- src/tabs.py | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++- src/timed_events.py | 15 ++++++++++++++- 4 files changed, 69 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/core.py b/src/core.py index 8629ecc8..76f5026e 100644 --- a/src/core.py +++ b/src/core.py @@ -629,7 +629,6 @@ class Core(object): """ res = read_char(self.stdscr) while res is None: - log.debug('checking events') self.check_timed_events() res = read_char(self.stdscr) return res @@ -707,6 +706,7 @@ class Core(object): if not options.debug: curses.raw() theme.init_colors() + stdscr.idlok(True) stdscr.keypad(True) curses.ungetch(" ") # H4X: without this, the screen is stdscr.getkey() # erased on the first "getkey()" diff --git a/src/keyboard.py b/src/keyboard.py index cca854fa..8688d439 100644 --- a/src/keyboard.py +++ b/src/keyboard.py @@ -51,7 +51,7 @@ def read_char(s): # last_char_time = time.time() s.timeout(1000) (first, char) = get_next_byte(s) - if first is None: + if first is None and char is None: return None if not isinstance(first, int): # Keyboard special, like KEY_HOME etc return char diff --git a/src/tabs.py b/src/tabs.py index c84ba0fa..ac434745 100644 --- a/src/tabs.py +++ b/src/tabs.py @@ -40,6 +40,8 @@ import common import core import singleton import xhtml +import weakref +import timed_events import multiuserchat as muc @@ -50,6 +52,8 @@ from contact import Contact, Resource from user import User from logger import logger +from datetime import datetime, timedelta + SHOW_NAME = { 'dnd': _('busy'), 'away': _('away'), @@ -245,6 +249,12 @@ class ChatTab(Tab): # we know that the remote user wants chatstates, or not. # None means we don’t know yet, and we send only "active" chatstates self.chatstate = None # can be "active", "composing", "paused", "gone", "inactive" + # We keep a weakref of the event that will set our chatstate to "paused", so that + # we can delete it or change it if we need to + self.timed_event_paused = None + # if that’s None, then no paused chatstate was sent recently + # if that’s a weakref returning None, then a paused chatstate was sent + # since the last input self.key_func['M-/'] = self.last_words_completion self.key_func['^M'] = self.on_enter self.commands['say'] = (self.command_say, @@ -281,6 +291,7 @@ class ChatTab(Tab): """ Send an empty chatstate message """ + state = '%s' % state msg = self.core.xmpp.make_message(self.get_name()) msg['type'] = self.message_type msg['chat_state'] = state @@ -294,9 +305,41 @@ class ChatTab(Tab): if config.get('send_chat_states', 'true') == 'true' and self.remote_wants_chatstates: if not empty_before and empty_after: self.send_chat_state("active") - elif empty_before and not empty_after: + elif (empty_before or (self.timed_event_paused is not None and not self.timed_event_paused())) and not empty_after: self.send_chat_state("composing") + def set_paused_delay(self, composing): + """ + we create a timed event that will put us to paused + in a few seconds + """ + if config.get('send_chat_states', 'true') != 'true': + return + if self.timed_event_paused: + # check the weakref + event = self.timed_event_paused() + if event: + # the event already exists: we just update + # its date + event.change_date(datetime.now() + timedelta(seconds=4)) + return + new_event = timed_events.DelayedEvent(4, self.send_chat_state, 'paused') + self.core.add_timed_event(new_event) + self.timed_event_paused = weakref.ref(new_event) + + def cancel_paused_delay(self): + """ + Remove that event from the list and set it to None. + Called for example when the input is emptied, or when the message + is sent + """ + if self.timed_event_paused: + event = self.timed_event_paused() + if event: + self.core.timed_events.remove(event) + del event + self.timed_event_paused = None + def command_say(self, line): raise NotImplementedError @@ -643,6 +686,9 @@ class MucTab(ChatTab): self.input.do_command(key) empty_after = self.input.get_text() == '' or (self.input.get_text().startswith('/') and not self.input.get_text().startswith('//')) self.send_composing_chat_state(empty_before, empty_after) + if not empty_before and empty_after: + self.cancel_paused_delay() + self.set_paused_delay(empty_before and not empty_after) return False def completion(self): @@ -975,6 +1021,9 @@ class PrivateTab(ChatTab): self.input.do_command(key) empty_after = self.input.get_text() == '' or (self.input.get_text().startswith('/') and not self.input.get_text().startswith('//')) self.send_composing_chat_state(empty_before, empty_after) + if not empty_before and empty_after: + self.cancel_paused_delay() + self.set_paused_delay(empty_before and not empty_after) return False def on_lose_focus(self): @@ -1399,6 +1448,9 @@ class ConversationTab(ChatTab): self.input.do_command(key) empty_after = self.input.get_text() == '' or (self.input.get_text().startswith('/') and not self.input.get_text().startswith('//')) self.send_composing_chat_state(empty_before, empty_after) + if not empty_before and empty_after: + self.cancel_paused_delay() + self.set_paused_delay(empty_before and not empty_after) return False def on_lose_focus(self): diff --git a/src/timed_events.py b/src/timed_events.py index 7cb92cde..66d3449e 100644 --- a/src/timed_events.py +++ b/src/timed_events.py @@ -31,6 +31,7 @@ class TimedEvent(object): Note that these events can NOT be used for very small delay or a very precise date, since the check for events is done once per second, as a maximum. + The callback and its arguments should be passed as the lasts arguments. """ def __init__(self, date, callback, *args): self._callback = callback @@ -57,12 +58,24 @@ class TimedEvent(object): else: return False + def change_date(self, date): + """ + Simply change the date of the event + """ + self.next_call_date = date + + def add_delay(self, delay): + """ + Add a delay (in seconds) to the date + """ + self.next_call_date += datetime.timedelta(seconds=delay) + class DelayedEvent(TimedEvent): """ The date is calculated from now + a delay in seconds Use it if you want an event to happen in, e.g. 6 seconds """ def __init__(self, delay, callback, *args): - date = datetime.datetime.now() + datetime.timedelta(0, delay) + date = datetime.datetime.now() + datetime.timedelta(seconds=delay) TimedEvent.__init__(self, date, callback, args) -- cgit v1.2.3