From 2a5b0afc44fe85b049ca4c8b59b1ecd12e2f41fc Mon Sep 17 00:00:00 2001 From: Travis Shirk <travis@pobox.com> Date: Fri, 6 Jan 2006 06:59:55 +0000 Subject: [PATCH] More groupchat goodness --- src/chat_control.py | 146 +++++++--------- src/common/gajim.py | 3 +- src/config.py | 1 + src/gajim.py | 109 ++++++------ src/groupchat_control.py | 361 ++++++++++++++++++++++++++++++++++++++- src/gtkgui.glade | 6 +- src/message_window.py | 9 +- src/roster_window.py | 26 ++- src/tooltips.py | 14 +- 9 files changed, 510 insertions(+), 165 deletions(-) diff --git a/src/chat_control.py b/src/chat_control.py index bed6d8ce16..8f79451850 100644 --- a/src/chat_control.py +++ b/src/chat_control.py @@ -281,18 +281,16 @@ class ChatControlBase(MessageControl): def _process_command(self, message): if not message: return False + if message == '/clear': self.conv_textview.clear() # clear conversation - # FIXME: Need this function self.clear(self.msg_textview) # clear message textview too return True elif message == '/compact': self.set_compact_view(not self.compact_view_current) - # FIXME: Need this function self.clear(self.msg_textview) return True - else: - return False + return False def send_message(self, message, keyID = '', type = 'chat', chatstate = None): '''Send the given message to the active tab''' @@ -308,19 +306,6 @@ class ChatControlBase(MessageControl): # Clear msg input message_buffer = self.msg_textview.get_buffer() message_buffer.set_text('') # clear message buffer (and tv of course) -# FIXME GC ONLY -# if contact is None: -# # contact was from pm in MUC -# room, nick = gajim.get_room_and_nick_from_fjid(jid) -# gc_contact = gajim.contacts.get_gc_contact(self.account, room, nick) -# if not gc_contact: -# # contact left the room, or we left the room -# dialogs.ErrorDialog(_('Sending private message failed'), -# #in second %s code replaces with nickname -# _('You are no longer in room "%s" or "%s" has left.') % \ -# (room, nick)).get_response() -# return - def save_sent_message(self, message): #save the message, so user can scroll though the list with key up/down @@ -433,13 +418,6 @@ class ChatControlBase(MessageControl): def update_tags(self): self.conv_textview.update_tags() - def set_compact_view(self, state): - '''Toggle compact view. state is bool''' - MessageControl.set_compact_view(self, state) - # make the last message visible, when changing to "full view" - if not state: - gobject.idle_add(self.conv_textview.scroll_to_end_iter) - def clear(self, tv): buffer = tv.get_buffer() start, end = buffer.get_bounds() @@ -559,10 +537,10 @@ class ChatControlBase(MessageControl): if not self.nb_unread: return jid = self.contact.jid - if self.conv_textview.at_the_end() and self.window.is_active(): + if self.conv_textview.at_the_end() and self.parent_win.window.is_active(): #we are at the end self.nb_unread = self.get_specific_unread() - self.parent_win.redraw_tab(jid) + self.parent_win.redraw_tab(self.contact) self.parent_win.show_title() if gajim.interface.systray_enabled: gajim.interface.systray.remove_jid(jid, self.account, @@ -600,6 +578,55 @@ class ChatControlBase(MessageControl): color.blue = int((color.blue * p) + (mask * (1 - p))) return color + def remove_possible_switch_to_menuitems(self, menu): + ''' remove duplicate 'Switch to' if they exist and return clean menu''' + childs = menu.get_children() + + contact = self.parent_win.get_active_contact() + jid = contact.jid + if _('not in the roster') in contact.groups: # for add_to_roster_menuitem + childs[5].show() + childs[5].set_no_show_all(False) + else: + childs[5].hide() + childs[5].set_no_show_all(True) + + if self.type_id == message_control.TYPE_GC: + start_removing_from = 7 # # this is from the seperator and after + else: + start_removing_from = 6 # this is from the seperator and after + + for child in childs[start_removing_from:]: + menu.remove(child) + return menu + + def set_compact_view(self, state): + '''Toggle compact view. state is bool''' + MessageControl.set_compact_view(self, state) + # make the last message visible, when changing to "full view" + if not state: + gobject.idle_add(self.conv_textview.scroll_to_end_iter) + + if self.type_id == message_control.TYPE_GC: + widgets = [ + self.xml.get_widget('banner_eventbox'), + self.xml.get_widget('gc_actions_hbox'), + self.xml.get_widget('list_scrolledwindow'), + ] + else: + widgets = [ + self.xml.get_widget('banner_eventbox'), + self.xml.get_widget('actions_hbox'), + ] + + for widget in widgets: + if state: + widget.set_no_show_all(True) + widget.hide() + else: + widget.set_no_show_all(False) + widget.show_all() + ################################################################################ class ChatControl(ChatControlBase): '''A control for standard 1-1 chat''' @@ -1004,28 +1031,6 @@ class ChatControl(ChatControlBase): return tab_img - def remove_possible_switch_to_menuitems(self, menu): - ''' remove duplicate 'Switch to' if they exist and return clean menu''' - childs = menu.get_children() - - contact = self.parent_win.get_active_contact() - jid = contact.jid - if _('not in the roster') in contact.groups: # for add_to_roster_menuitem - childs[5].show() - childs[5].set_no_show_all(False) - else: - childs[5].hide() - childs[5].set_no_show_all(True) - start_removing_from = 6 # this is from the seperator and after - -# FIXME: GC only -# elif : -# start_removing_from = 7 # # this is from the seperator and after - - for child in childs[start_removing_from:]: - menu.remove(child) - return menu - def prepare_context_menu(self): '''sets compact view menuitem active state sets active and sensitivity state for toggle_gpg_menuitem @@ -1051,28 +1056,6 @@ class ChatControl(ChatControlBase): return menu - def set_compact_view(self, state): - '''Toggle compact view. state is bool''' - ChatControlBase.set_compact_view(self, state) - - widgets = [ - self.xml.get_widget('banner_eventbox'), - self.xml.get_widget('actions_hbox'), - ] -# FIXME GC only -# elif self.widget_name == 'groupchat_window': -# widgets = [self.xmls[jid].get_widget('banner_eventbox'), -# self.xmls[jid].get_widget('gc_actions_hbox'), -# self.xmls[jid].get_widget('list_scrolledwindow'), -# ] - for widget in widgets: - if state: - widget.set_no_show_all(True) - widget.hide() - else: - widget.set_no_show_all(False) - widget.show_all() - def send_chatstate(self, state, contact = None): ''' sends OUR chatstate as STANDLONE chat state message (eg. no body) to contact only if new chatstate is different from the previous one @@ -1292,9 +1275,8 @@ class ChatControl(ChatControlBase): # Is it a pm ? is_pm = False room_jid, nick = gajim.get_room_and_nick_from_fjid(jid) - # FIXME: Accessing gc's, here? - gcs = gajim.interface.instances[self.account]['gc'] - if gcs.has_key(room_jid): + control = gajim.interface.msg_win_mgr.get_control(room_jid) + if control.type_id == message_control.TYPE_GC: is_pm = True events_to_keep = [] for event in l: @@ -1309,16 +1291,16 @@ class ChatControl(ChatControlBase): else: kind = 'print_queue' self.print_conversation(data[0], kind, tim = data[3], - encrypted = data[4], subject = data[1]) + encrypted = data[4], subject = data[1]) # remove from gc nb_unread if it's pm or from roster if is_pm: - gcs[room_jid].nb_unread[room_jid] -= 1 + control.nb_unread -= 1 else: gajim.interface.roster.nb_unread -= 1 if is_pm: - gcs[room_jid].show_title() + control.parent_win.show_title() else: gajim.interface.roster.show_title() # Keep only non-messages events @@ -1327,12 +1309,10 @@ class ChatControl(ChatControlBase): else: del gajim.awaiting_events[self.account][jid] typ = 'chat' # Is it a normal chat or a pm ? -# # reset to status image in gc if it is a pm -# # FIXME: New data structure -# gcs = gajim.interface.instances[self.account]['gc'] -# if gcs.has_key(room_jid): -# gcs[room_jid].draw_all_roster() -# typ = 'pm' + # reset to status image in gc if it is a pm + if is_pm: + control.draw_widgets() + typ = 'pm' gajim.interface.roster.draw_contact(jid, self.account) if gajim.interface.systray_enabled: @@ -1344,7 +1324,7 @@ class ChatControl(ChatControlBase): gajim.interface.roster.really_remove_contact(self.contact, self.account) elif typ == 'pm': - gcs[room_jid].remove_contact(room_jid, nick) + control.remove_contact(nick) def show_bigger_avatar(self, small_avatar): '''resizes the avatar, if needed, so it has at max half the screen size diff --git a/src/common/gajim.py b/src/common/gajim.py index 2ae16817f9..6a01fdaa5a 100644 --- a/src/common/gajim.py +++ b/src/common/gajim.py @@ -145,8 +145,7 @@ def get_real_jid_from_fjid(account, fjid): if not nick: # It's not a fake_jid, it is a real jid return fjid # we return the real jid real_jid = fjid - gcs = interface.instances[account]['gc'] - if gcs.has_key(room_jid): + if interface.msg_win_mgr.get_control(room_jid): # It's a pm, so if we have real jid it's in contact.jid gc_contact = contacts.get_gc_contact(account, room_jid, nick) if not gc_contact: diff --git a/src/config.py b/src/config.py index 756b8ff5e6..97704445e5 100644 --- a/src/config.py +++ b/src/config.py @@ -36,6 +36,7 @@ import gtkgui_helpers import dialogs import vcard import cell_renderer_image +import message_control try: import gtkspell diff --git a/src/gajim.py b/src/gajim.py index af10d4bfa2..adca48e09a 100755 --- a/src/gajim.py +++ b/src/gajim.py @@ -35,6 +35,8 @@ import pygtk import message_control +from chat_control import ChatControlBase + from common import exceptions from common import i18n i18n.init() @@ -176,10 +178,9 @@ class Interface: title = data[1] prompt = data[2] proposed_nick = data[3] - w = self.instances[account]['gc'] - if w.has_key(room_jid): # user may close the window before we are here - w[room_jid].show_change_nick_input_dialog(title, prompt, proposed_nick, - room_jid) + gc_control = gajim.interface.msg_win_mgr.get_control(room_jid) + if gc_control: # user may close the window before we are here + gc_control.show_change_nick_input_dialog(title, prompt, proposed_nick) def handle_event_http_auth(self, account, data): #('HTTP_AUTH', account, (method, url, transaction_id, iq_obj)) @@ -219,9 +220,9 @@ class Interface: (jid_from, file_props)) conn.disconnect_transfer(file_props) return - if jid_from in self.instances[account]['gc']: - self.instances[account]['gc'][jid_from].print_conversation( - 'Error %s: %s' % (array[2], array[1]), jid_from) + ctl = gajim.interface.msg_win_mgr.get_control(jid_from) + if ctl and ctl.type_id == message_control.TYPE_GC: + ctl.print_conversation('Error %s: %s' % (array[2], array[1])) def handle_event_con_type(self, account, con_type): # ('CON_TYPE', account, con_type) which can be 'ssl', 'tls', 'tcp' @@ -240,8 +241,9 @@ class Interface: if not gajim.gc_connected.has_key(account): return for room_jid in gajim.gc_connected[account]: - if self.instances[account]['gc'].has_key(room_jid): - self.instances[account]['gc'][room_jid].got_disconnected(room_jid) + gc_control = gajim.interface.msg_win_mgr.get_control(room_jid) + if gc_control: + gc_control.got_disconnected() else: gobject.timeout_add(30000, self.allow_notif, account) model[self.roster.status_message_menuitem_iter][3] = True # sensitivity for this menuitem @@ -476,34 +478,33 @@ class Interface: fjid = array[0] jids = fjid.split('/', 1) jid = jids[0] - gcs = self.instances[account]['gc'] - if jid in gcs: - if len(jids) > 1: # it's a pm - nick = jids[1] - if not gajim.interface.msg_win_mgr.has_window(fjid): - gc = gcs[jid] - tv = gc.list_treeview[jid] - model = tv.get_model() - i = gc.get_contact_iter(jid, nick) - if i: - show = model[i][3] - else: - show = 'offline' - gc_c = gajim.contacts.create_gc_contact(room_jid = jid, - name = nick, show = show) - c = gajim.contacts.contact_from_gc_contct(c) - self.roster.new_chat(c, account) - ctl = gajim.interface.msg_win_mgr.get_control(fjid) - ctl.print_conversation('Error %s: %s' % (array[1], array[2]), - 'status') + gcs = gajim.interface.msg_win_mgr.get_controls(message_window.TYPE_GC) + for gc_control in gcs: + if jid == gc_control.contact.jid: + if len(jids) > 1: # it's a pm + nick = jids[1] + if not gajim.interface.msg_win_mgr.get_control(fjid): + tv = gc_control.list_treeview + model = tv.get_model() + i = gc_control.get_contact_iter(nick) + if i: + show = model[i][3] + else: + show = 'offline' + gc_c = gajim.contacts.create_gc_contact(room_jid = jid, + name = nick, show = show) + c = gajim.contacts.contact_from_gc_contct(c) + self.roster.new_chat(c, account, private_chat = True) + ctl = gajim.interface.msg_win_mgr.get_control(fjid) + ctl.print_conversation('Error %s: %s' % (array[1], array[2]), + 'status') + return + + gc_control.print_conversation('Error %s: %s' % (array[1], array[2])) + if gc_control.parent_win.get_active_jid() == jid: + gc_control.set_subject(gc_control.subject) return - # FIXME - gcs[jid].print_conversation('Error %s: %s' % \ - (array[1], array[2]), jid) - if gcs[jid].get_active_jid() == jid: - gcs[jid].set_subject(jid, - gcs[jid].subjects[jid]) - return + if jid.find('@') <= 0: jid = jid.replace('@', '') self.roster.on_message(jid, _('error while sending') + \ @@ -723,12 +724,12 @@ class Interface: #('GC_SUBJECT', account, (jid, subject)) jids = array[0].split('/', 1) jid = jids[0] - if not self.instances[account]['gc'].has_key(jid): + gc_control = gajim.interface.msg_win_mgr.get_control(jid) + if not gc_control: return - self.instances[account]['gc'][jid].set_subject(jid, array[1]) + gc_control.set_subject(array[1]) if len(jids) > 1: - self.instances[account]['gc'][jid].print_conversation( - '%s has set the subject to %s' % (jids[1], array[1]), jid) + gc_control.print_conversation('%s has set the subject to %s' % (jids[1], array[1])) def handle_event_gc_config(self, account, array): #('GC_CONFIG', account, (jid, config)) config is a dict @@ -953,14 +954,12 @@ class Interface: def handle_event_signed_in(self, account, empty): '''SIGNED_IN event is emitted when we sign in, so handle it''' # join already open groupchats - for room_jid in self.instances[account]['gc']: - if room_jid == 'tabbed': - continue + for gc_control in gajim.interface.msg_win_mgr.get_controls(message_control.TYPE_GC): + room_jid = gc_control.contact.jid if gajim.gc_connected[account][room_jid]: continue - room, server = gajim.get_room_name_and_server_from_room_jid( - room_jid) - nick = self.instances[account]['gc'][room_jid].nicks[room_jid] + room, server = gajim.get_room_name_and_server_from_room_jid(room_jid) + nick = gc_control.nick password = '' if gajim.gc_passwords.has_key(room_jid): password = gajim.gc_passwords[room_jid] @@ -1242,17 +1241,16 @@ class Interface: def handle_event(self, account, jid, typ): w = None - if typ == 'gc': - if wins['gc'].has_key(jid): - w = wins['gc'][jid] - elif typ == 'chat': + if typ == message_control.TYPE_GC: + w = gajim.interface.msg_win_mgr.get_window(jid) + elif typ == message_control.TYPE_CHAT: if gajim.interface.msg_win_mgr.has_window(jid): w = gajim.interface.msg_win_mgr.get_window(jid) else: contact = gajim.contacts.get_first_contact_from_jid(account, jid) self.roster.new_chat(contact, account) w = gajim.interface.msg_win_mgr.get_window(jid) - elif typ == 'pm': + elif typ == message_control.TYPE_PM: if gajim.interface.msg_win_mgr.has_window(jid): w = gajim.interface.msg_win_mgr.get_window(jid) else: @@ -1266,7 +1264,7 @@ class Interface: gc_contact = gajim.contacts.create_gc_contact(room_jid = room_jid, name = nick, show = show) c = gajim.contacts.contact_from_gc_contct(gc_contact) - self.roster.new_chat(c, account) + self.roster.new_chat(c, account, private_chat = True) w = gajim.interface.msg_win_mgr.get_window(jid) elif typ in ('normal', 'file-request', 'file-request-error', 'file-send-error', 'file-error', 'file-stopped', 'file-completed'): @@ -1278,8 +1276,11 @@ class Interface: w.set_active_tab(jid) w.window.present() w.window.window.focus() - tv = w.conversation_textviews[jid] - tv.scroll_to_end() + ctl = w.get_control(jid) + # Using isinstance here because we want to catch all derived types + if isinstance(ctl, ChatControlBase): + tv = ctl.conv_textview + tv.scroll_to_end() def __init__(self): gajim.interface = self diff --git a/src/groupchat_control.py b/src/groupchat_control.py index 42c813e1b2..9217b34d24 100644 --- a/src/groupchat_control.py +++ b/src/groupchat_control.py @@ -63,6 +63,28 @@ class PrivateChatControl(ChatControl): self.TYPE_ID = 'pm' self.display_name = _('Private chat') + def send_message(self, message): + '''call this function to send our message''' + if not message: + return + + # We need to make sure that we can still send through the room and that the + # recipient did not go away + contact = gajim.contacts.get_first_contact_from_jid(self.account, self.contact.jid) + if contact is None: + # contact was from pm in MUC + room, nick = gajim.get_room_and_nick_from_fjid(self.contact.jid) + gc_contact = gajim.contacts.get_gc_contact(self.account, room, nick) + if not gc_contact: + dialogs.ErrorDialog( + _('Sending private message failed'), + #in second %s code replaces with nickname + _('You are no longer in room "%s" or "%s" has left.') % \ + (room, nick)).get_response() + return + + ChatControl.send_message(self, message) + class GroupchatControl(ChatControlBase): TYPE_ID = message_control.TYPE_GC @@ -255,7 +277,7 @@ class GroupchatControl(ChatControlBase): menu = self.gc_popup_menu childs = menu.get_children() # compact_view_menuitem - childs[5].set_active(self.compact_view_current_state) + childs[5].set_active(self.compact_view_current) menu = self.remove_possible_switch_to_menuitems(menu) return menu @@ -303,7 +325,8 @@ class GroupchatControl(ChatControlBase): else: gc_c = gajim.contacts.get_gc_contact(self.account, self.room_jid, nick) c = gajim.contacts.contact_from_gc_contact(gc_c) - gajim.interface.roster.new_chat(c, self.account) + print "creating PM chat" + gajim.interface.roster.new_chat(c, self.account, private_chat = True) # Scroll to line self.list_treeview.expand_row(path[0:1], False) self.list_treeview.scroll_to_cell(path) @@ -536,9 +559,32 @@ class GroupchatControl(ChatControlBase): print "draw_roster" for nick in gajim.contacts.get_nick_list(self.account, self.room_jid): gc_contact = gajim.contacts.get_gc_contact(self.account, self.room_jid, nick) - self.add_contact_to_roster(self.room_jid, nick, gc_contact.show, - gc_contact.role, gc_contact.affiliation, gc_contact.status, - gc_contact.jid) + self.add_contact_to_roster(nick, gc_contact.show, gc_contact.role, + gc_contact.affiliation, gc_contact.status, + gc_contact.jid) + + def on_send_pm(self, widget=None, model=None, iter=None, nick=None, msg=None): + '''opens a chat window and msg is not None sends private message to a + contact in a room''' + if nick is None: + nick = model[iter][C_NICK].decode('utf-8') + fjid = gajim.construct_fjid(self.room_jid, nick) # 'fake' jid + + chat_win = gajim.interface.msg_win_mgr.get_window(fjid) + if not chat_win: + gc_c = gajim.contacts.get_gc_contact(self.account, self.room_jid, nick) + c = gajim.contacts.contact_from_gc_contact(gc_c) + print "creating PM chat" + gajim.interface.roster.new_chat(c, self.account, private_chat = True) + chat_win = gajim.interface.msg_win_mgr.get_window(fjid) + chat_control = chat_win.get_control(fjid) + + #make active here in case we need to send a message + chat_win.set_active_tab(fjid) + + if msg: + chat_control.send_message(msg) + chat_win.window.present() def draw_contact(self, nick, selected=False, focus=False): iter = self.get_contact_iter(nick) @@ -708,3 +754,308 @@ class GroupchatControl(ChatControlBase): if model.iter_n_children(parent_iter) == 0: model.remove(parent_iter) + def _process_command(self, message): + if message[0] != '/': + return False + + # Handle common commands + if ChatControlBase._process_command(self, message): + return True + + message = message[1:] + message_array = message.split(' ', 1) + command = message_array.pop(0).lower() + if message_array == ['']: + message_array = [] + + if command == 'nick': + # example: /nick foo + if len(message_array): + nick = message_array[0] + gajim.connections[self.account].change_gc_nick(self.room_jid, nick) + self.clear(self.msg_textview) + else: + self.get_command_help(command) + return True + elif command == 'query' or command == 'chat': + # Open a chat window to the specified nick + # example: /query foo + if len(message_array): + nick = message_array.pop(0) + nicks = gajim.contacts.get_nick_list(self.account, self.room_jid) + if nick in nicks: + self.on_send_pm(nick = nick) + self.clear(self.msg_textview) + else: + self.print_conversation(_('Nickname not found: %s') % nick) + else: + self.get_command_help(command) + return True + elif command == 'msg': + # Send a message to a nick. Also opens a private message window. + # example: /msg foo Hey, what's up? + if len(message_array): + message_array = message_array[0].split() + nick = message_array.pop(0) + room_nicks = gajim.contacts.get_nick_list(self.account, self.room_jid) + if nick in room_nicks: + privmsg = ' '.join(message_array) + self.on_send_pm(nick=nick, msg=privmsg) + self.clear(self.msg_textview) + else: + self.print_conversation(_('Nickname not found: %s') % nick) + else: + self.get_command_help(command) + return True + elif command == 'topic': + # display or change the room topic + # example: /topic : print topic + # /topic foo : change topic to foo + if len(message_array): + new_topic = message_array.pop(0) + gajim.connections[self.account].send_gc_subject(self.room_jid, + new_topic) + else: + self.print_conversation(self.subject) + self.clear(self.msg_textview) + return True + elif command == 'invite': + # invite a user to a room for a reason + # example: /invite user@example.com reason + if len(message_array): + message_array = message_array[0].split() + invitee = message_array.pop(0) + if invitee.find('@') >= 0: + reason = ' '.join(message_array) + gajim.connections[self.account].send_invite(self.room_jid, + invitee, reason) + s = _('Invited %(contact_jid)s to %(room_jid)s.') % { + 'contact_jid': invitee, + 'room_jid': self.room_jid} + self.print_conversation(s) + self.clear(self.msg_textview) + else: + #%s is something the user wrote but it is not a jid so we inform + s = _('%s does not appear to be a valid JID') % invitee + self.print_conversation(s) + else: + self.get_command_help(command) + return True + elif command == 'join': + # example: /join room@conference.example.com/nick + if len(message_array): + message_array = message_array[0] + if message_array.find('@') >= 0: + room, servernick = message_array.split('@') + if servernick.find('/') >= 0: + server, nick = servernick.split('/', 1) + else: + server = servernick + nick = '' + #join_gc window is needed in order to provide for password entry. + if gajim.interface.instances[self.account].has_key('join_gc'): + gajim.interface.instances[self.account]['join_gc'].\ + window.present() + else: + try: + gajim.interface.instances[self.account]['join_gc'] =\ + dialogs.JoinGroupchatWindow(self.account, + server = server, room = room, nick = nick) + except RuntimeError: + pass + self.clear(self.msg_textview) + else: + #%s is something the user wrote but it is not a jid so we inform + s = _('%s does not appear to be a valid JID') % message_array + self.print_conversation(s) + else: + self.get_command_help(command) + return True + elif command == 'leave' or command == 'part' or command == 'close': + # Leave the room and close the tab or window + # FIXME: Sometimes this doesn't actually leave the room. Why? + reason = 'offline' + if len(message_array): + reason = message_array.pop(0) + self.remove_tab(reason) + return True + elif command == 'ban': + if len(message_array): + message_array = message_array[0].split() + nick = message_array.pop(0) + room_nicks = gajim.contacts.get_nick_list(self.account, self.room_jid) + reason = ' '.join(message_array) + if nick in room_nicks: + ban_jid = gajim.construct_fjid(self.room_jid, nick) + gajim.connections[self.account].gc_set_affiliation(self.room_jid, + ban_jid, 'outcast', reason) + self.clear(self.msg_textview) + elif nick.find('@') >= 0: + gajim.connections[self.account].gc_set_affiliation(self.room_jid, + nick, 'outcast', reason) + self.clear(self.msg_textview) + else: + self.print_conversation(_('Nickname not found: %s') % nick) + else: + self.get_command_help(command) + return True + elif command == 'kick': + if len(message_array): + message_array = message_array[0].split() + nick = message_array.pop(0) + room_nicks = gajim.contacts.get_nick_list(self.account, self.room_jid) + reason = ' '.join(message_array) + if nick in room_nicks: + gajim.connections[self.account].gc_set_role(self.room_jid, nick, + 'none', reason) + self.clear(self.msg_textview) + else: + self.print_conversation(_('Nickname not found: %s') % nick) + else: + self.get_command_help(command) + return True + elif command == 'help': + if len(message_array): + subcommand = message_array.pop(0) + self.get_command_help(subcommand) + else: + self.get_command_help(command) + self.clear(self.msg_textview) + return True + elif command == 'say': + if len(message_array): + gajim.connections[self.account].send_gc_message(self.room_jid, + message[4:]) + self.clear(self.msg_textview) + else: + self.get_command_help(command) + return True + else: + self.print_conversation(_('No such command: /%s (if you want to send this, ' + 'prefix it with /say)') % command) + return True + + return False + + def send_message(self, message): + '''call this function to send our message''' + if not message: + return + + if message != '' or message != '\n': + self.save_sent_message(message) + + if not self._process_command(message): + # Send the message + gajim.connections[self.account].send_gc_message(self.room_jid, message) + self.msg_textview.get_buffer().set_text('') + self.msg_textview.grab_focus() + + def get_command_help(self, command): + if command == 'help': + self.print_conversation(_('Commands: %s') % self.muc_cmds) + elif command == 'ban': + s = _('Usage: /%s <nickname|JID> [reason], bans the JID from the room.' + ' The nickname of an occupant may be substituted, but not if it contains "@".' + ' If the JID is currently in the room, he/she/it will also be kicked.' + ' Does NOT support spaces in nickname.') % command + self.print_conversation(s) + elif command == 'chat' or command == 'query': + self.print_conversation(_('Usage: /%s <nickname>, opens a private chat ' + 'window to the specified occupant.') % command) + elif command == 'clear': + self.print_conversation(_('Usage: /%s, clears the text window.') % command) + elif command == 'close' or command == 'leave' or command == 'part': + self.print_conversation(_('Usage: /%s [reason], closes the current window ' + 'or tab, displaying reason if specified.') % command) + elif command == 'compact': + self.print_conversation(_('Usage: /%s, sets the groupchat window to compact ' + 'mode.') % command) + elif command == 'invite': + self.print_conversation(_('Usage: /%s <JID> [reason], invites JID to the ' + 'current room, optionally providing a reason.') % command) + elif command == 'join': + self.print_conversation(_('Usage: /%s <room>@<server>[/nickname], offers to ' + 'join room@server optionally using specified ' + 'nickname.') % command) + elif command == 'kick': + self.print_conversation(_('Usage: /%s <nickname> [reason], removes the occupant ' + 'specified by nickname from the room and optionally ' + 'displays a reason. Does NOT support spaces in ' + 'nickname.') % command) + elif command == 'me': + self.print_conversation(_('Usage: /%s <action>, sends action to the current ' + 'room. Use third person. (e.g. /%s explodes.)') %\ + (command, command)) + elif command == 'msg': + s = _('Usage: /%s <nickname> [message], opens a private message window and ' + 'sends message to the occupant specified by nickname.') % command + self.print_conversation(s) + elif command == 'nick': + s = _('Usage: /%s <nickname>, changes your nickname in current room.') % command + self.print_conversation(s) + elif command == 'topic': + self.print_conversation(_('Usage: /%s [topic], displays or updates the current ' + 'room topic.') % command) + elif command == 'say': + self.print_conversation(_('Usage: /%s <message>, sends a message without ' + 'looking for other commands.') % command) + else: + self.print_conversation(_('No help info for /%s') % command) + + def get_role(self, nick): + gc_contact = gajim.contacts.get_gc_contact(self.account, self.room_jid, nick) + if gc_contact: + return gc_contact.role + else: + return 'visitor' + + def show_change_nick_input_dialog(self, title, prompt, proposed_nick = None): + '''asks user for new nick and on ok it sets it on room''' + instance = dialogs.InputDialog(title, prompt, proposed_nick) + response = instance.get_response() + if response == gtk.RESPONSE_OK: + nick = instance.input_entry.get_text().decode('utf-8') + self.nick = nick + gajim.connections[self.account].change_gc_nick(self.room_jid, nick) + + def shutdown(self): + gajim.connections[self.account].send_gc_status(self.nick, self.room_jid, + show='offline', status=reason) + # They can already be removed by the destroy function + if self.room_jid in gajim.contacts.get_gc_list(self.account): + gajim.contacts.remove_room(self.account, self.room_jid) + del gajim.gc_connected[self.account][self.room_jid] + + def allow_shutdown(self): + # FIXME: + # whether to ask for comfirmation before closing muc + if gajim.config.get('confirm_close_muc'): + names = [] + if not room_jid: + for r_jid in self.xmls: + if gajim.gc_connected[self.account][r_jid]: + names.append(gajim.get_nick_from_jid(r_jid)) + else: + names = [room_jid] + + rooms_no = len(names) + if rooms_no >= 2: # if we are in many rooms + pritext = _('Are you sure you want to leave rooms "%s"?') % ', '.join(names) + sectext = _('If you close this window, you will be disconnected from these rooms.') + + elif rooms_no == 1: # just in one room + pritext = _('Are you sure you want to leave room "%s"?') % names[0] + sectext = _('If you close this window, you will be disconnected from this room.') + + if rooms_no > 0: + dialog = dialogs.ConfirmationDialogCheck(pritext, sectext, + _('Do _not ask me again')) + + if dialog.is_checked(): + gajim.config.set('confirm_close_muc', False) + dialog.destroy() + + if dialog.get_response() != gtk.RESPONSE_OK: + return False + return True diff --git a/src/gtkgui.glade b/src/gtkgui.glade index 3c68130a42..0db888e164 100644 --- a/src/gtkgui.glade +++ b/src/gtkgui.glade @@ -19054,7 +19054,7 @@ Maybe I'll refactor later</property> <property name="show_tabs">True</property> <property name="show_border">True</property> <property name="tab_pos">GTK_POS_TOP</property> - <property name="scrollable">False</property> + <property name="scrollable">True</property> <property name="enable_popup">False</property> <child> @@ -19875,7 +19875,7 @@ topic</property> </child> <child> - <widget class="GtkScrolledWindow" id="scrolledwindow48"> + <widget class="GtkScrolledWindow" id="list_scrolledwindow"> <property name="width_request">100</property> <property name="visible">True</property> <property name="hscrollbar_policy">GTK_POLICY_NEVER</property> @@ -20108,7 +20108,7 @@ topic</property> <property name="right_padding">0</property> <child> - <widget class="GtkHBox" id="hbox3015"> + <widget class="GtkHBox" id="gc_actions_hbox"> <property name="visible">True</property> <property name="homogeneous">False</property> <property name="spacing">2</property> diff --git a/src/message_window.py b/src/message_window.py index 565b443ded..6cca9b3b65 100644 --- a/src/message_window.py +++ b/src/message_window.py @@ -558,12 +558,19 @@ class MessageWindowMgr: return win def get_control(self, jid): - '''Amonst all windows, return the MessageControl for jid''' + '''Amongst all windows, return the MessageControl for jid''' win = self.get_window(jid) if win: return win.get_control(jid) return None + def get_controls(self, type): + ctls = [] + for c in self.controls(): + if c.type_id == type: + ctls.append(c) + return ctls + def windows(self): for w in self._windows.values(): yield w diff --git a/src/roster_window.py b/src/roster_window.py index 318a6bebcd..2f65bf045a 100644 --- a/src/roster_window.py +++ b/src/roster_window.py @@ -1529,11 +1529,9 @@ _('If "%s" accepts this request you will know his or her status.') % jid) passphrase) gajim.connections[account].gpg_passphrase(passphrase) - for room_jid in gajim.interface.instances[account]['gc']: - if room_jid != 'tabbed': - nick = gajim.interface.instances[account]['gc'][room_jid].nicks[room_jid] - gajim.connections[account].send_gc_status(nick, room_jid, status, - txt) + for gc_control in gajim.interface.msg_win_mgr.get_controls(message_control.TYPE_GC): + gajim.connections[account].send_gc_status(gc_control.nick, gc_control.room_jid, + status, txt) gajim.connections[account].change_status(status, txt, sync, auto) if status == 'online' and gajim.interface.sleeper.getState() != \ common.sleepy.STATE_UNKNOWN: @@ -1672,14 +1670,24 @@ _('If "%s" accepts this request you will know his or her status.') % jid) self.update_status_combobox() self.make_menu() - def new_chat(self, contact, account): + def new_chat(self, contact, account, private_chat = False): # Get target window, create a control, and associate it with the window + if not private_chat: + type = message_control.TYPE_CHAT + else: + type = message_control.TYPE_PM + mw = gajim.interface.msg_win_mgr.get_window(contact.jid) if not mw: - mw = gajim.interface.msg_win_mgr.create_window(contact, account, - ChatControl.TYPE_ID) - chat_control = ChatControl(mw, contact, account) + mw = gajim.interface.msg_win_mgr.create_window(contact, account, type) + + if not private_chat: + chat_control = ChatControl(mw, contact, account) + else: + chat_control = PrivateChatControl(mw, contact, account) + mw.new_tab(chat_control) + if gajim.awaiting_events[account].has_key(contact.jid): # We call this here to avoid race conditions with widget validation chat_control.read_queue() diff --git a/src/tooltips.py b/src/tooltips.py index 068c2ddcb9..7a6577f22d 100644 --- a/src/tooltips.py +++ b/src/tooltips.py @@ -250,15 +250,13 @@ class NotificationAreaTooltip(BaseTooltip, StatusTable): else: unread_pm += ctl.nb_unread - # FIXME # we count unread gc/pm messages - gc_wins = gajim.interface.instances[acct]['gc'] - for jid in gc_wins: - if jid != 'tabbed': - pm_msgs = gc_wins[jid].get_specific_unread(jid) - unread_gc += gc_wins[jid].nb_unread[jid] - unread_gc -= pm_msgs - unread_pm += pm_msgs + chat_t = message_control.TYPE_GC + for gc_control in gajim.interface.msg_win_mgr.get_controls(chat_t): + pm_msgs = gc_control.get_specific_unread() + unread_gc += gc_control.nb_unread + unread_gc -= pm_msgs + unread_pm += pm_msgs if unread_chat or unread_single_chat or unread_gc or unread_pm: text = '' -- GitLab