diff --git a/src/chat_control.py b/src/chat_control.py
index 2ab0b5191..4e1c106c7 100644
--- a/src/chat_control.py
+++ b/src/chat_control.py
@@ -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
diff --git a/src/conversation_textview.py b/src/conversation_textview.py
index baa27de74..19832ded4 100644
--- a/src/conversation_textview.py
+++ b/src/conversation_textview.py
@@ -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:]
diff --git a/src/groupchat_control.py b/src/groupchat_control.py
index c1b65f44d..93ebde200 100644
--- a/src/groupchat_control.py
+++ b/src/groupchat_control.py
@@ -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):
diff --git a/src/gtkgui.glade b/src/gtkgui.glade
index 3a841cb6e..fb46bfd26 100644
--- a/src/gtkgui.glade
+++ b/src/gtkgui.glade
@@ -18028,9 +18028,6 @@ Maybe I'll refactor later
GDK_WINDOW_TYPE_HINT_NORMAL
GDK_GRAVITY_NORTH_WEST
True
-
-
-
@@ -18623,7 +18620,6 @@ Status message
True
GTK_RELIEF_NONE
True
-
@@ -18674,7 +18670,6 @@ Status message
True
True
False
-
@@ -18694,7 +18689,6 @@ topic
-1
False
0
-
@@ -18856,7 +18850,6 @@ topic
True
GTK_RELIEF_NORMAL
True
-
@@ -19114,7 +19107,6 @@ topic
True
False
False
-
@@ -19170,7 +19162,6 @@ topic
True
GTK_RELIEF_NONE
True
-
diff --git a/src/gtkgui_helpers.py b/src/gtkgui_helpers.py
index 4eb81e31f..c8b68dba0 100644
--- a/src/gtkgui_helpers.py
+++ b/src/gtkgui_helpers.py
@@ -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):
diff --git a/src/history_window.py b/src/history_window.py
index d06127e6a..86308cddc 100644
--- a/src/history_window.py
+++ b/src/history_window.py
@@ -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)
diff --git a/src/message_textview.py b/src/message_textview.py
index a30b9e258..0affa4dc9 100644
--- a/src/message_textview.py
+++ b/src/message_textview.py
@@ -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)
diff --git a/src/message_window.py b/src/message_window.py
index c61dbe012..2f9ec7e2a 100644
--- a/src/message_window.py
+++ b/src/message_window.py
@@ -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)