diff --git a/src/chat_control.py b/src/chat_control.py index 5a899cba4..92abc273b 100644 --- a/src/chat_control.py +++ b/src/chat_control.py @@ -56,8 +56,9 @@ class ChatControlBase(MessageControl): def _update_banner_state_image(self): pass # Derived types MAY implement this - def __init__(self, parent_win, widget_name, contact, acct): - MessageControl.__init__(self, parent_win, widget_name, contact, acct); + def __init__(self, parent_win, widget_name, display_name, contact, acct): + MessageControl.__init__(self, parent_win, widget_name, display_name, + contact, acct); # FIXME: These are hidden from 0.8 on, but IMO all these things need # to be shown optionally. Esp. the never-used Send button @@ -324,11 +325,20 @@ class ChatControlBase(MessageControl): self.button_clicked = widget self.emoticons_menu.popup(None, None, self.position_menu_under_button, 1, 0) + def update_font(self): + font = pango.FontDescription(gajim.config.get('conversation_font')) + self.conv_textview.modify_font(font) + self.msg_textview.modify_font(font) + + def update_tags(self): + self.conv_textview.update_tags() + class ChatControl(ChatControlBase): '''A control for standard 1-1 chat''' def __init__(self, parent_win, contact, acct): - ChatControlBase.__init__(self, parent_win, 'chat_child_vbox', contact, acct); + ChatControlBase.__init__(self, parent_win, 'chat_child_vbox', _('Chat'), + contact, acct); self.compact_view = gajim.config.get('always_compact_view_chat') # chatstate timers and state @@ -573,3 +583,54 @@ class ChatControl(ChatControlBase): ChatControlBase.print_conversation_line(self, text, kind, name, tim, subject = subject) + def markup_tab_label(self, label_str, chatstate): + '''Markup the label if necessary. Returns a tuple such as: + (new_label_str, color) + either of which can be None + if chatstate is given that means we have HE SENT US a chatstate''' + + unread = '' + num_unread = self.nb_unread + if num_unread == 1 and not gajim.config.get('show_unread_tab_icon'): + unread = '*' + elif num_unread > 1: + unread = '[' + unicode(num_unread) + ']' + + # Draw tab label using chatstate + theme = gajim.config.get('roster_theme') + color = None + if chatstate is not None: + if chatstate == 'composing': + color = gajim.config.get_per('themes', theme, + 'state_composing_color') + elif chatstate == 'inactive': + color = gajim.config.get_per('themes', theme, + 'state_inactive_color') + elif chatstate == 'gone': + color = gajim.config.get_per('themes', theme, + 'state_gone_color') + elif chatstate == 'paused': + color = gajim.config.get_per('themes', theme, + 'state_paused_color') + else: + color = gajim.config.get_per('themes', theme, + 'state_active_color') + if color: + color = gtk.gdk.colormap_get_system().alloc_color(color) + # We set the color for when it's the current tab or not + nickname.modify_fg(gtk.STATE_NORMAL, color) + # FIXME + #if chatstate in ('inactive', 'gone'): + # In inactive tab color to be lighter against the darker inactive + # background + if self.parent_win.get_active_control() != self: + p = 0.4 + mask = 0 + color.red = int((color.red * p) + (mask * (1 - p))) + color.green = int((color.green * p) + (mask * (1 - p))) + color.blue = int((color.blue * p) + (mask * (1 - p))) + nickname.modify_fg(gtk.STATE_ACTIVE, color) + + if num_unread: # if unread, text in the label becomes bold + label_str = '' + unread + label_str + '' + return (label_str, color) diff --git a/src/common/config.py b/src/common/config.py index dd894819a..5721a3f66 100644 --- a/src/common/config.py +++ b/src/common/config.py @@ -94,17 +94,13 @@ class Config: 'custombrowser': [ opt_str, 'firefox' ], 'custommailapp': [ opt_str, 'mozilla-thunderbird -compose' ], 'custom_file_manager': [ opt_str, 'xffm' ], - 'gc-x-position': [opt_int, 0], - 'gc-y-position': [opt_int, 0], - 'gc-width': [opt_int, 675], - 'gc-height': [opt_int, 400], 'gc-hpaned-position': [opt_int, 540], 'gc_refer_to_nick_char': [opt_str, ','], 'gc_proposed_nick_char': [opt_str, '_'], - 'chat-x-position': [opt_int, 0], - 'chat-y-position': [opt_int, 0], - 'chat-width': [opt_int, 480], - 'chat-height': [opt_int, 440], + 'msgwin-x-position': [opt_int, 0], + 'msgwin-y-position': [opt_int, 0], + 'msgwin-width': [opt_int, 480], + 'msgwin-height': [opt_int, 440], 'single_msg-x-position': [opt_int, 0], 'single_msg-y-position': [opt_int, 0], 'single_msg-width': [opt_int, 400], diff --git a/src/config.py b/src/config.py index 54736144a..ff68be0d9 100644 --- a/src/config.py +++ b/src/config.py @@ -683,25 +683,8 @@ class PreferencesWindow: def update_text_tags(self): '''Update color tags in Opened Chat Windows''' - for a in gajim.connections: - for kind in ('chats', 'gc'): - windows = gajim.interface.instances[a][kind] - if windows.has_key('tabbed'): - windows['tabbed'].update_tags() - else: - for jid in windows.keys(): - windows[jid].update_tags() - - def update_text_font(self): - '''Update text font in Opened Chat Windows''' - for a in gajim.connections: - for kind in ('chats', 'gc'): - windows = gajim.interface.instances[a][kind] - if windows.has_key('tabbed'): - windows['tabbed'].update_font() - else: - for jid in windows.keys(): - windows[jid].update_font() + for win in gajim.interface.msg_win_mgr.windows.values(): + win.update_tags() def on_preference_widget_color_set(self, widget, text): color = widget.get_color() @@ -716,6 +699,11 @@ class PreferencesWindow: self.update_text_font() gajim.interface.save_config() + def update_text_font(self): + '''Update text font in Opened Chat Windows''' + for win in gajim.interface.msg_win_mgr.windows.values(): + win.update_font() + def on_incoming_msg_colorbutton_color_set(self, widget): self.on_preference_widget_color_set(widget, 'inmsgcolor') diff --git a/src/gajim.py b/src/gajim.py index 5565fe114..21be9bede 100755 --- a/src/gajim.py +++ b/src/gajim.py @@ -38,8 +38,6 @@ from common import i18n i18n.init() _ = i18n._ -from message_window import MessageWindowMgr - try: import gtk except RuntimeError, msg: @@ -1308,6 +1306,8 @@ class Interface: def __init__(self): gajim.interface = self + # This is the manager and factory of message windows set by the module + self.msg_win_mgr = None self.default_values = { 'inmsgcolor': gajim.config.get('inmsgcolor'), 'outmsgcolor': gajim.config.get('outmsgcolor'), @@ -1452,9 +1452,6 @@ class Interface: gobject.timeout_add(200, self.process_connections) gobject.timeout_add(500, self.read_sleepy) - # This is the manager and factory of message windows - self.msg_win_mgr = MessageWindowMgr() - def test_migration(migration): if not migration.PROCESSING: dialog = gtk.Dialog() diff --git a/src/gtkgui_helpers.py b/src/gtkgui_helpers.py index e2c3b7625..94c38c1aa 100644 --- a/src/gtkgui_helpers.py +++ b/src/gtkgui_helpers.py @@ -210,12 +210,15 @@ def move_window(window, x, y): def resize_window(window, w, h): '''resizes window but also checks if huge window or negative values''' + if not w or not h: + return if w > screen_w: w = screen_w if h > screen_h: h = screen_h window.resize(abs(w), abs(h)) +# FIXME: Remove or update?? def one_window_opened(typ): for account in gajim.connections: if not gajim.interface.instances[account].has_key(typ): diff --git a/src/message_window.py b/src/message_window.py index 7f4572b4e..418229924 100644 --- a/src/message_window.py +++ b/src/message_window.py @@ -18,6 +18,8 @@ import pango import gobject import common +import gtkgui_helpers + from common import gajim #################### @@ -69,11 +71,31 @@ class MessageWindow: # Connect event handling for this Window self.window.connect('delete-event', self._on_window_delete) self.window.connect('destroy', self._on_window_destroy) + + # Restore previous window position + if gajim.config.get('saveposition'): + # get window position and size from config + gtkgui_helpers.move_window(self.window, + gajim.config.get('msgwin-x-position'), + gajim.config.get('msgwin-y-position')) + gtkgui_helpers.resize_window(self.window, + gajim.config.get('msgwin-width'), + gajim.config.get('msgwin-height')) self.window.show_all() def _on_window_delete(self, win, event): print "MessageWindow._on_window_delete:", win, event + if gajim.config.get('saveposition'): + # save the window size and position + x, y = win.get_position() + gajim.config.set('msgwin-x-position', x) + gajim.config.set('msgwin-y-position', y) + width, height = win.get_size() + gajim.config.set('msgwin-width', width) + gajim.config.set('msgwin-height', height) + + return False def _on_window_destroy(self, win): print "MessageWindow._on_window_destroy:", win @@ -97,6 +119,7 @@ class MessageWindow: self.notebook.append_page(control.widget, tab_label_box) self.redraw_tab(control.contact) + self.show_title() self.window.show_all() def on_message_textview_mykeypress_event(self, widget, event_keyval, @@ -143,6 +166,36 @@ class MessageWindow: def on_close_button_clicked(self, button, contact): '''When close button is pressed: close a tab''' self.remove_tab(contact) + + def show_title(self, urgent = True): + '''redraw the window's title''' + unread = 0 + for ctl in self._controls.values(): + unread += ctl.nb_unread + start = '' + if unread > 1: + start = '[' + unicode(unread) + '] ' + elif unread == 1: + start = '* ' + + ctl = self.get_active_control() + if len(self._controls) > 1: # if more than one tab in the same window + add = ctl.display_name + elif len(self._controls) == 1: # just one tab + add = ctl.contact.name +# FIXME: This is for GC only +# elif self.widget_name == 'groupchat_window': +# name = gajim.get_nick_from_jid(jid) +# add = name + + title = start + add + if len(gajim.connections) >= 2: # if we have 2 or more accounts + title += ' (' + _('account: ') + ctl.account + ')' + + # Update UI + self.window.set_title(title) + if urgent: + gtkgui_helpers.set_unset_urgency_hint(self.window, unread) def remove_tab(self, contact): # TODO @@ -163,12 +216,15 @@ class MessageWindow: else: close_button.hide() - # FIXME: Handle nb_unread - num_unread = 0 - # Update nick - nick_label.set_markup(contact.name) + nickname.set_max_width_chars(10) + (tab_label_str, tab_label_color) = ctl.markup_tab_label(contact.name) + nick_label.set_markup(tab_label_str) + if tab_label_color: + nick_label.modify_fg(gtk.STATE_NORMAL, tab_label_color) + nick_label.modify_fg(gtk.STATE_ACTIVE, tab_label_color) + num_unread = ctl.nb_unread # Set tab image (always 16x16); unread messages show the 'message' image img_16 = gajim.interface.roster.get_appropriate_state_images(contact.jid) if num_unread and gajim.config.get('show_unread_tab_icon'): @@ -192,11 +248,12 @@ class MessageWindow: return ctl return None - def get_active_contact(self): + def get_active_control(self): notebook = self.notebook active_widget = notebook.get_nth_page(notebook.get_current_page()) - return self._widgetToControl(active_widget).contact - + return self._widgetToControl(active_widget) + def get_active_contact(self): + return self.get_active_control().contact def get_active_jid(self): return self.get_active_contact().jid @@ -208,6 +265,12 @@ class MessageWindow: def toggle_emoticons(self): for ctl in self._controls.values(): ctl.toggle_emoticons() + def update_font(self): + for ctl in self._controls.values(): + ctl.update_font() + def update_tags(self): + for ctl in self._controls.values(): + ctl.update_tags() class MessageWindowMgr: '''A manager and factory for MessageWindow objects''' @@ -248,8 +311,6 @@ class MessageWindowMgr: def _on_window_delete(self, win, event): # FIXME print "MessageWindowMgr._on_window_delete:", win - msg_win = self._gtkWinToMsgWin(win) - def _on_window_destroy(self, win): # FIXME print "MessageWindowMgr._on_window_destroy:", win @@ -280,14 +341,16 @@ class MessageWindowMgr: class MessageControl(gtk.VBox): '''An abstract base widget that can embed in the gtk.Notebook of a MessageWindow''' - def __init__(self, parent_win, widget_name, contact, account): + def __init__(self, parent_win, widget_name, display_name, contact, account): gtk.VBox.__init__(self) self.parent_win = parent_win self.widget_name = widget_name + self.display_name = display_name self.contact = contact self.account = account self.compact_view = False + self.nb_unread = 0 self.xml = gtk.glade.XML(GTKGUI_GLADE, widget_name, APP) self.widget = self.xml.get_widget(widget_name) @@ -302,6 +365,12 @@ class MessageControl(gtk.VBox): pass # NOTE: Derived classes SHOULD implement this def toggle_emoticons(self): pass # NOTE: Derived classes MAY implement this + def update_font(self): + pass # NOTE: Derived classes SHOULD implement this + def update_tags(self): + pass # NOTE: Derived classes SHOULD implement this + def markup_tab_label(self, label_str): + return label_str def send_message(self, message, keyID = '', chatstate = None): '''Send the given message to the active tab''' diff --git a/src/roster_window.py b/src/roster_window.py index 8495906b9..77f2099fd 100644 --- a/src/roster_window.py +++ b/src/roster_window.py @@ -33,8 +33,6 @@ import time import common.sleepy import tabbed_chat_window import groupchat_window -import message_window -import chat_control import history_window import dialogs import vcard @@ -48,6 +46,7 @@ from gajim import Contact from common import gajim from common import helpers from common import i18n +from message_window import MessageWindowMgr from chat_control import ChatControl _ = i18n._ @@ -1655,7 +1654,8 @@ _('If "%s" accepts this request you will know his or her status.') %jid) chat_control = ChatControl(mw, contact, account) mw.new_tab(chat_control) - # REMOVE ME + # REMOVE + ################################## chats = gajim.interface.instances[account]['chats'] if gajim.config.get('usetabbedchat'): if not chats.has_key('tabbed'): @@ -1668,6 +1668,7 @@ _('If "%s" accepts this request you will know his or her status.') %jid) else: chats[contact.jid] = tabbed_chat_window.TabbedChatWindow(contact, account) + ####################### def new_chat_from_jid(self, account, jid): if gajim.contacts[account].has_key(jid): @@ -2558,6 +2559,7 @@ _('If "%s" accepts this request you will know his or her status.') %jid) def __init__(self): self.xml = gtk.glade.XML(GTKGUI_GLADE, 'roster_window', APP) self.window = self.xml.get_widget('roster_window') + gajim.interface.msg_win_mgr = MessageWindowMgr() if gajim.config.get('roster_window_skip_taskbar'): self.window.set_property('skip-taskbar-hint', True) self.tree = self.xml.get_widget('roster_treeview')