Hooking up more pieces

This commit is contained in:
Travis Shirk 2006-01-02 09:04:30 +00:00
parent 4b59506c97
commit 8d42f5d0f0
4 changed files with 225 additions and 37 deletions

View file

@ -79,18 +79,22 @@ class ChatControlBase(MessageControl):
# Create textviews and connect signals # Create textviews and connect signals
self.conv_textview = ConversationTextview(None) # FIXME: remove account arg self.conv_textview = ConversationTextview(None) # FIXME: remove account arg
self.conv_textview.show_all() self.conv_textview.show_all()
scrolledwindow = self.xml.get_widget('conversation_scrolledwindow') self.conv_scrolledwindow = self.xml.get_widget('conversation_scrolledwindow')
scrolledwindow.add(self.conv_textview) self.conv_scrolledwindow.add(self.conv_textview)
self.conv_scrolledwindow.get_vadjustment().connect('value-changed',
self.on_conversation_vadjustment_value_changed)
self.conv_textview.connect('key_press_event', self.conv_textview.connect('key_press_event',
self.on_conversation_textview_key_press_event) self.on_conversation_textview_key_press_event)
# add MessageTextView to UI and connect signals # add MessageTextView to UI and connect signals
message_scrolledwindow = self.xml.get_widget('message_scrolledwindow') self.msg_scrolledwindow = self.xml.get_widget('message_scrolledwindow')
self.msg_textview = MessageTextView() self.msg_textview = MessageTextView()
self.msg_textview.connect('mykeypress', self.msg_textview.connect('mykeypress',
self.on_message_textview_mykeypress_event) self.on_message_textview_mykeypress_event)
message_scrolledwindow.add(self.msg_textview) self.msg_scrolledwindow.add(self.msg_textview)
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)
self.msg_textview.connect('size-request', self.size_request, self.xml)
self.update_font()
# the following vars are used to keep history of user's messages # the following vars are used to keep history of user's messages
self.sent_history = [] self.sent_history = []
@ -365,7 +369,6 @@ class ChatControlBase(MessageControl):
def set_compact_view(self, state): def set_compact_view(self, state):
'''Toggle compact view. state is bool''' '''Toggle compact view. state is bool'''
MessageControl.set_compact_view(self, state) MessageControl.set_compact_view(self, state)
# make the last message visible, when changing to "full view" # make the last message visible, when changing to "full view"
if not state: if not state:
gobject.idle_add(self.conv_textview.scroll_to_end_iter) gobject.idle_add(self.conv_textview.scroll_to_end_iter)
@ -403,6 +406,101 @@ class ChatControlBase(MessageControl):
gajim.interface.instances['logs'][jid] = history_window.HistoryWindow( gajim.interface.instances['logs'][jid] = history_window.HistoryWindow(
jid, self.account) jid, self.account)
def set_control_active(self, state):
if state:
jid = self.contact.jid
if self.conv_textview.at_the_end():
#we are at the end
if self.nb_unread > 0:
self.nb_unread = 0 + self.get_specific_unread()
self.parent_win.redraw_tab(jid)
self.parent_win.show_title()
if gajim.interface.systray_enabled:
gajim.interface.systray.remove_jid(jid,
self.account,
self.type)
self.conv_textview.grab_focus()
def bring_scroll_to_end(self, textview, diff_y = 0):
''' scrolls to the end of textview if end is not visible '''
buffer = textview.get_buffer()
end_iter = buffer.get_end_iter()
end_rect = textview.get_iter_location(end_iter)
visible_rect = textview.get_visible_rect()
# scroll only if expected end is not visible
if end_rect.y >= (visible_rect.y + visible_rect.height + diff_y):
gobject.idle_add(self.scroll_to_end_iter, textview)
def scroll_to_end_iter(self, textview):
buffer = textview.get_buffer()
end_iter = buffer.get_end_iter()
textview.scroll_to_iter(end_iter, 0, False, 1, 1)
return False
def size_request(self, msg_textview , requisition, xml_top):
''' When message_textview changes its size. If the new height
will enlarge the window, enable the scrollbar automatic policy
Also enable scrollbar automatic policy for horizontal scrollbar
if message we have in message_textview is too big'''
if msg_textview.window is None:
return
min_height = self.conv_scrolledwindow.get_property('height-request')
conversation_height = self.conv_textview.window.get_size()[1]
message_height = msg_textview.window.get_size()[1]
message_width = msg_textview.window.get_size()[0]
# new tab is not exposed yet
if conversation_height < 2:
return
if conversation_height < min_height:
min_height = conversation_height
# we don't want to always resize in height the message_textview
# so we have minimum on conversation_textview's scrolled window
# but we also want to avoid window resizing so if we reach that
# minimum for conversation_textview and maximum for message_textview
# we set to automatic the scrollbar policy
diff_y = message_height - requisition.height
if diff_y != 0:
if conversation_height + diff_y < min_height:
if message_height + conversation_height - min_height > min_height:
self.msg_scrolledwindow.set_property('vscrollbar-policy',
gtk.POLICY_AUTOMATIC)
self.msg_scrolledwindow.set_property('height-request',
message_height + conversation_height - min_height)
self.bring_scroll_to_end(msg_textview)
else:
self.msg_scrolledwindow.set_property('vscrollbar-policy',
gtk.POLICY_NEVER)
self.msg_scrolledwindow.set_property('height-request', -1)
self.conv_textview.bring_scroll_to_end(diff_y - 18)
# enable scrollbar automatic policy for horizontal scrollbar
# if message we have in message_textview is too big
if requisition.width > message_width:
self.msg_scrolledwindow.set_property('hscrollbar-policy',
gtk.POLICY_AUTOMATIC)
else:
self.msg_scrolledwindow.set_property('hscrollbar-policy',
gtk.POLICY_NEVER)
return True
def on_conversation_vadjustment_value_changed(self, widget):
if not self.nb_unread:
return
jid = self.contact.jid
if self.conv_textview.at_the_end() and self.window.is_active():
#we are at the end
self.nb_unread = self.get_specific_unread()
self.parent_win.redraw_tab(jid)
self.parent_win.show_title()
if gajim.interface.systray_enabled:
gajim.interface.systray.remove_jid(jid, self.account,
self.type_id)
################################################################################ ################################################################################
class ChatControl(ChatControlBase): class ChatControl(ChatControlBase):
'''A control for standard 1-1 chat''' '''A control for standard 1-1 chat'''
@ -421,6 +519,8 @@ class ChatControl(ChatControlBase):
xm = gtk.glade.XML(GTKGUI_GLADE, 'chat_control_popup_menu', APP) xm = gtk.glade.XML(GTKGUI_GLADE, 'chat_control_popup_menu', APP)
xm.signal_autoconnect(self) xm.signal_autoconnect(self)
self.popup_menu = xm.get_widget('chat_control_popup_menu') self.popup_menu = xm.get_widget('chat_control_popup_menu')
xm = gtk.glade.XML(GTKGUI_GLADE, 'banner_eventbox', APP)
xm.signal_autoconnect(self)
def _schedule_activity_timers(self): def _schedule_activity_timers(self):
self.possible_paused_timeout_id = gobject.timeout_add(5000, self.possible_paused_timeout_id = gobject.timeout_add(5000,
@ -742,12 +842,6 @@ class ChatControl(ChatControlBase):
'''sets compact view menuitem active state '''sets compact view menuitem active state
sets active and sensitivity state for toggle_gpg_menuitem sets active and sensitivity state for toggle_gpg_menuitem
and remove possible 'Switch to' menuitems''' and remove possible 'Switch to' menuitems'''
# FIXME: GC only
# if self.widget_name == 'groupchat_window':
# menu = self.gc_popup_menu
# childs = menu.get_children()
# # compact_view_menuitem
# childs[5].set_active(self.compact_view_current)
menu = self.popup_menu menu = self.popup_menu
childs = menu.get_children() childs = menu.get_children()
# check if gpg capabitlies or else make gpg toggle insensitive # check if gpg capabitlies or else make gpg toggle insensitive
@ -790,6 +884,7 @@ class ChatControl(ChatControlBase):
else: else:
widget.set_no_show_all(False) widget.set_no_show_all(False)
widget.show_all() widget.show_all()
def on_compact_view_menuitem_activate(self, widget): def on_compact_view_menuitem_activate(self, widget):
isactive = widget.get_active() isactive = widget.get_active()
self.set_compact_view(isactive) self.set_compact_view(isactive)
@ -886,7 +981,7 @@ class ChatControl(ChatControlBase):
gajim.interface.systray.remove_jid(self.contact.jid, self.account, gajim.interface.systray.remove_jid(self.contact.jid, self.account,
self.type) self.type)
def check_delete(self): def allow_shutdown(self):
jid = self.contact.jid jid = self.contact.jid
if time.time() - gajim.last_message_time[self.account][jid] < 2: if time.time() - gajim.last_message_time[self.account][jid] < 2:
# 2 seconds # 2 seconds
@ -896,8 +991,8 @@ class ChatControl(ChatControlBase):
_('If you close this tab and you have history disabled, '\ _('If you close this tab and you have history disabled, '\
'this message will be lost.')) 'this message will be lost.'))
if dialog.get_response() != gtk.RESPONSE_OK: if dialog.get_response() != gtk.RESPONSE_OK:
return True #stop the propagation of the event return False #stop the propagation of the event
return False return True
def handle_incoming_chatstate(self): def handle_incoming_chatstate(self):
''' handle incoming chatstate that jid SENT TO us ''' ''' handle incoming chatstate that jid SENT TO us '''
@ -905,4 +1000,16 @@ class ChatControl(ChatControlBase):
# update chatstate in tab for this chat # update chatstate in tab for this chat
self.parent_win.redraw_tab(self.contact, self.contact.chatstate) self.parent_win.redraw_tab(self.contact, self.contact.chatstate)
def on_banner_eventbox_button_press_event(self, widget, event):
'''If right-clicked, show popup'''
if event.button == 3: # right click
self.parent_win.popup_menu(event)
def set_control_active(self, state):
ChatControlBase.set_control_active(self, state)
# send chatstate inactive to the one we're leaving
# and active to the one we visit
if state:
self.send_chatstate('active', self.contact.jid)
else:
self.send_chatstate('inactive', self.contact.jid)

