Sending messages, and more framework

This commit is contained in:
Travis Shirk 2005-12-31 03:53:48 +00:00
parent 1eec68634c
commit 96d7dcbce2
4 changed files with 269 additions and 15 deletions

View File

@ -60,6 +60,7 @@ class Chat:
def __init__(self, account, widget_name): def __init__(self, account, widget_name):
self.xml = gtk.glade.XML(GTKGUI_GLADE, widget_name, APP) self.xml = gtk.glade.XML(GTKGUI_GLADE, widget_name, APP)
self.window = self.xml.get_widget(widget_name) self.window = self.xml.get_widget(widget_name)
print type(self.window)
self.widget_name = widget_name self.widget_name = widget_name

View File

@ -54,8 +54,8 @@ class ChatControlBase(MessageControl):
def _update_banner_state_image(self): def _update_banner_state_image(self):
pass # Derived types MAY implement this pass # Derived types MAY implement this
def __init__(self, widget_name, contact): def __init__(self, parent_win, widget_name, contact, acct):
MessageControl.__init__(self, widget_name, contact); MessageControl.__init__(self, parent_win, widget_name, contact, acct);
# FIXME: These are hidden from 0.8 on, but IMO all these things need # FIXME: These are hidden from 0.8 on, but IMO all these things need
# to be shown optionally. Esp. the never-used Send button # to be shown optionally. Esp. the never-used Send button
@ -79,6 +79,14 @@ class ChatControlBase(MessageControl):
self.msg_textview.connect('key_press_event', self.msg_textview.connect('key_press_event',
self.on_message_textview_key_press_event) self.on_message_textview_key_press_event)
# the following vars are used to keep history of user's messages
self.sent_history = []
self.sent_history_pos = -1
self.typing_new = False
self.orig_msg = ''
self.nb_unread = 0
def _paint_banner(self): def _paint_banner(self):
'''Repaint banner with theme color''' '''Repaint banner with theme color'''
theme = gajim.config.get('roster_theme') theme = gajim.config.get('roster_theme')
@ -117,13 +125,9 @@ class ChatControlBase(MessageControl):
print "ChatControl.on_message_textview_key_press_event", event print "ChatControl.on_message_textview_key_press_event", event
if event.keyval == gtk.keysyms.Page_Down: # PAGE DOWN if event.keyval == gtk.keysyms.Page_Down: # PAGE DOWN
if event.state & gtk.gdk.CONTROL_MASK: # CTRL + PAGE DOWN
self.notebook.emit('key_press_event', event)
if event.state & gtk.gdk.SHIFT_MASK: # SHIFT + PAGE DOWN if event.state & gtk.gdk.SHIFT_MASK: # SHIFT + PAGE DOWN
self.conv_textview.emit('key_press_event', event) self.conv_textview.emit('key_press_event', event)
elif event.keyval == gtk.keysyms.Page_Up: # PAGE UP elif event.keyval == gtk.keysyms.Page_Up: # PAGE UP
if event.state & gtk.gdk.CONTROL_MASK: # CTRL + PAGE UP
self.notebook.emit('key_press_event', event)
if event.state & gtk.gdk.SHIFT_MASK: # SHIFT + PAGE UP if event.state & gtk.gdk.SHIFT_MASK: # SHIFT + PAGE UP
self.conv_textview.emit('key_press_event', event) self.conv_textview.emit('key_press_event', event)
@ -132,8 +136,6 @@ class ChatControlBase(MessageControl):
'''When a key is pressed: '''When a key is pressed:
if enter is pressed without the shift key, message (if not empty) is sent if enter is pressed without the shift key, message (if not empty) is sent
and printed in the conversation''' and printed in the conversation'''
# FIXME: Need send_message
assert(False)
# NOTE: handles mykeypress which is custom signal connected to this # NOTE: handles mykeypress which is custom signal connected to this
# CB in new_tab(). for this singal see message_textview.py # CB in new_tab(). for this singal see message_textview.py
@ -185,17 +187,100 @@ class ChatControlBase(MessageControl):
_('Your message can not be sent until you are connected.')).get_response() _('Your message can not be sent until you are connected.')).get_response()
send_message = False send_message = False
# FIXME: Define send_message (base class??)
if send_message: if send_message:
self.send_message(message) # send the message self.send_message(message) # send the message
def _process_command(self, message):
if not message:
return False
if message == '/clear':
self.conv_textview.clear() # clear conversation
# FIXME: Need this function
self.clear(self.msg_textview) # clear message textview too
return True
elif message == '/compact':
# FIXME: Need this function
self.set_compact_view(not self.compact_view_current_state)
# FIXME: Need this function
self.clear(self.msg_textview)
return True
else:
return False
def send_message(self, message, keyID = '', chatstate = None):
'''Send the given message to the active tab'''
if not message or message == '\n':
return
if not self._process_command(message):
MessageControl.send_message(self, message, keyID, chatstate)
# Record message history
self.save_sent_message(message)
# Clear msg input
message_buffer = self.msg_textview.get_buffer()
message_buffer.set_text('') # clear message buffer (and tv of course)
def save_sent_message(self, message):
#save the message, so user can scroll though the list with key up/down
size = len(self.sent_history)
#we don't want size of the buffer to grow indefinately
max_size = gajim.config.get('key_up_lines')
if size >= max_size:
for i in xrange(0, size - 1):
self.sent_history[i] = self.sent_history[i + 1]
self.sent_history[max_size - 1] = message
else:
self.sent_history.append(message)
self.sent_history_pos = size + 1
self.typing_new = True
self.orig_msg = ''
def print_conversation_line(self, text, kind, name, tim,
other_tags_for_name = [], other_tags_for_time = [],
other_tags_for_text = [], count_as_new = True, subject = None):
'''prints 'chat' type messages'''
jid = self.contact.jid
textview = self.conv_textview
end = False
if textview.at_the_end() or kind == 'outgoing':
end = True
textview.print_conversation_line(text, jid, kind, name, tim,
other_tags_for_name, other_tags_for_time, other_tags_for_text, subject)
if not count_as_new:
return
if kind == 'incoming_queue':
gajim.last_message_time[self.account][jid] = time.time()
urgent = True
if (jid != self.parent_win.get_active_jid() or \
not self.parent_win.is_active() or not end) and\
kind in ('incoming', 'incoming_queue'):
self.nb_unread += 1
if gajim.interface.systray_enabled and\
gajim.config.get('trayicon_notification_on_new_messages'):
gajim.interface.systray.add_jid(jid, self.account,
self.get_message_type(jid))
self.redraw_tab(jid)
self.show_title(urgent)
class ChatControl(ChatControlBase): class ChatControl(ChatControlBase):
'''A control for standard 1-1 chat''' '''A control for standard 1-1 chat'''
def __init__(self, contact): def __init__(self, parent_win, contact, acct):
ChatControlBase.__init__(self, 'chat_child_vbox', contact); ChatControlBase.__init__(self, parent_win, 'chat_child_vbox', contact, acct);
self.compact_view = gajim.config.get('always_compact_view_chat') self.compact_view = gajim.config.get('always_compact_view_chat')
# chatstate timers and state
self._schedule_activity_timers()
self.reset_kbd_mouse_timeout_vars()
def _schedule_activity_timers(self):
self.possible_paused_timeout_id = gobject.timeout_add(5000,
self.check_for_possible_paused_chatstate, None)
self.possible_inactive_timeout_id = gobject.timeout_add(30000,
self.check_for_possible_inactive_chatstate, None)
def draw_widgets(self): def draw_widgets(self):
# The name banner is drawn here # The name banner is drawn here
ChatControlBase.draw_widgets(self) ChatControlBase.draw_widgets(self)
@ -293,5 +378,139 @@ class ChatControl(ChatControlBase):
'assigned one') % self.contact.name 'assigned one') % self.contact.name
gtk.Tooltips().set_tip(self.xml.get_widget('gpg_eventbox'), tt) gtk.Tooltips().set_tip(self.xml.get_widget('gpg_eventbox'), tt)
def send_message(self, message, keyID = '', chatstate = None):
'''Send a message to contact'''
if not message or message == '\n' or self._process_command(message):
return
contact = self.contact
jid = self.contact.jid
keyID = ''
encrypted = False
if self.xml.get_widget('gpg_togglebutton').get_active():
keyID = contact.keyID
encrypted = True
chatstates_on = gajim.config.get('chat_state_notifications') != 'disabled'
chatstate_to_send = None
if chatstates_on and contact is not None:
if contact.our_chatstate is None:
# no info about peer
# send active to discover chat state capabilities
# this is here (and not in send_chatstate)
# because we want it sent with REAL message
# (not standlone) eg. one that has body
chatstate_to_send = 'active'
contact.our_chatstate = 'ask' # pseudo state
# if peer supports jep85 and we are not 'ask', send 'active'
# NOTE: first active and 'ask' is set in gajim.py
elif contact.our_chatstate not in (False, 'ask'):
#send active chatstate on every message (as JEP says)
chatstate_to_send = 'active'
contact.our_chatstate = 'active'
gobject.source_remove(self.possible_paused_timeout_id)
gobject.source_remove(self.possible_inactive_timeout_id)
self._schedule_activity_timers()
ChatControlBase.send_message(self, message, keyID, chatstate_to_send)
self.print_conversation(message, self.contact.jid, encrypted = encrypted)
def check_for_possible_paused_chatstate(self, arg):
''' did we move mouse of that window or write something in message
textview in the last 5 seconds?
if yes we go active for mouse, composing for kbd
if no we go paused if we were previously composing '''
contact = self.contact
jid = contact.jid
current_state = contact.our_chatstate
if current_state is False: # jid doesn't support chatstates
return False # stop looping
message_buffer = self.msg_textview.get_buffer()
if self.kbd_activity_in_last_5_secs and message_buffer.get_char_count():
# Only composing if the keyboard activity was in text entry
# FIXME: Need send_chatstate
self.send_chatstate('composing')
elif self.mouse_over_in_last_5_secs and\
jid == self.parent_win.get_active_jid():
self.send_chatstate('active')
else:
if current_state == 'composing':
self.send_chatstate('paused') # pause composing
# assume no activity and let the motion-notify or 'insert-text' make them True
# refresh 30 seconds vars too or else it's 30 - 5 = 25 seconds!
self.reset_kbd_mouse_timeout_vars()
return True # loop forever
def check_for_possible_inactive_chatstate(self, arg):
''' did we move mouse over that window or wrote something in message
textview in the last 30 seconds?
if yes we go active
if no we go inactive '''
contact = self.contact
jid = contact.jid
current_state = contact.our_chatstate
if current_state is False: # jid doesn't support chatstates
return False # stop looping
if self.mouse_over_in_last_5_secs or self.kbd_activity_in_last_5_secs:
return True # loop forever
if not self.mouse_over_in_last_30_secs or self.kbd_activity_in_last_30_secs:
self.send_chatstate('inactive', jid)
# assume no activity and let the motion-notify or 'insert-text' make them True
# refresh 30 seconds too or else it's 30 - 5 = 25 seconds!
self.reset_kbd_mouse_timeout_vars()
return True # loop forever
def reset_kbd_mouse_timeout_vars(self):
self.kbd_activity_in_last_5_secs = False
self.mouse_over_in_last_5_secs = False
self.mouse_over_in_last_30_secs = False
self.kbd_activity_in_last_30_secs = False
def print_conversation(self, text, frm = '', tim = None,
encrypted = False, subject = None):
'''Print a line in the conversation:
if contact is set to status: it's a status message
if contact is set to another value: it's an outgoing message
if contact is set to print_queue: it is incomming from queue
if contact is not set: it's an incomming message'''
contact = self.contact
jid = contact.jid
if frm == 'status':
kind = 'status'
name = ''
else:
ec = gajim.encrypted_chats[self.account]
if encrypted and jid not in ec:
msg = _('Encryption enabled')
ChatControlBase.print_conversation_line(self, msg,
'status', '', tim)
ec.append(jid)
if not encrypted and jid in ec:
msg = _('Encryption disabled')
ChatControlBase.print_conversation_line(self, msg,
'status', '', tim)
ec.remove(jid)
self.xml.get_widget('gpg_togglebutton').set_active(encrypted)
if not frm:
kind = 'incoming'
name = contact.name
elif frm == 'print_queue': # incoming message, but do not update time
kind = 'incoming_queue'
name = contact.name
else:
kind = 'outgoing'
name = gajim.nicks[self.account]
ChatControlBase.print_conversation_line(self, text, kind, name, tim,
subject = subject)

