Migrating code into the new classes

This commit is contained in:
Travis Shirk 2006-01-02 22:08:50 +00:00
parent 8d42f5d0f0
commit f195c47ea7
7 changed files with 243 additions and 41 deletions

View File

@ -1,13 +0,0 @@
Index: src/roster_window.py
===================================================================
--- src/roster_window.py (revision 4923)
+++ src/roster_window.py (working copy)
@@ -87,6 +87,8 @@
model = self.tree.get_model()
root = self.get_account_iter(account)
group_iter = model.iter_children(root)
+ # C_NAME column contacts the pango escaped group name
+ name = gtkgui_helpers.escape_for_pango_markup(name)
while group_iter:
group_name = model[group_iter][C_NAME].decode('utf-8')
if name == group_name:

View File

@ -188,11 +188,11 @@ class ChatControlBase(MessageControl):
if event.keyval == gtk.keysyms.Up:
if event.state & gtk.gdk.CONTROL_MASK: # Ctrl+UP
self.sent_messages_scroll(jid, 'up', widget.get_buffer())
self.sent_messages_scroll('up', widget.get_buffer())
return
elif event.keyval == gtk.keysyms.Down:
if event.state & gtk.gdk.CONTROL_MASK: # Ctrl+Down
self.sent_messages_scroll(jid, 'down', widget.get_buffer())
self.sent_messages_scroll('down', widget.get_buffer())
return
elif event.keyval == gtk.keysyms.Return or \
event.keyval == gtk.keysyms.KP_Enter: # ENTER
@ -501,6 +501,30 @@ class ChatControlBase(MessageControl):
gajim.interface.systray.remove_jid(jid, self.account,
self.type_id)
def sent_messages_scroll(self, direction, conv_buf):
size = len(self.sent_history)
if self.typing_new:
#user was typing something and then went into history, so save
#whatever is already typed
start_iter = conv_buf.get_start_iter()
end_iter = conv_buf.get_end_iter()
self.orig_msg = conv_buf.get_text(start_iter, end_iter, 0).decode('utf-8')
self.typing_new = False
if direction == 'up':
if self.sent_history_pos == 0:
return
self.sent_history_pos = self.sent_history_pos - 1
conv_buf.set_text(self.sent_history[self.sent_history_pos])
elif direction == 'down':
if self.sent_history_pos >= size - 1:
conv_buf.set_text(self.orig_msg);
self.typing_new = True
self.sent_history_pos = size
return
self.sent_history_pos = self.sent_history_pos + 1
conv_buf.set_text(self.sent_history[self.sent_history_pos])
################################################################################
class ChatControl(ChatControlBase):
'''A control for standard 1-1 chat'''
@ -512,16 +536,50 @@ class ChatControl(ChatControlBase):
self.compact_view_always = gajim.config.get('always_compact_view_chat')
self.set_compact_view(self.compact_view_always)
# chatstate timers and state
self._schedule_activity_timers()
self.reset_kbd_mouse_timeout_vars()
xm = gtk.glade.XML(GTKGUI_GLADE, 'chat_control_popup_menu', APP)
xm.signal_autoconnect(self)
self.popup_menu = xm.get_widget('chat_control_popup_menu')
xm = gtk.glade.XML(GTKGUI_GLADE, 'banner_eventbox', APP)
xm.signal_autoconnect(self)
self.TARGET_TYPE_URI_LIST = 80
self.dnd_list = [ ( 'text/uri-list', 0, self.TARGET_TYPE_URI_LIST ) ]
self.widget.connect('drag_data_received', self._on_drag_data_received)
self.widget.drag_dest_set(gtk.DEST_DEFAULT_MOTION | gtk.DEST_DEFAULT_HIGHLIGHT |
gtk.DEST_DEFAULT_DROP,
self.dnd_list, gtk.gdk.ACTION_COPY)
# keep timeout id and window obj for possible big avatar
# it is on enter-notify and leave-notify so no need to be per jid
self.show_bigger_avatar_timeout_id = None
self.bigger_avatar_window = None
self.show_avatar(self.contact.resource)
# chatstate timers and state
self.reset_kbd_mouse_timeout_vars()
self._schedule_activity_timers()
self.parent_win.window.connect('motion-notify-event',
self._on_window_motion_notify)
def save_var(self, jid):
gpg_enabled = self.xmls[jid].get_widget('gpg_togglebutton').get_active()
return {'gpg_enabled': gpg_enabled}
def load_var(self, jid, var):
if not self.xmls.has_key(jid):
return
self.xmls[jid].get_widget('gpg_togglebutton').set_active(
var['gpg_enabled'])
def _on_window_motion_notify(self, widget, event):
'''it gets called no matter if it is the active window or not'''
print "_on_window_motion_notify"
if widget.get_property('has-toplevel-focus'):
# change chatstate only if window is the active one
self.mouse_over_in_last_5_secs = True
self.mouse_over_in_last_30_secs = True
def _schedule_activity_timers(self):
self.possible_paused_timeout_id = gobject.timeout_add(5000,
self.check_for_possible_paused_chatstate, None)
@ -1013,3 +1071,41 @@ class ChatControl(ChatControlBase):
self.send_chatstate('active', self.contact.jid)
else:
self.send_chatstate('inactive', self.contact.jid)
def show_avatar(self, resource = None):
jid = self.contact.jid
jid_with_resource = jid
if resource:
jid_with_resource += '/' + resource
# we assume contact has no avatar
scaled_pixbuf = None
real_jid = gajim.get_real_jid_from_fjid(self.account, jid)
pixbuf = None
if real_jid:
pixbuf = gtkgui_helpers.get_avatar_pixbuf_from_cache(real_jid)
if not real_jid or pixbuf == 'ask':
# we don't have the vcard or it's pm and we don't have the real jid
gajim.connections[self.account].request_vcard(jid_with_resource)
return
if pixbuf is not None:
scaled_pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf, 'chat')
image = self.xml.get_widget('avatar_image')
image.set_from_pixbuf(scaled_pixbuf)
image.show_all()
def _on_drag_data_received(self, widget, context, x, y, selection, target_type,
timestamp):
# If not resource, we can't send file
if not self.contact.resource:
return
if target_type == self.TARGET_TYPE_URI_LIST:
uri = selection.data.strip()
uri_splitted = uri.split() # we may have more than one file dropped
for uri in uri_splitted:
path = helpers.get_file_path_from_dnd_dropped_uri(uri)
if os.path.isfile(path): # is it file?
ft = gajim.interface.instances['file_transfers']
ft.send_file(self.account, self.contact, path)

View File

@ -650,13 +650,11 @@ class Interface:
if gajim.interface.msg_win_mgr.has_window(jid):
win = gajim.interface.msg_type.get_window(jid)
ctl = win.get_control(jid)
# FIXME: Why is this needed
elif resource and gajim.interface.msg_win_mgr.has_window(jid + '/' + resource):
win = gajim.interface.msg_type.get_window(jid + '/' + resource)
ctl = win.get_control(jid + '/' + resource)
if win:
# FIXME: Are these args needed
ctl.show_avatar(jid, resource)
ctl.show_avatar()
# Show avatar in roster
self.roster.draw_avatar(jid, account)
if self.remote_ctrl:

View File

@ -1330,7 +1330,7 @@ current room topic.') % command, room_jid)
room_jid, show='offline', status=reason)
del self.nicks[room_jid]
# They can already be removed by the destroy function
if room_jid in gajim.contacts.get_room_list(self.account):
if room_jid in gajim.contacts.get_gc_list(self.account):
gajim.contacts.remove_room(self.account, room_jid)
del gajim.gc_connected[self.account][room_jid]
del self.list_treeview[room_jid]

View File

@ -34,12 +34,10 @@ APP = i18n.APP
GTKGUI_GLADE = 'gtkgui.glade'
####################
class MessageControl(gtk.VBox):
class MessageControl:
'''An abstract base widget that can embed in the gtk.Notebook of a MessageWindow'''
def __init__(self, type_id, parent_win, widget_name, display_name, contact, account):
gtk.VBox.__init__(self)
self.type_id = type_id
self.parent_win = parent_win
self.widget_name = widget_name
@ -50,7 +48,7 @@ class MessageControl(gtk.VBox):
self.compact_view_current = False
self.nb_unread = 0
self.print_time_timeout_id = None
# FIXME: Make this a member like all the others
# FIXME: Make this a member
gajim.last_message_time[self.account][contact.jid] = 0
self.xml = gtk.glade.XML(GTKGUI_GLADE, widget_name, APP)
@ -101,6 +99,14 @@ class MessageControl(gtk.VBox):
# NOTE: Derived classes MAY implement this
return True
def save_var(self, jid):
'''When called, the derived type should serialize it's state in the form of a
name/value (i.e. dict) result.
the return value must be compatible with wthe one given to load_var'''
pass # Derived classes SHOULD implement this
def load_var(self, jid, var):
pass # Derived classes SHOULD implement this
def send_message(self, message, keyID = '', type = 'chat', chatstate = None):
'''Send the given message to the active tab'''
# refresh timers

View File

@ -42,6 +42,10 @@ class MessageWindow:
self.widget_name = 'message_window'
self.xml = gtk.glade.XML(GTKGUI_GLADE, self.widget_name, APP)
self.window = self.xml.get_widget(self.widget_name)
# FIXME: assertion that !GTK_WIDGET_REALIZED fails
# gtk+ doesn't make use of the motion notify on gtkwindow by default
# so this line adds that
#self.window.set_events(gtk.gdk.POINTER_MOTION_MASK)
self.alignment = self.xml.get_widget('alignment')
self.notebook = self.xml.get_widget('notebook')
@ -70,6 +74,8 @@ class MessageWindow:
self.notebook.set_show_border(gajim.config.get('tabs_border'))
self.notebook.connect('switch-page',
self._on_notebook_switch_page)
self.notebook.connect('key-press-event',
self._on_notebook_key_press)
# Connect event handling for this Window
self.window.connect('delete-event', self._on_window_delete)
@ -85,12 +91,8 @@ class MessageWindow:
gtkgui_helpers.resize_window(self.window,
gajim.config.get('msgwin-width'),
gajim.config.get('msgwin-height'))
self.window.show_all()
def _on_window_focus(self, widget, event):
# FIXME:
print "_on_window_focus"
# window received focus, so if we had urgency REMOVE IT
# NOTE: we do not have to read the message (it maybe in a bg tab)
# to remove urgency hint so this functions does that
@ -253,21 +255,26 @@ class MessageWindow:
self.notebook.set_current_page(ctl_page)
def remove_tab(self, contact):
# Shutdown the MessageControl
ctl = self.get_control(contact.jid)
if len(self._controls) == 1 or not ctl.allow_shutdown():
if not ctl.allow_shutdown():
return
if gajim.interface.systray_enabled:
gajim.interface.systray.remove_jid(contact.jid, ctl.account,
ctl.type)
ctl.shutdown()
self.notebook.remove_page(self.notebook.page_num(self.childs[jid]))
# Update external state
if gajim.interface.systray_enabled:
gajim.interface.systray.remove_jid(contact.jid, ctl.account,
ctl.type_id)
del gajim.last_message_time[ctl.account][ctl.contact.jid]
if len(self._controls) == 1:
self.window.destroy()
return
self.notebook.remove_page(self.notebook.page_num(ctl.widget))
del self._controls[contact.jid]
del gajim.last_message_time[self.account][jid]
if len(self.xmls) == 1: # we now have only one tab
if len(self._controls) == 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:
@ -442,6 +449,112 @@ class MessageWindow:
new_ctl = self._widget_to_control(notebook.get_nth_page(page_num))
new_ctl.set_control_active(True)
def _on_notebook_key_press(self, widget, event):
st = '1234567890' # alt+1 means the first tab (tab 0)
ctl = self.get_active_control()
jid = ctl.jid
if event.keyval == gtk.keysyms.Escape: # ESCAPE
if ctl.type == TYPE_CHAT:
self.remove_tab(jid)
elif event.keyval == gtk.keysyms.F4 and \
(event.state & gtk.gdk.CONTROL_MASK): # CTRL + F4
self.remove_tab(jid)
elif event.keyval == gtk.keysyms.w and \
(event.state & gtk.gdk.CONTROL_MASK): # CTRL + W
self.remove_tab(jid)
elif event.string and event.string in st and \
(event.state & gtk.gdk.MOD1_MASK): # alt + 1,2,3..
self.notebook.set_current_page(st.index(event.string))
elif event.keyval == gtk.keysyms.c and \
(event.state & gtk.gdk.MOD1_MASK): # alt + C toggles compact view
ctl.set_compact_view(not self.compact_view_current_state)
# FIXME: Move this to ChatControlBase
elif event.keyval == gtk.keysyms.e and \
(event.state & gtk.gdk.MOD1_MASK): # alt + E opens emoticons menu
if gajim.config.get('useemoticons'):
msg_tv = self.message_textviews[jid]
def set_emoticons_menu_position(w, msg_tv = msg_tv):
window = msg_tv.get_window(gtk.TEXT_WINDOW_WIDGET)
# get the window position
origin = window.get_origin()
size = window.get_size()
buf = msg_tv.get_buffer()
# get the cursor position
cursor = msg_tv.get_iter_location(buf.get_iter_at_mark(
buf.get_insert()))
cursor = msg_tv.buffer_to_window_coords(gtk.TEXT_WINDOW_TEXT,
cursor.x, cursor.y)
x = origin[0] + cursor[0]
y = origin[1] + size[1]
menu_width, menu_height = self.emoticons_menu.size_request()
#FIXME: get_line_count is not so good
#get the iter of cursor, then tv.get_line_yrange
# so we know in which y we are typing (not how many lines we have
# then go show just above the current cursor line for up
# or just below the current cursor line for down
#TEST with having 3 lines and writing in the 2nd
if y + menu_height > gtk.gdk.screen_height():
# move menu just above cursor
y -= menu_height + (msg_tv.allocation.height / buf.get_line_count())
#else: # move menu just below cursor
# y -= (msg_tv.allocation.height / buf.get_line_count())
return (x, y, True) # push_in True
self.emoticons_menu.popup(None, None, set_emoticons_menu_position, 1, 0)
# FIXME Move to ChatControlBase
elif event.keyval == gtk.keysyms.Page_Down:
if event.state & gtk.gdk.SHIFT_MASK: # SHIFT + PAGE DOWN
conv_textview = self.conversation_textviews[jid]
rect = conv_textview.get_visible_rect()
iter = conv_textview.get_iter_at_location(rect.x,
rect.y + rect.height)
conv_textview.scroll_to_iter(iter, 0.1, True, 0, 0)
# FIXME Move to ChatControlBase
elif event.keyval == gtk.keysyms.Page_Up:
if event.state & gtk.gdk.SHIFT_MASK: # SHIFT + PAGE UP
conv_textview = self.conversation_textviews[jid]
rect = conv_textview.get_visible_rect()
iter = conv_textview.get_iter_at_location(rect.x, rect.y)
conv_textview.scroll_to_iter(iter, 0.1, True, 0, 1)
# FIXME Move to ChatControlBase
elif event.keyval == gtk.keysyms.Up:
if event.state & gtk.gdk.SHIFT_MASK: # SHIFT + UP
conversation_scrolledwindow = self.xml.get_widget('conversation_scrolledwindow')
conversation_scrolledwindow.emit('scroll-child',
gtk.SCROLL_PAGE_BACKWARD, False)
elif event.keyval == gtk.keysyms.ISO_Left_Tab: # SHIFT + TAB
if event.state & gtk.gdk.CONTROL_MASK: # CTRL + SHIFT + TAB
self.move_to_next_unread_tab(False)
elif event.keyval == gtk.keysyms.Tab: # TAB
if event.state & gtk.gdk.CONTROL_MASK: # CTRL + TAB
self.move_to_next_unread_tab(True)
# FIXME Move to ChatControlBase
elif (event.keyval == gtk.keysyms.l or event.keyval == gtk.keysyms.L) \
and event.state & gtk.gdk.CONTROL_MASK: # CTRL + L
conv_textview = self.conversation_textviews[jid]
conv_textview.get_buffer().set_text('')
# FIXME Move to ChatControlBase
elif event.keyval == gtk.keysyms.v and event.state & gtk.gdk.CONTROL_MASK:
# CTRL + V
msg_textview = self.message_textviews[jid]
if not msg_textview.is_focus():
msg_textview.grab_focus()
msg_textview.emit('key_press_event', event)
elif event.state & gtk.gdk.CONTROL_MASK or \
(event.keyval == gtk.keysyms.Control_L) or \
(event.keyval == gtk.keysyms.Control_R):
# we pressed a control key or ctrl+sth: we don't block
# the event in order to let ctrl+c (copy text) and
# others do their default work
pass
# FIXME Move to ChatControlBase
else: # it's a normal key press make sure message_textview has focus
msg_textview = self.message_textviews[jid]
if msg_textview.get_property('sensitive'):
if not msg_textview.is_focus():
msg_textview.grab_focus()
msg_textview.emit('key_press_event', event)
################################################################################
class MessageWindowMgr:
'''A manager and factory for MessageWindow objects'''
@ -468,6 +581,7 @@ class MessageWindowMgr:
def _new_window(self):
win = MessageWindow()
win.window.show_all()
# we track the lifetime of this window
win.window.connect('destroy', self._on_window_destroy)
return win

View File

@ -250,7 +250,8 @@ class Systray:
contacts_menu = gtk.Menu()
item.set_submenu(contacts_menu)
for jid in gajim.contacts.get_jid_list(account):
contact = gajim.get_contact_with_highest_priority(account, jid)
contact = gajim.contacts.get_contact_with_highest_priority(account,
jid)
if group in contact.groups and contact.show != 'offline' and \
contact.show != 'error':
at_least_one = True