View file

@ -44,6 +44,11 @@ class GroupchatControl(ChatControlBase):
# if the room jid is in the list, the room has mentioned us # if the room jid is in the list, the room has mentioned us
self.muc_attentions = [] self.muc_attentions = []
# connect the menuitems to their respective functions
xm = gtk.glade.XML(GTKGUI_GLADE, 'gc_popup_menu', APP)
xm.signal_autoconnect(self)
self.gc_popup_menu = xm.get_widget('gc_popup_menu')
def markup_tab_label(self, label_str, chatstate): def markup_tab_label(self, label_str, chatstate):
'''Markup the label if necessary. Returns a tuple such as: '''Markup the label if necessary. Returns a tuple such as:
(new_label_str, color) (new_label_str, color)
@ -82,3 +87,13 @@ class GroupchatControl(ChatControlBase):
label_str = '<b>' + str(num_unread) + label_str + '</b>' label_str = '<b>' + str(num_unread) + label_str + '</b>'
return (label_str, color) return (label_str, color)
def prepare_context_menu(self):
'''sets compact view menuitem active state
sets active and sensitivity state for toggle_gpg_menuitem
and remove possible 'Switch to' menuitems'''
menu = self.gc_popup_menu
childs = menu.get_children()
# compact_view_menuitem
childs[5].set_active(self.compact_view_current_state)
menu = self.remove_possible_switch_to_menuitems(menu)
return menu

View file

@ -50,12 +50,19 @@ class MessageControl(gtk.VBox):
self.compact_view_current = False self.compact_view_current = False
self.nb_unread = 0 self.nb_unread = 0
self.print_time_timeout_id = None self.print_time_timeout_id = None
# FIXME: Make this a member like all the others
gajim.last_message_time[self.account][contact.jid] = 0
self.xml = gtk.glade.XML(GTKGUI_GLADE, widget_name, APP) self.xml = gtk.glade.XML(GTKGUI_GLADE, widget_name, APP)
self.widget = self.xml.get_widget(widget_name) self.widget = self.xml.get_widget(widget_name)
# Autoconnect glade signals # Autoconnect glade signals
self.xml.signal_autoconnect(self) self.xml.signal_autoconnect(self)
def set_control_active(self, state):
'''Called when the control becomes active (state is True)
or inactive (state is False)'''
pass # Derived types MUST implement this method
def shutdown(self): def shutdown(self):
# NOTE: Derived classes MUST implement this # NOTE: Derived classes MUST implement this
assert(False) assert(False)
@ -87,12 +94,12 @@ class MessageControl(gtk.VBox):
def set_compact_view(self, state): def set_compact_view(self, state):
# NOTE: Derived classes MAY implement this # NOTE: Derived classes MAY implement this
self.compact_view_current = state self.compact_view_current = state
def check_delete(self): def allow_shutdown(self):
'''Called when a window has been asked to delete itself. If a control is '''Called to check is a control is allowed to shutdown.
not in a suitable shutdown state this method should return True to halt If a control is not in a suitable shutdown state this method
the delete''' should return False'''
# NOTE: Derived classes MAY implement this # NOTE: Derived classes MAY implement this
return False return True
def send_message(self, message, keyID = '', type = 'chat', chatstate = None): def send_message(self, message, keyID = '', type = 'chat', chatstate = None):
'''Send the given message to the active tab''' '''Send the given message to the active tab'''