View File

@ -186,6 +186,24 @@ class MessageWindow:
for ctl in self._controls.values(): for ctl in self._controls.values():
ctl.repaint_themed_widgets() ctl.repaint_themed_widgets()
def _widgetToControl(self, widget):
for ctl in self._controls.values():
if ctl.widget == widget:
return ctl
return None
def get_active_contact(self):
notebook = self.notebook
active_widget = notebook.get_nth_page(notebook.get_current_page())
return self._widgetToControl(active_widget).contact
def get_active_jid(self):
return self.get_active_contact().jid
def is_active(self):
return self.window.is_active()
class MessageWindowMgr: class MessageWindowMgr:
'''A manager and factory for MessageWindow objects''' '''A manager and factory for MessageWindow objects'''
@ -257,11 +275,13 @@ class MessageWindowMgr:
class MessageControl(gtk.VBox): class MessageControl(gtk.VBox):
'''An abstract base widget that can embed in the gtk.Notebook of a MessageWindow''' '''An abstract base widget that can embed in the gtk.Notebook of a MessageWindow'''
def __init__(self, widget_name, contact): def __init__(self, parent_win, widget_name, contact, account):
gtk.VBox.__init__(self) gtk.VBox.__init__(self)
self.parent_win = parent_win
self.widget_name = widget_name self.widget_name = widget_name
self.contact = contact self.contact = contact
self.account = account
self.compact_view = False self.compact_view = False
self.xml = gtk.glade.XML(GTKGUI_GLADE, widget_name, APP) self.xml = gtk.glade.XML(GTKGUI_GLADE, widget_name, APP)
@ -273,3 +293,17 @@ class MessageControl(gtk.VBox):
pass # NOTE: Derived classes SHOULD implement this pass # NOTE: Derived classes SHOULD implement this
def update_state(self): def update_state(self):
pass # NOTE: Derived classes SHOULD implement this pass # NOTE: Derived classes SHOULD implement this
def send_message(self, message, keyID = '', chatstate = None):
'''Send the given message to the active tab'''
if not message or message == '\n':
return
# refresh timers
self.reset_kbd_mouse_timeout_vars()
jid = self.contact.jid
# Send and update history
gajim.connections[self.account].send_message(jid, message, keyID, chatstate)

View File

@ -1653,7 +1653,7 @@ _('If "%s" accepts this request you will know his or her status.') %jid)
def new_chat(self, contact, account): def new_chat(self, contact, account):
# Get target window # Get target window
mw = self.msg_win_mgr.get_window(contact, account, None) # FIXME: type arg mw = self.msg_win_mgr.get_window(contact, account, None) # FIXME: type arg
chat_control = ChatControl(contact) chat_control = ChatControl(mw, contact, account)
mw.new_tab(chat_control) mw.new_tab(chat_control)
# REMOVE ME # REMOVE ME
@ -2225,7 +2225,7 @@ _('If "%s" accepts this request you will know his or her status.') %jid)
def repaint_themed_widgets(self): def repaint_themed_widgets(self):
'''Notify windows that contain themed widgets to repaint them''' '''Notify windows that contain themed widgets to repaint them'''
for win in self.msg_win_mgr.windows(): for win in self.msg_win_mgr.windows.values():
win.repaint_themed_widgets() win.repaint_themed_widgets()
for account in gajim.connections: for account in gajim.connections:
for addr in gajim.interface.instances[account]['disco']: for addr in gajim.interface.instances[account]['disco']: