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 - @@ -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)