View file

@ -68,6 +68,8 @@ class MessageWindow:
else: else:
self.notebook.set_show_tabs(False) self.notebook.set_show_tabs(False)
self.notebook.set_show_border(gajim.config.get('tabs_border')) self.notebook.set_show_border(gajim.config.get('tabs_border'))
self.notebook.connect('switch-page',
self._on_notebook_switch_page)
# Connect event handling for this Window # Connect event handling for this Window
self.window.connect('delete-event', self._on_window_delete) self.window.connect('delete-event', self._on_window_delete)
@ -97,17 +99,20 @@ class MessageWindow:
widget.props.urgency_hint = False widget.props.urgency_hint = False
ctl = self.get_active_control() ctl = self.get_active_control()
if ctl:
ctl.set_control_active(True)
# Undo "unread" state display, etc. # Undo "unread" state display, etc.
if ctl.type_id == message_control.TYPE_GC: if ctl.type_id == message_control.TYPE_GC:
self.redraw_tab(ctl.contact, 'active') self.redraw_tab(ctl.contact, 'active')
else: else:
# NOTE: we do not send any chatstate to preserve inactive, gone, etc. # NOTE: we do not send any chatstate to preserve
# inactive, gone, etc.
self.redraw_tab(ctl.contact) self.redraw_tab(ctl.contact)
def _on_window_delete(self, win, event): def _on_window_delete(self, win, event):
# Make sure all controls are okay with being deleted # Make sure all controls are okay with being deleted
for ctl in self._controls.values(): for ctl in self._controls.values():
if not ctl.check_delete(): if not ctl.allow_shutdown():
return True # halt the delete return True # halt the delete
# FIXME: Do based on type, main, never, peracct, pertype # FIXME: Do based on type, main, never, peracct, pertype
@ -132,7 +137,11 @@ class MessageWindow:
def new_tab(self, control): def new_tab(self, control):
assert(not self._controls.has_key(control.contact.jid)) assert(not self._controls.has_key(control.contact.jid))
self._controls[control.contact.jid] = control self._controls[control.contact.jid] = control
if len(self._controls) > 1:
self.notebook.set_show_tabs(True)
self.alignment.set_property('top-padding', 2)
# Connect to keyboard events
control.widget.connect('key_press_event', control.widget.connect('key_press_event',
self.on_conversation_textview_key_press_event) self.on_conversation_textview_key_press_event)
# FIXME: need to get this event without access to message_textvier # FIXME: need to get this event without access to message_textvier
@ -146,11 +155,22 @@ class MessageWindow:
tab_label_box = xml.get_widget('chat_tab_ebox') tab_label_box = xml.get_widget('chat_tab_ebox')
xml.signal_connect('on_close_button_clicked', self.on_close_button_clicked, xml.signal_connect('on_close_button_clicked', self.on_close_button_clicked,
control.contact) control.contact)
xml.signal_connect('on_tab_eventbox_button_press_event',
self.on_tab_eventbox_button_press_event, control.widget)
self.notebook.append_page(control.widget, tab_label_box) self.notebook.append_page(control.widget, tab_label_box)
self.redraw_tab(control.contact) self.redraw_tab(control.contact)
self.show_title() self.show_title()
self.window.show_all() self.window.show_all()
# NOTE: we do not call set_control_active(True) since we don't know whether
# the tab is the active one.
def on_tab_eventbox_button_press_event(self, widget, event, child):
if event.button == 3:
n = self.notebook.page_num(child)
self.notebook.set_current_page(n)
self.popup_menu(event)
def on_message_textview_mykeypress_event(self, widget, event_keyval, def on_message_textview_mykeypress_event(self, widget, event_keyval,
event_keymod): event_keymod):
@ -233,14 +253,27 @@ class MessageWindow:
self.notebook.set_current_page(ctl_page) self.notebook.set_current_page(ctl_page)
def remove_tab(self, contact): def remove_tab(self, contact):
print "MessageWindow.remove_tab" ctl = self.get_control(contact.jid)
if len(self._controls) == 1: if len(self._controls) == 1 or not ctl.allow_shutdown():
# There is only one tab return
# FIXME: Should we assert on contact?
self.window.destroy() if gajim.interface.systray_enabled:
else: gajim.interface.systray.remove_jid(contact.jid, ctl.account,
pass ctl.type)
# TODO ctl.shutdown()
self.notebook.remove_page(self.notebook.page_num(self.childs[jid]))
del self._controls[contact.jid]
del gajim.last_message_time[self.account][jid]
if len(self.xmls) == 1: # we now have only one tab
show_tabs_if_one_tab = gajim.config.get('tabs_always_visible')
self.notebook.set_show_tabs(show_tabs_if_one_tab)
if not show_tabs_if_one_tab:
self.alignment.set_property('top-padding', 0)
self.show_title()
def redraw_tab(self, contact, chatstate = None): def redraw_tab(self, contact, chatstate = None):
ctl = self._controls[contact.jid] ctl = self._controls[contact.jid]
@ -284,7 +317,7 @@ 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): def _widget_to_control(self, widget):
for ctl in self._controls.values(): for ctl in self._controls.values():
if ctl.widget == widget: if ctl.widget == widget:
return ctl return ctl
@ -293,7 +326,7 @@ class MessageWindow:
def get_active_control(self): def get_active_control(self):
notebook = self.notebook notebook = self.notebook
active_widget = notebook.get_nth_page(notebook.get_current_page()) active_widget = notebook.get_nth_page(notebook.get_current_page())
return self._widgetToControl(active_widget) return self._widget_to_control(active_widget)
def get_active_contact(self): def get_active_contact(self):
return self.get_active_control().contact return self.get_active_control().contact
def get_active_jid(self): def get_active_jid(self):
@ -328,7 +361,7 @@ class MessageWindow:
if page_num == None: if page_num == None:
page_num = notebook.get_current_page() page_num = notebook.get_current_page()
nth_child = notebook.get_nth_page(page_num) nth_child = notebook.get_nth_page(page_num)
return self._widgetToControl(nth_child) return self._widget_to_control(nth_child)
def controls(self): def controls(self):
for ctl in self._controls.values(): for ctl in self._controls.values():
@ -381,7 +414,33 @@ class MessageWindow:
else: # traverse for ever (eg. don't stop at first tab) else: # traverse for ever (eg. don't stop at first tab)
self.notebook.set_current_page( self.notebook.set_current_page(
self.notebook.get_n_pages() - 1) self.notebook.get_n_pages() - 1)
def popup_menu(self, event):
menu = self.get_active_control().prepare_context_menu()
# common menuitems (tab switches)
if len(self._controls) > 1: # if there is more than one tab
menu.append(gtk.SeparatorMenuItem()) # seperator
for ctl in self._controls.values():
jid = ctl.contact.jid
if jid != self.get_active_jid():
item = gtk.ImageMenuItem(_('Switch to %s') %\
self.names[jid])
img = gtk.image_new_from_stock(gtk.STOCK_JUMP_TO,
gtk.ICON_SIZE_MENU)
item.set_image(img)
item.connect('activate',
lambda obj, jid:self.set_active_tab(jid), jid)
menu.append(item)
# show the menu
menu.popup(None, None, None, event.button, event.time)
menu.show_all()
def _on_notebook_switch_page(self, notebook, page, page_num):
old_no = notebook.get_current_page()
old_ctl = self._widget_to_control(notebook.get_nth_page(old_no))
old_ctl.set_control_active(False)
new_ctl = self._widget_to_control(notebook.get_nth_page(page_num))
new_ctl.set_control_active(True)
################################################################################ ################################################################################
class MessageWindowMgr: class MessageWindowMgr: