break more circular references that keep

objects alive (the other 1/2 of #1829)
context menu for groupchat banner
This commit is contained in:
Dimitur Kirov 2006-04-18 15:36:16 +00:00
parent 365d9d4eb6
commit 5c9613db65
8 changed files with 166 additions and 114 deletions

View File

@ -108,13 +108,18 @@ class ChatControlBase(MessageControl):
id = self.widget.connect('key_press_event', self._on_keypress_event)
self.handlers[id] = self.widget
widget = self.xml.get_widget('banner_eventbox')
id = widget.connect('button-press-event',
self._on_banner_eventbox_button_press_event)
self.handlers[id] = widget
# Create textviews and connect signals
self.conv_textview = ConversationTextview(self.account)
self.conv_textview.show_all()
self.conv_scrolledwindow = self.xml.get_widget(
'conversation_scrolledwindow')
self.conv_scrolledwindow.add(self.conv_textview)
self.conv_scrolledwindow.add(self.conv_textview.tv)
widget = self.conv_scrolledwindow.get_vadjustment()
id = widget.connect('value-changed',
self.on_conversation_vadjustment_value_changed)
@ -170,6 +175,12 @@ class ChatControlBase(MessageControl):
gajim.config.set('use_speller', False)
self.style_event_id = 0
self.conv_textview.tv.show()
# moved from ChatControl
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 _on_send_button_clicked(self, widget):
'''When send button is pressed: send the current message'''
@ -241,7 +252,7 @@ class ChatControlBase(MessageControl):
if event.state & gtk.gdk.CONTROL_MASK:
# CTRL + l|L: clear conv_textview
if event.keyval == gtk.keysyms.l or event.keyval == gtk.keysyms.L:
self.conv_textview.get_buffer().set_text('')
self.conv_textview.tv.get_buffer().set_text('')
return True
# CTRL + v: Paste into msg_textview
elif event.keyval == gtk.keysyms.v:
@ -313,7 +324,7 @@ class ChatControlBase(MessageControl):
# SHIFT + PAGE_[UP|DOWN]: send to conv_textview
elif event.keyval == gtk.keysyms.Page_Down or \
event.keyval == gtk.keysyms.Page_Up:
self.conv_textview.emit('key_press_event', event)
self.conv_textview.tv.emit('key_press_event', event)
return True
elif event.state & gtk.gdk.CONTROL_MASK:
if event.keyval == gtk.keysyms.Tab: # CTRL + TAB
@ -327,7 +338,7 @@ class ChatControlBase(MessageControl):
# 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
self.conv_textview.emit('key_press_event', event)
self.conv_textview.tv.emit('key_press_event', event)
return False
def _on_message_textview_mykeypress_event(self, widget, event_keyval,
@ -394,7 +405,7 @@ class ChatControlBase(MessageControl):
return False
if message == '/clear':
self.conv_textview.clear() # clear conversation
self.conv_textview.tv.clear() # clear conversation
self.clear(self.msg_textview) # clear message textview too
return True
elif message == '/compact':
@ -501,7 +512,7 @@ class ChatControlBase(MessageControl):
def update_font(self):
font = pango.FontDescription(gajim.config.get('conversation_font'))
self.conv_textview.modify_font(font)
self.conv_textview.tv.modify_font(font)
self.msg_textview.modify_font(font)
def update_tags(self):
@ -516,7 +527,7 @@ class ChatControlBase(MessageControl):
'''When history menuitem is pressed: call history window'''
if not jid:
jid = self.contact.jid
if gajim.interface.instances['logs'].has_key(jid):
gajim.interface.instances['logs'][jid].window.present()
else:
@ -569,7 +580,7 @@ class ChatControlBase(MessageControl):
return
min_height = self.conv_scrolledwindow.get_property('height-request')
conversation_height = self.conv_textview.window.get_size()[1]
conversation_height = self.conv_textview.tv.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
@ -731,11 +742,6 @@ class ChatControl(ChatControlBase):
id = message_tv_buffer.connect('changed', self._on_message_tv_buffer_changed)
self.handlers[id] = message_tv_buffer
widget = self.xml.get_widget('banner_eventbox')
id = widget.connect('button-press-event',
self._on_banner_eventbox_button_press_event)
self.handlers[id] = widget
widget = self.xml.get_widget('avatar_eventbox')
id = widget.connect('enter-notify-event', self.on_avatar_eventbox_enter_notify_event)
self.handlers[id] = widget
@ -1198,19 +1204,25 @@ class ChatControl(ChatControlBase):
# connect signals
history_menuitem.connect('activate',
id = history_menuitem.connect('activate',
self._on_history_menuitem_activate)
send_file_menuitem.connect('activate',
self.handlers[id] = history_menuitem
id = send_file_menuitem.connect('activate',
self._on_send_file_menuitem_activate)
compact_view_menuitem.connect('activate',
self.handlers[id] = send_file_menuitem
id = compact_view_menuitem.connect('activate',
self._on_compact_view_menuitem_activate)
add_to_roster_menuitem.connect('activate',
self.handlers[id] = compact_view_menuitem
id = add_to_roster_menuitem.connect('activate',
self._on_add_to_roster_menuitem_activate)
toggle_gpg_menuitem.connect('activate',
self.handlers[id] = add_to_roster_menuitem
id = toggle_gpg_menuitem.connect('activate',
self._on_toggle_gpg_menuitem_activate)
information_menuitem.connect('activate',
self.handlers[id] = toggle_gpg_menuitem
id = information_menuitem.connect('activate',
self._on_contact_information_menuitem_activate)
menu.connect('selection-done', gtkgui_helpers.destroy_widget)
self.handlers[id] = information_menuitem
menu.connect('selection-done', lambda w:w.destroy())
return menu
def send_chatstate(self, state, contact = None):
@ -1311,8 +1323,11 @@ class ChatControl(ChatControlBase):
# remove all register handlers on wigets, created by self.xml
# to prevent circular references among objects
for i in self.handlers.keys():
self.handlers[i].disconnect(i)
if self.handlers[i].handler_is_connected(i):
self.handlers[i].disconnect(i)
del self.handlers[i]
self.conv_textview.del_handlers()
self.msg_textview.destroy()
def allow_shutdown(self):
@ -1334,11 +1349,6 @@ class ChatControl(ChatControlBase):
# update chatstate in tab for this chat
self.parent_win.redraw_tab(self, 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

View File

@ -44,33 +44,38 @@ gtk.glade.textdomain(APP)
GTKGUI_GLADE = 'gtkgui.glade'
class ConversationTextview(gtk.TextView):
class ConversationTextview:
'''Class for the conversation textview (where user reads already said messages)
for chat/groupchat windows'''
def __init__(self, account):
gtk.TextView.__init__(self)
# no need to inherit TextView, use it as property is safer
self.tv = gtk.TextView()
# set properties
self.set_border_width(1)
self.set_accepts_tab(True)
self.set_editable(False)
self.set_cursor_visible(False)
self.set_wrap_mode(gtk.WRAP_WORD)
self.set_left_margin(2)
self.set_right_margin(2)
self.tv.set_border_width(1)
self.tv.set_accepts_tab(True)
self.tv.set_editable(False)
self.tv.set_cursor_visible(False)
self.tv.set_wrap_mode(gtk.WRAP_WORD)
self.tv.set_left_margin(2)
self.tv.set_right_margin(2)
self.handlers = {}
# connect signals
self.connect('motion_notify_event', self.on_textview_motion_notify_event)
self.connect('populate_popup', self.on_textview_populate_popup)
self.connect('button_press_event', self.on_textview_button_press_event)
id = self.tv.connect('motion_notify_event', self.on_textview_motion_notify_event)
self.handlers[id] = self.tv
id = self.tv.connect('populate_popup', self.on_textview_populate_popup)
self.handlers[id] = self.tv
id = self.tv.connect('button_press_event', self.on_textview_button_press_event)
self.handlers[id] = self.tv
self.account = account
self.change_cursor = None
self.last_time_printout = 0
font = pango.FontDescription(gajim.config.get('conversation_font'))
self.modify_font(font)
buffer = self.get_buffer()
self.tv.modify_font(font)
buffer = self.tv.get_buffer()
end_iter = buffer.get_end_iter()
buffer.create_mark('end', end_iter, False)
@ -105,12 +110,14 @@ class ConversationTextview(gtk.TextView):
color = gajim.config.get('urlmsgcolor')
tag.set_property('foreground', color)
tag.set_property('underline', pango.UNDERLINE_SINGLE)
tag.connect('event', self.hyperlink_handler, 'url')
id = tag.connect('event', self.hyperlink_handler, 'url')
self.handlers[id] = tag
tag = buffer.create_tag('mail')
tag.set_property('foreground', color)
tag.set_property('underline', pango.UNDERLINE_SINGLE)
tag.connect('event', self.hyperlink_handler, 'mail')
id = tag.connect('event', self.hyperlink_handler, 'mail')
self.handlers[id] = tag
tag = buffer.create_tag('bold')
tag.set_property('weight', pango.WEIGHT_BOLD)
@ -125,6 +132,14 @@ class ConversationTextview(gtk.TextView):
self.line_tooltip = tooltips.BaseTooltip()
def del_handlers(self):
for i in self.handlers.keys():
self.handlers[i].disconnect(i)
del self.handlers
self.tv.destroy()
#TODO
# self.line_tooltip.destroy()
def update_tags(self):
self.tagIn.set_property('foreground', gajim.config.get('inmsgcolor'))
self.tagOut.set_property('foreground', gajim.config.get('outmsgcolor'))
@ -132,44 +147,44 @@ class ConversationTextview(gtk.TextView):
gajim.config.get('statusmsgcolor'))
def at_the_end(self):
buffer = self.get_buffer()
buffer = self.tv.get_buffer()
end_iter = buffer.get_end_iter()
end_rect = self.get_iter_location(end_iter)
visible_rect = self.get_visible_rect()
end_rect = self.tv.get_iter_location(end_iter)
visible_rect = self.tv.get_visible_rect()
if end_rect.y <= (visible_rect.y + visible_rect.height):
return True
return False
def scroll_to_end(self):
parent = self.get_parent()
buffer = self.get_buffer()
self.scroll_to_mark(buffer.get_mark('end'), 0, True, 0, 1)
parent = self.tv.get_parent()
buffer = self.tv.get_buffer()
self.tv.scroll_to_mark(buffer.get_mark('end'), 0, True, 0, 1)
adjustment = parent.get_hadjustment()
adjustment.set_value(0)
return False # when called in an idle_add, just do it once
def bring_scroll_to_end(self, diff_y = 0):
''' scrolls to the end of textview if end is not visible '''
buffer = self.get_buffer()
buffer = self.tv.get_buffer()
end_iter = buffer.get_end_iter()
end_rect = self.get_iter_location(end_iter)
visible_rect = self.get_visible_rect()
end_rect = self.tv.get_iter_location(end_iter)
visible_rect = self.tv.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)
def scroll_to_end_iter(self):
buffer = self.get_buffer()
buffer = self.tv.get_buffer()
end_iter = buffer.get_end_iter()
self.scroll_to_iter(end_iter, 0, False, 1, 1)
self.tv.scroll_to_iter(end_iter, 0, False, 1, 1)
return False # when called in an idle_add, just do it once
def show_line_tooltip(self):
pointer = self.get_pointer()
x, y = self.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT, pointer[0],
pointer = self.tv.get_pointer()
x, y = self.tv.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT, pointer[0],
pointer[1])
tags = self.get_iter_at_location(x, y).get_tags()
tag_table = self.get_buffer().get_tag_table()
tags = self.tv.get_iter_at_location(x, y).get_tags()
tag_table = self.tv.get_buffer().get_tag_table()
over_line = False
for tag in tags:
if tag == tag_table.lookup('focus-out-line'):
@ -177,26 +192,26 @@ class ConversationTextview(gtk.TextView):
break
if over_line and not self.line_tooltip.win:
# check if the current pointer is still over the line
position = self.window.get_origin()
win = self.get_toplevel()
position = self.tv.window.get_origin()
win = self.tv.get_toplevel()
self.line_tooltip.show_tooltip(_('Text below this line is what has '
'been said since the last time you paid attention to this group chat'), 8, position[1] + pointer[1])
def on_textview_motion_notify_event(self, widget, event):
'''change the cursor to a hand when we are over a mail or an url'''
pointer_x, pointer_y, spam = self.window.get_pointer()
x, y = self.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT, pointer_x,
pointer_x, pointer_y, spam = self.tv.window.get_pointer()
x, y = self.tv.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT, pointer_x,
pointer_y)
tags = self.get_iter_at_location(x, y).get_tags()
tags = self.tv.get_iter_at_location(x, y).get_tags()
if self.change_cursor:
self.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(
self.tv.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(
gtk.gdk.Cursor(gtk.gdk.XTERM))
self.change_cursor = None
tag_table = self.get_buffer().get_tag_table()
tag_table = self.tv.get_buffer().get_tag_table()
over_line = False
for tag in tags:
if tag in (tag_table.lookup('url'), tag_table.lookup('mail')):
self.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(
self.tv.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(
gtk.gdk.Cursor(gtk.gdk.HAND2))
self.change_cursor = tag
elif tag == tag_table.lookup('focus-out-line'):
@ -209,13 +224,13 @@ class ConversationTextview(gtk.TextView):
if over_line and not self.line_tooltip.win:
self.line_tooltip.timeout = gobject.timeout_add(500,
self.show_line_tooltip)
self.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(
self.tv.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(
gtk.gdk.Cursor(gtk.gdk.LEFT_PTR))
self.change_cursor = tag
def clear(self, tv = None):
'''clear text in the textview'''
buffer = self.get_buffer()
buffer = self.tv.get_buffer()
start, end = buffer.get_bounds()
buffer.delete(start, end)
@ -231,7 +246,8 @@ class ConversationTextview(gtk.TextView):
menu.prepend(item)
item = gtk.ImageMenuItem(gtk.STOCK_CLEAR)
menu.prepend(item)
item.connect('activate', self.clear)
id = item.connect('activate', self.clear)
self.handlers[id] = item
if self.selected_phrase:
s = self.selected_phrase
if len(s) > 25:
@ -249,7 +265,8 @@ class ConversationTextview(gtk.TextView):
link = 'http://%s.wikipedia.org/wiki/Special:Search?search=%s'\
% (gajim.LANG, self.selected_phrase)
item = gtk.MenuItem(_('Read _Wikipedia Article'))
item.connect('activate', self.visit_url_from_menuitem, link)
id = item.connect('activate', self.visit_url_from_menuitem, link)
self.handlers[id] = item
submenu.append(item)
item = gtk.MenuItem(_('Look it up in _Dictionary'))
@ -263,7 +280,8 @@ class ConversationTextview(gtk.TextView):
else:
link = 'http://%s.wiktionary.org/wiki/Special:Search?search=%s'\
% (gajim.LANG, self.selected_phrase)
item.connect('activate', self.visit_url_from_menuitem, link)
id = item.connect('activate', self.visit_url_from_menuitem, link)
self.handlers[id] = item
else:
if dict_link.find('%s') == -1:
#we must have %s in the url if not WIKTIONARY
@ -271,7 +289,8 @@ class ConversationTextview(gtk.TextView):
item.set_property('sensitive', False)
else:
link = dict_link % self.selected_phrase
item.connect('activate', self.visit_url_from_menuitem, link)
id = item.connect('activate', self.visit_url_from_menuitem, link)
self.handlers[id] = item
submenu.append(item)
@ -283,7 +302,8 @@ class ConversationTextview(gtk.TextView):
else:
item = gtk.MenuItem(_('Web _Search for it'))
link = search_link % self.selected_phrase
item.connect('activate', self.visit_url_from_menuitem, link)
id = item.connect('activate', self.visit_url_from_menuitem, link)
self.handlers[id] = item
submenu.append(item)
menu.show_all()
@ -297,10 +317,10 @@ class ConversationTextview(gtk.TextView):
if event.button != 3: # if not right click
return False
win = self.get_window(gtk.TEXT_WINDOW_TEXT)
x, y = self.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT,
win = self.tv.get_window(gtk.TEXT_WINDOW_TEXT)
x, y = self.tv.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT,
int(event.x), int(event.y))
iter = self.get_iter_at_location(x, y)
iter = self.tv.get_iter_at_location(x, y)
tags = iter.get_tags()
@ -313,7 +333,7 @@ class ConversationTextview(gtk.TextView):
# we check if sth was selected and if it was we assign
# selected_phrase variable
# so on_conversation_textview_populate_popup can use it
buffer = self.get_buffer()
buffer = self.tv.get_buffer()
return_val = buffer.get_selection_bounds()
if return_val: # if sth was selected when we right-clicked
# get the selected text
@ -352,8 +372,10 @@ class ConversationTextview(gtk.TextView):
menu = xml.get_widget('chat_context_menu')
childs = menu.get_children()
if kind == 'url':
childs[0].connect('activate', self.on_copy_link_activate, text)
childs[1].connect('activate', self.on_open_link_activate, kind, text)
id = childs[0].connect('activate', self.on_copy_link_activate, text)
self.handlers[id] = childs[0]
id = childs[1].connect('activate', self.on_open_link_activate, kind, text)
self.handlers[id] = childs[1]
childs[2].hide() # copy mail address
childs[3].hide() # open mail composer
childs[4].hide() # jid section separator
@ -361,11 +383,15 @@ class ConversationTextview(gtk.TextView):
childs[6].hide() # join group chat
childs[7].hide() # add to roster
else: # It's a mail or a JID
childs[2].connect('activate', self.on_copy_link_activate, text)
childs[3].connect('activate', self.on_open_link_activate, kind, text)
childs[5].connect('activate', self.on_start_chat_activate, text)
childs[6].connect('activate',
id = childs[2].connect('activate', self.on_copy_link_activate, text)
self.handlers[id] = childs[2]
id = childs[3].connect('activate', self.on_open_link_activate, kind, text)
self.handlers[id] = childs[3]
id = childs[5].connect('activate', self.on_start_chat_activate, text)
self.handlers[id] = childs[5]
id = childs[6].connect('activate',
self.on_join_group_chat_menuitem_activate, text)
self.handlers[id] = childs[6]
allow_add = False
c = gajim.contacts.get_first_contact_from_jid(self.account, text)
@ -376,7 +402,8 @@ class ConversationTextview(gtk.TextView):
allow_add = True
if allow_add:
childs[7].connect('activate', self.on_add_to_roster_activate, text)
id = childs[7].connect('activate', self.on_add_to_roster_activate, text)
self.handlers[id] = childs[7]
childs[7].show() # show add to roster menuitem
else:
childs[7].hide() # hide add to roster menuitem
@ -396,7 +423,7 @@ class ConversationTextview(gtk.TextView):
# we get the end of the tag
while not end_iter.ends_tag(texttag):
end_iter.forward_char()
word = self.get_buffer().get_text(begin_iter, end_iter).decode('utf-8')
word = self.tv.get_buffer().get_text(begin_iter, end_iter).decode('utf-8')
if event.button == 3: # right click
self.make_link_menu(event, kind, word)
else:
@ -411,7 +438,7 @@ class ConversationTextview(gtk.TextView):
after *last* special text, so we can print it in
print_conversation_line()'''
buffer = self.get_buffer()
buffer = self.tv.get_buffer()
start = 0
end = 0
@ -445,7 +472,7 @@ class ConversationTextview(gtk.TextView):
use_other_tags = True
show_ascii_formatting_chars = \
gajim.config.get('show_ascii_formatting_chars')
buffer = self.get_buffer()
buffer = self.tv.get_buffer()
possible_emot_ascii_caps = special_text.upper() # emoticons keys are CAPS
if gajim.config.get('emoticons_theme') and \
@ -458,7 +485,7 @@ class ConversationTextview(gtk.TextView):
img.set_from_file(gajim.interface.emoticons[emot_ascii])
img.show()
#add with possible animation
self.add_child_at_anchor(img, anchor)
self.tv.add_child_at_anchor(img, anchor)
elif special_text.startswith('http://') or \
special_text.startswith('www.') or \
special_text.startswith('ftp://') or \
@ -533,7 +560,7 @@ class ConversationTextview(gtk.TextView):
buffer.insert_with_tags_by_name(end_iter, special_text, *all_tags)
def print_empty_line(self):
buffer = self.get_buffer()
buffer = self.tv.get_buffer()
end_iter = buffer.get_end_iter()
buffer.insert(end_iter, '\n')
@ -546,7 +573,7 @@ class ConversationTextview(gtk.TextView):
# kind = info, we print things as if it was a status: same color, ...
if kind == 'info':
kind = 'status'
buffer = self.get_buffer()
buffer = self.tv.get_buffer()
buffer.begin_user_action()
end_iter = buffer.get_end_iter()
at_the_end = False
@ -620,7 +647,7 @@ class ConversationTextview(gtk.TextView):
def print_name(self, name, kind, other_tags_for_name):
if name:
buffer = self.get_buffer()
buffer = self.tv.get_buffer()
end_iter = buffer.get_end_iter()
name_tags = other_tags_for_name[:] # create a new list
name_tags.append(kind)
@ -632,14 +659,14 @@ class ConversationTextview(gtk.TextView):
def print_subject(self, subject):
if subject: # if we have subject, show it too!
subject = _('Subject: %s\n') % subject
buffer = self.get_buffer()
buffer = self.tv.get_buffer()
end_iter = buffer.get_end_iter()
buffer.insert(end_iter, subject)
self.print_empty_line()
def print_real_text(self, text, text_tags = [], name = None):
'''this adds normal and special text. call this to add text'''
buffer = self.get_buffer()
buffer = self.tv.get_buffer()
# /me is replaced by name if name is given
if name and (text.startswith('/me ') or text.startswith('/me\n')):
text = '* ' + name + text[3:]

View File

@ -299,7 +299,7 @@ class GroupchatControl(ChatControlBase):
self.got_disconnected() #init some variables
self.update_ui()
self.conv_textview.grab_focus()
self.conv_textview.tv.grab_focus()
self.widget.show_all()
def notify_on_new_messages(self):
@ -568,7 +568,7 @@ class GroupchatControl(ChatControlBase):
return
print_focus_out_line = False
buffer = self.conv_textview.get_buffer()
buffer = self.conv_textview.tv.get_buffer()
if self.focus_out_end_iter_offset is None:
# this happens only first time we focus out on this room
@ -1179,7 +1179,8 @@ class GroupchatControl(ChatControlBase):
# remove all register handlers on wigets, created by self.xml
# to prevent circular references among objects
for i in self.handlers.keys():
self.handlers[i].disconnect(i)
if self.handlers[i].handler_is_connected(i):
self.handlers[i].disconnect(i)
del self.handlers[i]
def allow_shutdown(self):

View File

@ -18028,9 +18028,6 @@ Maybe I'll refactor later</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
<property name="focus_on_map">True</property>
<signal name="delete_event" handler="_on_window_delete" last_modification_time="Thu, 12 Jan 2006 02:55:53 GMT"/>
<signal name="destroy" handler="_on_window_destroy" last_modification_time="Thu, 12 Jan 2006 02:56:25 GMT"/>
<signal name="focus_in_event" handler="_on_window_focus" last_modification_time="Thu, 12 Jan 2006 02:56:55 GMT"/>
<child>
<widget class="GtkAlignment" id="msg_window_alignment">
@ -18623,7 +18620,6 @@ Status message</property>
<property name="can_focus">True</property>
<property name="relief">GTK_RELIEF_NONE</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_close_button_clicked" last_modification_time="Sat, 12 Mar 2005 00:12:43 GMT"/>
<child>
<widget class="GtkImage" id="image1329">
@ -18674,7 +18670,6 @@ Status message</property>
<property name="visible">True</property>
<property name="visible_window">True</property>
<property name="above_child">False</property>
<signal name="button_press_event" handler="on_banner_eventbox_button_press_event" last_modification_time="Mon, 08 Aug 2005 15:32:39 GMT"/>
<child>
<widget class="GtkLabel" id="banner_name_label">
@ -18694,7 +18689,6 @@ topic</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
<signal name="button_press_event" handler="on_banner_label_button_press_event" last_modification_time="Sun, 07 Aug 2005 15:07:13 GMT"/>
</widget>
</child>
</widget>
@ -18856,7 +18850,6 @@ topic</property>
<property name="can_focus">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_emoticons_button_clicked" last_modification_time="Thu, 17 Nov 2005 18:06:37 GMT"/>
<child>
<widget class="GtkHBox" id="hbox3014">
@ -19114,7 +19107,6 @@ topic</property>
<property name="visible">True</property>
<property name="visible_window">False</property>
<property name="above_child">False</property>
<signal name="button_press_event" handler="on_tab_eventbox_button_press_event" last_modification_time="Thu, 04 Aug 2005 09:38:11 GMT"/>
<child>
<widget class="GtkHBox" id="hbox3017">
@ -19170,7 +19162,6 @@ topic</property>
<property name="can_focus">True</property>
<property name="relief">GTK_RELIEF_NONE</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_close_button_clicked" last_modification_time="Sat, 12 Mar 2005 00:12:43 GMT"/>
<child>
<widget class="GtkImage" id="image1347">

View File

@ -47,12 +47,13 @@ screen_h = gtk.gdk.screen_height()
def popup_emoticons_under_button(menu, button, parent_win):
''' pops emoticons menu under button, which is in parent_win'''
window_x1, window_y1 = parent_win.get_origin()
def position_menu_under_button(menu):
# inline function, which will not keep refs, when used as CB
button_x, button_y = button.allocation.x, button.allocation.y
# now convert them to X11-relative
window_x, window_y = parent_win.get_origin()
window_x, window_y = window_x1, window_y1
x = window_x + button_x
y = window_y + button_y
@ -70,7 +71,7 @@ def popup_emoticons_under_button(menu, button, parent_win):
# push_in is True so all the menuitems are always inside screen
push_in = True
return (x, y, push_in)
menu.popup(None, None, position_menu_under_button, 1, 0)
def get_theme_font_for_option(theme, option):

View File

@ -69,8 +69,8 @@ class HistoryWindow:
self.calendar = xml.get_widget('calendar')
scrolledwindow = xml.get_widget('scrolledwindow')
self.history_textview = conversation_textview.ConversationTextview(account)
scrolledwindow.add(self.history_textview)
self.history_buffer = self.history_textview.get_buffer()
scrolledwindow.add(self.history_textview.tv)
self.history_buffer = self.history_textview.tv.get_buffer()
self.history_buffer.create_tag('highlight', background = 'yellow')
self.query_entry = xml.get_widget('query_entry')
self.search_button = xml.get_widget('search_button')
@ -142,6 +142,7 @@ class HistoryWindow:
# if user destroys the window, and we have a generator filling mark days
# stop him!
gobject.source_remove(self.mark_days_idle_call_id)
self.history_textview.del_handlers()
del gajim.interface.instances['logs'][self.jid]
def on_close_button_clicked(self, widget):
@ -391,4 +392,4 @@ class HistoryWindow:
match_start_mark = self.history_buffer.create_mark('match_start',
match_start_iter, True)
self.history_textview.scroll_to_mark(match_start_mark, 0, True)
self.history_textview.tv.scroll_to_mark(match_start_mark, 0, True)

View File

@ -50,6 +50,9 @@ class MessageTextView(gtk.TextView):
self.set_pixels_above_lines(2)
self.set_pixels_below_lines(2)
def destroy(self):
import gc
gobject.idle_add(lambda:gc.collect())
if gobject.pygtk_version < (2, 8, 0):
gobject.type_register(MessageTextView)

View File

@ -55,11 +55,19 @@ class MessageWindow:
self.account = acct
# If None, the window is not tied to any specific type
self.type = type
# dict { handler id: widget}. Keeps callbacks, which
# lead to cylcular references
self.handlers = {}
self.widget_name = 'message_window'
self.xml = gtk.glade.XML(GTKGUI_GLADE, self.widget_name, APP)
self.xml.signal_autoconnect(self)
self.window = self.xml.get_widget(self.widget_name)
id = self.window.connect('delete-event', self._on_window_delete)
self.handlers[id] = self.window
id = self.window.connect('destroy', self._on_window_destroy)
self.handlers[id] = self.window
id = self.window.connect('focus-in-event', self._on_window_focus)
self.handlers[id] = self.window
# gtk+ doesn't make use of the motion notify on gtkwindow by default
# so this line adds that
@ -67,10 +75,12 @@ class MessageWindow:
self.alignment = self.xml.get_widget('alignment')
self.notebook = self.xml.get_widget('notebook')
self.notebook.connect('switch-page',
id = self.notebook.connect('switch-page',
self._on_notebook_switch_page)
self.notebook.connect('key-press-event',
self.handlers[id] = self.notebook
id = self.notebook.connect('key-press-event',
self._on_notebook_key_press)
self.handlers[id] = self.notebook
# Remove the glade pages
while self.notebook.get_n_pages():
@ -96,6 +106,8 @@ class MessageWindow:
# set up DnD
self.hid = self.notebook.connect('drag_data_received',
self.on_tab_label_drag_data_received_cb)
self.handlers[self.hid] = self.notebook
self.notebook.drag_dest_set(gtk.DEST_DEFAULT_ALL, self.DND_TARGETS,
gtk.gdk.ACTION_MOVE)
@ -133,6 +145,11 @@ class MessageWindow:
for ctrl in self.controls():
ctrl.shutdown()
self._controls.clear()
for i in self.handlers.keys():
if self.handlers[i].handler_is_connected(i):
self.handlers[i].disconnect(i)
del self.handlers[i]
del self.handlers
def new_tab(self, control):
if not self._controls.has_key(control.account):
@ -499,6 +516,7 @@ class MessageWindow:
tab_label = self.notebook.get_tab_label(child)
tab_label.dnd_handler = tab_label.connect('drag_data_get',
self.on_tab_label_drag_data_get_cb)
self.handlers[tab_label.dnd_handler] = tab_label
tab_label.drag_source_set(gtk.gdk.BUTTON1_MASK, self.DND_TARGETS,
gtk.gdk.ACTION_MOVE)
tab_label.page_num = self.notebook.page_num(child)