From 893f15aeeec3897466bffe66eeb71ac651d22a23 Mon Sep 17 00:00:00 2001 From: Travis Shirk Date: Thu, 5 Jan 2006 05:51:28 +0000 Subject: [PATCH] Working on GroupchatControl --- data/iconsets/crystal/16x16/muc_active.png | Bin 0 -> 852 bytes data/iconsets/dcraven/16x16/muc_active.png | Bin 0 -> 852 bytes data/iconsets/gnome/16x16/muc_active.png | Bin 0 -> 852 bytes data/iconsets/gossip/16x16/muc_active.png | Bin 0 -> 852 bytes data/iconsets/gota/16x16/muc_active.png | Bin 0 -> 852 bytes data/iconsets/nuvola/16x16/muc_active.png | Bin 0 -> 852 bytes data/iconsets/simplebulb/16x16/muc_active.png | Bin 0 -> 852 bytes data/iconsets/stellar/16x16/muc_active.png | Bin 0 -> 852 bytes data/iconsets/sun/16x16/muc_active.png | Bin 0 -> 852 bytes src/chat_control.py | 18 +- src/common/contacts.py | 2 + src/gajim.py | 20 +- src/groupchat_control.py | 270 +++++++++++++++++- src/message_control.py | 3 - src/message_window.py | 12 +- src/roster_window.py | 14 +- 16 files changed, 293 insertions(+), 46 deletions(-) create mode 100644 data/iconsets/crystal/16x16/muc_active.png create mode 100644 data/iconsets/dcraven/16x16/muc_active.png create mode 100644 data/iconsets/gnome/16x16/muc_active.png create mode 100644 data/iconsets/gossip/16x16/muc_active.png create mode 100644 data/iconsets/gota/16x16/muc_active.png create mode 100644 data/iconsets/nuvola/16x16/muc_active.png create mode 100644 data/iconsets/simplebulb/16x16/muc_active.png create mode 100644 data/iconsets/stellar/16x16/muc_active.png create mode 100644 data/iconsets/sun/16x16/muc_active.png diff --git a/data/iconsets/crystal/16x16/muc_active.png b/data/iconsets/crystal/16x16/muc_active.png new file mode 100644 index 0000000000000000000000000000000000000000..5984ee8e80abafb95b0e598476f72bae07178e7f GIT binary patch literal 852 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 e@@s$=1nLLl22;pmHDJO30000' return (label_str, color) + def get_tab_image(self): + # Set tab image (always 16x16); unread messages show the 'message' image + img_16 = gajim.interface.roster.get_appropriate_state_images(self.room_jid) + + # nb_unread is the number directed messages (msgs that mention our nick) + tab_image = None + if self.nb_unread and gajim.config.get('show_unread_tab_icon'): + tab_image = img_16['message'] + else: + + tab_image = img_16['muc_active'] + return tab_image + def prepare_context_menu(self): '''sets compact view menuitem active state sets active and sensitivity state for toggle_gpg_menuitem @@ -118,3 +144,221 @@ class GroupchatControl(ChatControlBase): childs[5].set_active(self.compact_view_current_state) menu = self.remove_possible_switch_to_menuitems(menu) return menu + + def on_message(self, nick, msg, tim): + if not nick: + # message from server + self.print_conversation(msg, tim = tim) + else: + # message from someone + self.print_conversation(msg, nick, tim) + + def on_private_message(self, nick, msg, tim): + # Do we have a queue? + fjid = self.room_jid + '/' + nick + qs = gajim.awaiting_events[self.account] + no_queue = True + if qs.has_key(fjid): + no_queue = False + + # We print if window is opened + pm_control = gajim.interface.get_control(fjid) + if pm_control: + pm_control.print_conversation(msg, tim = tim) + return + + if no_queue: + qs[fjid] = [] + qs[fjid].append(('chat', (msg, '', 'incoming', tim, False, ''))) + + autopopup = gajim.config.get('autopopup') + autopopupaway = gajim.config.get('autopopupaway') + iter = self.get_contact_iter(nick) + path = self.list_treeview.get_model().get_path(iter) + if not autopopup or (not autopopupaway and \ + gajim.connections[self.account].connected > 2): + if no_queue: # We didn't have a queue: we change icons + model = self.list_treeview.get_model() + state_images =\ + gajim.interface.roster.get_appropriate_state_images(self.room_jid) + image = state_images['message'] + model[iter][C_IMG] = image + if gajim.interface.systray_enabled: + gajim.interface.systray.add_jid(fjid, self.account, 'pm') + self.parent_win.show_title() + else: + gc_c = gajim.contacts.get_gc_contact(self.account, self.room_jid, nick) + gajim.interface.roster.new_chat(gc_c, self.account) + # Scroll to line + self.list_treeview.expand_row(path[0:1], False) + self.list_treeview.scroll_to_cell(path) + self.list_treeview.set_cursor(path) + + def get_contact_iter(self, nick): + model = self.list_treeview.get_model() + fin = False + role_iter = model.get_iter_root() + if not role_iter: + return None + while not fin: + fin2 = False + user_iter = model.iter_children(role_iter) + if not user_iter: + fin2 = True + while not fin2: + if nick == model[user_iter][C_NICK].decode('utf-8'): + return user_iter + user_iter = model.iter_next(user_iter) + if not user_iter: + fin2 = True + role_iter = model.iter_next(role_iter) + if not role_iter: + fin = True + return None + + def print_conversation(self, text, contact = '', tim = None): + '''Print a line in the conversation: + if contact is set: it's a message from someone + if contact is not set: it's a message from the server or help''' + if isinstance(text, str): + text = unicode(text, 'utf-8') + other_tags_for_name = [] + other_tags_for_text = [] + if contact: + if contact == self.nick: # it's us + kind = 'outgoing' + else: + kind = 'incoming' + # muc-specific chatstate + self.parent_win.redraw_tab(self.contact, 'newmsg') + else: + kind = 'status' + + if kind == 'incoming': # it's a message NOT from us + # highlighting and sounds + (highlight, sound) = self.highlighting_for_message(text, tim) + if highlight: + self.redraw_tab(self.contact, 'attention') # muc-specific chatstate + other_tags_for_name.append('bold') + other_tags_for_text.append('marked') + if sound == 'received': + helpers.play_sound('muc_message_received') + elif sound == 'highlight': + helpers.play_sound('muc_message_highlight') + + self.check_and_possibly_add_focus_out_line() + + ChatControlBase.print_conversation_line(self, text, kind, contact, tim, + other_tags_for_name, [], other_tags_for_text) + + def highlighting_for_message(self, text, tim): + '''Returns a 2-Tuple. The first says whether or not to highlight the + text, the second, what sound to play.''' + highlight, sound = (None, None) + + # Do we play a sound on every muc message? + if gajim.config.get_per('soundevents', 'muc_message_received', 'enabled'): + if gajim.config.get('notify_on_all_muc_messages'): + sound = 'received' + + # Are any of the defined highlighting words in the text? + if self.needs_visual_notification(text): + highlight = True + if gajim.config.get_per('soundevents', 'muc_message_highlight', + 'enabled'): + sound = 'highlight' + + # Is it a history message? Don't want sound-floods when we join. + if tim != time.localtime(): + sound = None + + return (highlight, sound) + + def check_and_possibly_add_focus_out_line(self): + '''checks and possibly adds focus out line for room_jid if it needs it + and does not already have it as last event. If it goes to add this line + it removes previous line first''' + + win = gajim.interface.msg_win_mgr.get_window(self.room_jid) + if self.room_jid == win.get_active_jid() and\ + win.window.get_property('has-toplevel-focus'): + # it's the current room and it's the focused window. + # we have full focus (we are reading it!) + return + + if not self.allow_focus_out_line: + # if room did not receive focus-in from the last time we added + # --- line then do not readd + return + + print_focus_out_line = False + buffer = self.conv_textview.get_buffer() + + if self.focus_out_end_iter_offset is None: + # this happens only first time we focus out on this room + print_focus_out_line = True + + else: + if self.focus_out_end_iter_offset != buffer.get_end_iter().get_offset(): + # this means after last-focus something was printed + # (else end_iter's offset is the same as before) + # only then print ---- line (eg. we avoid printing many following + # ---- lines) + print_focus_out_line = True + + if print_focus_out_line and buffer.get_char_count() > 0: + buffer.begin_user_action() + + # remove previous focus out line if such focus out line exists + if self.focus_out_end_iter_offset is not None: + end_iter_for_previous_line = buffer.get_iter_at_offset( + self.focus_out_end_iter_offset) + begin_iter_for_previous_line = end_iter_for_previous_line.copy() + begin_iter_for_previous_line.backward_chars(2) # img_char+1 (the '\n') + + # remove focus out line + buffer.delete(begin_iter_for_previous_line, + end_iter_for_previous_line) + + # add the new focus out line + # FIXME: Why is this loaded from disk everytime + path_to_file = os.path.join(gajim.DATA_DIR, 'pixmaps', 'muc_separator.png') + focus_out_line_pixbuf = gtk.gdk.pixbuf_new_from_file(path_to_file) + end_iter = buffer.get_end_iter() + buffer.insert(end_iter, '\n') + buffer.insert_pixbuf(end_iter, focus_out_line_pixbuf) + + end_iter = buffer.get_end_iter() + before_img_iter = end_iter.copy() + before_img_iter.backward_char() # one char back (an image also takes one char) + buffer.apply_tag_by_name('focus-out-line', before_img_iter, end_iter) + #FIXME: remove this workaround when bug is fixed + # c http://bugzilla.gnome.org/show_bug.cgi?id=318569 + + self.allow_focus_out_line = False + + # update the iter we hold to make comparison the next time + self.focus_out_end_iter_offset = buffer.get_end_iter().get_offset() + + buffer.end_user_action() + + # scroll to the end (via idle in case the scrollbar has appeared) + gobject.idle_add(self.conv_textview.scroll_to_end) + + def needs_visual_notification(self, text): + '''checks text to see whether any of the words in (muc_highlight_words + and nick) appear.''' + + special_words = gajim.config.get('muc_highlight_words').split(';') + special_words.append(self.nick) + # Strip empties: ''.split(';') == [''] and would highlight everything. + # Also lowercase everything for case insensitive compare. + special_words = [word.lower() for word in special_words if word] + text = text.lower() + + text_splitted = text.split() + for word in text_splitted: # get each word of the text + for special_word in special_words: + if word.startswith(special_word): + return True + return False diff --git a/src/message_control.py b/src/message_control.py index 75d643964..dcd8515fc 100644 --- a/src/message_control.py +++ b/src/message_control.py @@ -119,9 +119,6 @@ class MessageControl: def send_message(self, message, keyID = '', type = 'chat', chatstate = None): '''Send the given message to the active tab''' - # refresh timers - self.reset_kbd_mouse_timeout_vars() - jid = self.contact.jid # Send and update history gajim.connections[self.account].send_message(jid, message, keyID, diff --git a/src/message_window.py b/src/message_window.py index 4641800b3..4cc6010f7 100644 --- a/src/message_window.py +++ b/src/message_window.py @@ -43,10 +43,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 +# 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.window.set_events(gtk.gdk.POINTER_MOTION_MASK) self.alignment = self.xml.get_widget('alignment') self.notebook = self.xml.get_widget('notebook') @@ -312,16 +312,16 @@ class MessageWindow: for ctl in self._controls.values(): ctl.update_tags() - def get_control(self, arg): + def get_control(self, key): '''Return the MessageControl for jid or n, where n is the notebook page index''' - if isinstance(arg, unicode): - jid = arg + if isinstance(key, unicode): + jid = key for ctl in self._controls.values(): if ctl.contact.jid == jid: return ctl return None else: - page_num = arg + page_num = key notebook = self.notebook if page_num == None: page_num = notebook.get_current_page() diff --git a/src/roster_window.py b/src/roster_window.py index df157282d..bd696172b 100644 --- a/src/roster_window.py +++ b/src/roster_window.py @@ -325,8 +325,7 @@ class RosterWindow: def join_gc_room(self, account, room_jid, nick, password): '''joins the room immediatelly''' - # FIXME - if room_jid in gajim.interface.instances[account]['gc'] and \ + if gajim.interface.msg_win_mgr.has_window(room_jid) and \ gajim.gc_connected[account][room_jid]: dialogs.ErrorDialog(_('You are already in room %s') % room_jid ).get_response() @@ -337,10 +336,11 @@ class RosterWindow: ).get_response() return room, server = room_jid.split('@') - if not room_jid in gajim.interface.instances[account]['gc']: + if not gajim.interface.msg_win_mgr.has_window(room_jid): self.new_room(room_jid, nick, account) - gajim.interface.instances[account]['gc'][room_jid].set_active_tab(room_jid) - gajim.interface.instances[account]['gc'][room_jid].window.present() + gc_win = gajim.interface.msg_win_mgr.get_window(room_jid) + gc_win.set_active_tab(room_jid) + gc_win.window.present() gajim.connections[account].join_gc(nick, room, server, password) if password: gajim.gc_passwords[room_jid] = password @@ -1705,10 +1705,8 @@ _('If "%s" accepts this request you will know his or her status.') % jid) mw.window.present() def new_room(self, room_jid, nick, account): - print "new_room" - # FIXME: Not contact. Use jid and nick # Get target window, create a control, and associate it with the window - contact = gajim.contacts.create_contact(jid = room_jid) + contact = gajim.contacts.create_contact(jid = room_jid, name = nick) mw = gajim.interface.msg_win_mgr.get_window(contact.jid) if not mw: mw = gajim.interface.msg_win_mgr.create_window(contact, account,