Merge one_window branch

Merged revisions 9143,9145-9155,9157-9162,9164-9169,9171-9177 via svnmerge from
svn://88.191.11.156/gajim/branches/one_window

........
  r9145 | nicfit | 2007-12-13 21:49:09 -0700 (Thu, 13 Dec 2007) | 2 lines

  Implemented the original Nikos patch with an HPaned instead of a HBox and only do this mode when one_message_window == 'always'
........
  r9152 | nicfit | 2007-12-15 13:33:56 -0700 (Sat, 15 Dec 2007) | 2 lines

  Added config and GUI for one_message_window_with_roster
........
  r9153 | nicfit | 2007-12-15 13:41:46 -0700 (Sat, 15 Dec 2007) | 2 lines

  Use one_message_window_with_roster and some whitespace cleanup
........
  r9154 | nicfit | 2007-12-15 14:04:49 -0700 (Sat, 15 Dec 2007) | 2 lines

  Scratch the chckbox for with roster mode, use one_message_window opt and combo
........
  r9155 | nicfit | 2007-12-15 17:01:13 -0700 (Sat, 15 Dec 2007) | 2 lines

  MessageWindowMgr knows about ONE_MESSAGE_WINDOW_ALWAYS_WITH_ROSTER and MessageWindow can reparent itself rather then the roster having to do so.
........
  r9157 | nicfit | 2007-12-15 17:47:20 -0700 (Sat, 15 Dec 2007) | 2 lines

  Resizing fixes and make the roster window shrink when last tab is removed
........
  r9158 | nicfit | 2007-12-15 19:15:11 -0700 (Sat, 15 Dec 2007) | 2 lines

  Added "Show roster" (CTRL+R) to view menu when using always_with_roster to quickly hide/show the roster.
........
  r9159 | nicfit | 2007-12-15 19:49:30 -0700 (Sat, 15 Dec 2007) | 2 lines

  Handle window title setting in always_with_roster mode.
........
  r9160 | nicfit | 2007-12-15 20:13:57 -0700 (Sat, 15 Dec 2007) | 2 lines

  Removed FIXME
........
  r9167 | nicfit | 2007-12-17 18:40:59 -0700 (Mon, 17 Dec 2007) | 2 lines

  When roster is hidden, show it when the number of MessageWindow controls == 0
........
  r9168 | nicfit | 2007-12-17 19:07:49 -0700 (Mon, 17 Dec 2007) | 2 lines

  Disable hiding roster when there are no message controls open
........
  r9169 | nicfit | 2007-12-17 20:41:11 -0700 (Mon, 17 Dec 2007) | 2 lines

  Bunch of saved size bugs fixed
........
This commit is contained in:
Travis Shirk 2007-12-18 23:42:22 +00:00
parent e7d711e753
commit 0afc7b2328
8 changed files with 718 additions and 579 deletions

File diff suppressed because it is too large Load Diff

View File

@ -230,6 +230,17 @@
<signal name="activate" handler="on_show_transports_menuitem_activate"/>
</widget>
</child>
<child>
<widget class="GtkCheckMenuItem" id="show_roster_menuitem">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Show _roster</property>
<property name="use_underline">True</property>
<property name="active">True</property>
<signal name="toggled" handler="on_show_roster_menuitem_toggled"/>
<accelerator key="R" modifiers="GDK_CONTROL_MASK" signal="activate"/>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator3">
<property name="visible">True</property>
@ -352,45 +363,65 @@
</packing>
</child>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow">
<widget class="GtkHPaned" id="roster_hpaned">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="border_width">2</property>
<property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkTreeView" id="roster_treeview">
<widget class="GtkVBox" id="roster_vbox2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">False</property>
<property name="reorderable">True</property>
<signal name="leave_notify_event" handler="on_roster_treeview_leave_notify_event"/>
<signal name="button_press_event" handler="on_roster_treeview_button_press_event"/>
<signal name="motion_notify_event" handler="on_roster_treeview_motion_notify_event"/>
<signal name="row_collapsed" handler="on_roster_treeview_row_collapsed"/>
<signal name="row_expanded" handler="on_roster_treeview_row_expanded"/>
<signal name="key_press_event" handler="on_roster_treeview_key_press_event"/>
<signal name="row_activated" handler="on_roster_treeview_row_activated"/>
<signal name="button_release_event" handler="on_roster_treeview_button_release_event"/>
<signal name="scroll_event" handler="on_roster_treeview_scroll_event"/>
<signal name="style_set" handler="on_roster_treeview_style_set"/>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="border_width">2</property>
<property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<child>
<widget class="GtkTreeView" id="roster_treeview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">False</property>
<property name="reorderable">True</property>
<signal name="leave_notify_event" handler="on_roster_treeview_leave_notify_event"/>
<signal name="button_press_event" handler="on_roster_treeview_button_press_event"/>
<signal name="motion_notify_event" handler="on_roster_treeview_motion_notify_event"/>
<signal name="row_collapsed" handler="on_roster_treeview_row_collapsed"/>
<signal name="row_expanded" handler="on_roster_treeview_row_expanded"/>
<signal name="key_press_event" handler="on_roster_treeview_key_press_event"/>
<signal name="row_activated" handler="on_roster_treeview_row_activated"/>
<signal name="button_release_event" handler="on_roster_treeview_button_release_event"/>
<signal name="scroll_event" handler="on_roster_treeview_scroll_event"/>
<signal name="style_set" handler="on_roster_treeview_style_set"/>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkComboBox" id="status_combobox">
<property name="visible">True</property>
<signal name="changed" handler="on_status_combobox_changed"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="resize">False</property>
<property name="shrink">True</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkComboBox" id="status_combobox">
<property name="visible">True</property>
<signal name="changed" handler="on_status_combobox_changed"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">2</property>
</packing>
</child>
</widget>
</child>
</widget>

View File

@ -45,7 +45,7 @@ opt_int = [ 'integer', 0 ]
opt_str = [ 'string', 0 ]
opt_bool = [ 'boolean', 0 ]
opt_color = [ 'color', '^(#[0-9a-fA-F]{6})|()$' ]
opt_one_window_types = ['never', 'always', 'peracct', 'pertype']
opt_one_window_types = ['never', 'always', 'always_with_roster', 'peracct', 'pertype']
opt_treat_incoming_messages = ['', 'chat', 'normal']
class Config:
@ -229,7 +229,7 @@ class Config:
'send_sha_in_gc_presence': [opt_bool, True, _('Jabberd1.4 does not like sha info when one join a password protected group chat. Turn this option to False to stop sending sha info in group chat presences.')],
'one_message_window': [opt_str, 'always',
#always, never, peracct, pertype should not be translated
_('Controls the window where new messages are placed.\n\'always\' - All messages are sent to a single window.\n\'never\' - All messages get their own window.\n\'peracct\' - Messages for each account are sent to a specific window.\n\'pertype\' - Each message type (e.g., chats vs. groupchats) are sent to a specific window. Note, changing this option requires restarting Gajim before the changes will take effect.')],
_('Controls the window where new messages are placed.\n\'always\' - All messages are sent to a single window.\n\'always_with_roster\' - Like \'always\' but the messages are in a single window along with the roster.\n\'never\' - All messages get their own window.\n\'peracct\' - Messages for each account are sent to a specific window.\n\'pertype\' - Each message type (e.g., chats vs. groupchats) are sent to a specific window.')],
'show_avatar_in_chat': [opt_bool, True, _('If False, you will no longer see the avatar in the chat window.')],
'escape_key_closes': [opt_bool, True, _('If True, pressing the escape key closes a tab/window.')],
'compact_view': [opt_bool, False, _('Hides the buttons in chat windows.')],

View File

@ -6,6 +6,7 @@
## Copyright (C) 2003-2005 Vincent Hanquez <tab@snarc.org>
## Copyright (C) 2006 Stefan Bethge <stefan@lanpartei.de>
## Copyright (C) 2007 Stephan Erb <steve-e@h3c.de>
## Copyright (C) 2007 Travis Shirk <travis@pobox.com>
##
## This file is part of Gajim.
##
@ -174,7 +175,7 @@ class PreferencesWindow:
# iconset
iconsets_list = os.listdir(os.path.join(gajim.DATA_DIR, 'iconsets'))
if os.path.isdir(gajim.MY_ICONSETS_PATH):
iconsets_list += os.listdir(gajim.MY_ICONSETS_PATH)
iconsets_list += os.listdir(gajim.MY_ICONSETS_PATH)
# new model, image in 0, string in 1
model = gtk.ListStore(gtk.Image, str)
renderer_image = cell_renderer_image.CellRendererImage(0, 0)
@ -537,9 +538,9 @@ class PreferencesWindow:
self.on_msg_treemodel_row_deleted)
self.default_msg_tree.get_model().connect('row-changed',
self.on_default_msg_treemodel_row_changed)
self.theme_preferences = None
self.notebook.set_current_page(0)
if not gajim.config.get('use_pep'):
self.notebook.remove_page(4)

View File

@ -1,6 +1,6 @@
## message_control.py
##
## Copyright (C) 2006 Travis Shirk <travis@pobox.com>
## Copyright (C) 2006-2007 Travis Shirk <travis@pobox.com>
## Copyright (C) 2007 Stephan Erb <steve-e@h3c.de>
##
## This file is part of Gajim.
@ -34,7 +34,7 @@ class MessageControl:
def __init__(self, type_id, parent_win, widget_name, contact, account, resource = None):
# dict { cb id : widget}
# keep all registered callbacks of widgets, created by self.xml
self.handlers = {}
self.handlers = {}
self.type_id = type_id
self.parent_win = parent_win
self.widget_name = widget_name
@ -126,8 +126,8 @@ class MessageControl:
if self.session.enable_encryption:
was_encrypted = True
print "starting a new session, dropping the old one!"
gajim.connections[self.account].delete_session(self.session.jid, self.session.thread_id)
gajim.connections[self.account].delete_session(self.session.jid,
self.session.thread_id)
self.session = session

View File

@ -6,10 +6,9 @@
## Vincent Hanquez <tab@snarc.org>
## Nikos Kouremenos <kourem@gmail.com>
## Dimitur Kirov <dkirov@gmail.com>
## Travis Shirk <travis@pobox.com>
## Norman Rasmussen <norman@rasmussen.co.za>
## Copyright (C) 2006 Travis Shirk <travis@pobox.com>
## Geobert Quach <geobert@gmail.com>
## Copyright (C) 2005-2007 Travis Shirk <travis@pobox.com>
## Copyright (C) 2006 Geobert Quach <geobert@gmail.com>
## Copyright (C) 2007 Stephan Erb <steve-e@h3c.de>
##
## This file is part of Gajim.
@ -39,7 +38,7 @@ from common import gajim
####################
class MessageWindow:
class MessageWindow(object):
'''Class for windows which contain message like things; chats,
groupchats, etc.'''
@ -53,8 +52,8 @@ class MessageWindow:
CLOSE_COMMAND,
CLOSE_CTRL_KEY
) = range(5)
def __init__(self, acct, type):
def __init__(self, acct, type, parent_window=None, parent_paned=None):
# A dictionary of dictionaries where _contacts[account][jid] == A MessageControl
self._controls = {}
# If None, the window is not tied to any specific account
@ -68,6 +67,18 @@ class MessageWindow:
self.widget_name = 'message_window'
self.xml = gtkgui_helpers.get_glade('%s.glade' % self.widget_name)
self.window = self.xml.get_widget(self.widget_name)
self.notebook = self.xml.get_widget('notebook')
self.parent_paned = None
if parent_window:
orig_window = self.window
self.window = parent_window
self.parent_paned = parent_paned
self.notebook.reparent(self.parent_paned)
self.parent_paned.pack2(self.notebook, resize=True, shrink=True)
orig_window.destroy()
del orig_window
id = self.window.connect('delete-event', self._on_window_delete)
self.handlers[id] = self.window
id = self.window.connect('destroy', self._on_window_destroy)
@ -91,7 +102,6 @@ class MessageWindow:
self.window.add_events(gtk.gdk.POINTER_MOTION_MASK)
self.alignment = self.xml.get_widget('alignment')
self.notebook = self.xml.get_widget('notebook')
id = self.notebook.connect('switch-page',
self._on_notebook_switch_page)
self.handlers[id] = self.notebook
@ -144,6 +154,9 @@ class MessageWindow:
n += len(dict)
return n
def resize(self, width, height):
gtkgui_helpers.resize_window(self.window, width, height)
def _on_window_focus(self, widget, event):
# window received focus, so if we had urgency REMOVE IT
# NOTE: we do not have to read the message (it maybe in a bg tab)
@ -179,6 +192,8 @@ class MessageWindow:
for ctrl in self.controls():
ctrl.shutdown()
self._controls.clear()
# Clean up handlers connected to the parent window, this is important since
# self.window may be the RosterWindow
for i in self.handlers.keys():
if self.handlers[i].handler_is_connected(i):
self.handlers[i].disconnect(i)
@ -210,8 +225,9 @@ class MessageWindow:
widget = xml.get_widget('tab_close_button')
id = widget.connect('clicked', self._on_close_button_clicked, control)
control.handlers[id] = widget
id = tab_label_box.connect('button-press-event', self.on_tab_eventbox_button_press_event, control.widget)
id = tab_label_box.connect('button-press-event', self.on_tab_eventbox_button_press_event,
control.widget)
control.handlers[id] = tab_label_box
self.notebook.append_page(control.widget, tab_label_box)
@ -263,7 +279,7 @@ class MessageWindow:
if not control:
# No more control in this window
return
# CTRL mask
if modifier & gtk.gdk.CONTROL_MASK:
if keyval == gtk.keysyms.h:
@ -286,7 +302,7 @@ class MessageWindow:
# Tab switch bindings
if keyval == gtk.keysyms.Right: # ALT + RIGHT
new = self.notebook.get_current_page() + 1
if new >= self.notebook.get_n_pages():
if new >= self.notebook.get_n_pages():
new = 0
self.notebook.set_current_page(new)
elif keyval == gtk.keysyms.Left: # ALT + LEFT
@ -307,7 +323,7 @@ class MessageWindow:
'''When close button is pressed: close a tab'''
self.remove_tab(control, self.CLOSE_CLOSE_BUTTON)
def show_title(self, urgent = True, control = None):
def show_title(self, urgent=True, control=None):
'''redraw the window's title'''
if not control:
control = self.get_active_control()
@ -341,10 +357,7 @@ class MessageWindow:
name += '/' + control.resource
window_mode = gajim.interface.msg_win_mgr.mode
if self.get_num_controls() == 1:
label = name
elif window_mode == MessageWindowMgr.ONE_MSG_WINDOW_PERTYPE:
if window_mode == MessageWindowMgr.ONE_MSG_WINDOW_PERTYPE:
# Show the plural form since number of tabs > 1
if self.type == 'chat':
label = _('Chats')
@ -352,9 +365,16 @@ class MessageWindow:
label = _('Group Chats')
else:
label = _('Private Chats')
elif window_mode == MessageWindowMgr.ONE_MSG_WINDOW_ALWAYS_WITH_ROSTER:
label = None
elif self.get_num_controls() == 1:
label = name
else:
label = _('Messages')
title = _('%s - Gajim') % label
title = 'Gajim'
if label:
title = _('%s - %s') % (label, title)
if window_mode == MessageWindowMgr.ONE_MSG_WINDOW_PERACCT:
title = title + ": " + control.account
@ -370,7 +390,7 @@ class MessageWindow:
ctrl = self._controls[acct][jid]
ctrl_page = self.notebook.page_num(ctrl.widget)
self.notebook.set_current_page(ctrl_page)
def remove_tab(self, ctrl, method, reason = None, force = False):
'''reason is only for gc (offline status message)
if force is True, do not ask any confirmation'''
@ -413,7 +433,13 @@ class MessageWindow:
gajim.interface.msg_win_mgr._on_window_destroy(self.window)
# dnd clean up
self.notebook.drag_dest_unset()
self.window.destroy()
if self.parent_paned:
# Don't close parent window, just remove the child
child = self.parent_paned.get_child2()
self.parent_paned.remove(child)
# FIXME: restore preferred roster size
else:
self.window.destroy()
return # don't show_title, we are dead
elif self.get_num_controls() == 1: # we are going from two tabs to one
show_tabs_if_one_tab = gajim.config.get('tabs_always_visible')
@ -542,7 +568,7 @@ class MessageWindow:
first_composing_ind = -1 # id of first composing ctrl to switch to
# if no others controls have awaiting events
# loop until finding an unread tab or having done a complete cycle
while True:
while True:
if forward == True: # look for the first unread tab on the right
ind = ind + 1
if ind >= self.notebook.get_n_pages():
@ -590,7 +616,7 @@ class MessageWindow:
if old_no >= 0:
old_ctrl = self._widget_to_control(notebook.get_nth_page(old_no))
old_ctrl.set_control_active(False)
new_ctrl = self._widget_to_control(notebook.get_nth_page(page_num))
new_ctrl.set_control_active(True)
self.show_title(control = new_ctrl)
@ -598,8 +624,8 @@ class MessageWindow:
def _on_notebook_key_press(self, widget, event):
control = self.get_active_control()
# Ctrl+PageUP / DOWN has to be handled by notebook
if event.state & gtk.gdk.CONTROL_MASK and event.keyval in (
gtk.keysyms.Page_Down, gtk.keysyms.Page_Up):
if (event.state & gtk.gdk.CONTROL_MASK and
event.keyval in (gtk.keysyms.Page_Down, gtk.keysyms.Page_Up)):
return False
if isinstance(control, ChatControlBase):
# we forwarded it to message textview
@ -609,7 +635,7 @@ class MessageWindow:
def setup_tab_dnd(self, child):
'''Set tab label as drag source and connect the drag_data_get signal'''
tab_label = self.notebook.get_tab_label(child)
tab_label.dnd_handler = tab_label.connect('drag_data_get',
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,
@ -630,11 +656,11 @@ class MessageWindow:
source_child = self.notebook.get_nth_page(source_page_num)
if dest_page_num != source_page_num:
self.notebook.reorder_child(source_child, dest_page_num)
def get_tab_at_xy(self, x, y):
'''Thanks to Gaim
Return the tab under xy and
if its nearer from left or right side of the tab
if its nearer from left or right side of the tab
'''
page_num = -1
to_right = False
@ -655,7 +681,7 @@ class MessageWindow:
if (y >= tab_alloc.y) and \
(y <= (tab_alloc.y + tab_alloc.height)):
page_num = i
if y > tab_alloc.y + (tab_alloc.height / 2.0):
to_right = True
break
@ -679,36 +705,53 @@ class MessageWindow:
tab_label.disconnect(tab_label.dnd_handler)
################################################################################
class MessageWindowMgr:
class MessageWindowMgr(gobject.GObject):
'''A manager and factory for MessageWindow objects'''
__gsignals__ = {
'window-delete': (gobject.SIGNAL_RUN_LAST, None, (object,)),
}
# These constants map to common.config.opt_one_window_types indices
(
ONE_MSG_WINDOW_NEVER,
ONE_MSG_WINDOW_ALWAYS,
ONE_MSG_WINDOW_ALWAYS_WITH_ROSTER,
ONE_MSG_WINDOW_PERACCT,
ONE_MSG_WINDOW_PERTYPE
) = range(4)
# A key constant for the main window for all messages
ONE_MSG_WINDOW_PERTYPE,
) = range(5)
# A key constant for the main window in ONE_MSG_WINDOW_ALWAYS mode
MAIN_WIN = 'main'
# A key constant for the main window in ONE_MSG_WINDOW_ALWAYS_WITH_ROSTER mode
ROSTER_MAIN_WIN = 'roster'
def __init__(self):
def __init__(self, parent_window, parent_paned):
''' A dictionary of windows; the key depends on the config:
ONE_MSG_WINDOW_NEVER: The key is the contact JID
ONE_MSG_WINDOW_ALWAYS: The key is MessageWindowMgr.MAIN_WIN
ONE_MSG_WINDOW_ALWAYS: The key is MessageWindowMgr.MAIN_WIN
ONE_MSG_WINDOW_ALWAYS_WITH_ROSTER: The key is MessageWindowMgr.MAIN_WIN
ONE_MSG_WINDOW_PERACCT: The key is the account name
ONE_MSG_WINDOW_PERTYPE: The key is a message type constant'''
gobject.GObject.__init__(self)
self._windows = {}
# Map the mode to a int constant for frequent compares
mode = gajim.config.get('one_message_window')
self.mode = common.config.opt_one_window_types.index(mode)
self.parent_win = parent_window
self.parent_paned = parent_paned
def change_account_name(self, old_name, new_name):
for win in self.windows():
win.change_account_name(old_name, new_name)
def _new_window(self, acct, type):
win = MessageWindow(acct, type)
parent_win = None
parent_paned = None
if self.mode == self.ONE_MSG_WINDOW_ALWAYS_WITH_ROSTER:
parent_win = self.parent_win
parent_paned = self.parent_paned
win = MessageWindow(acct, type, parent_win, parent_paned)
# we track the lifetime of this window
win.window.connect('delete-event', self._on_window_delete)
win.window.connect('destroy', self._on_window_destroy)
@ -729,7 +772,7 @@ class MessageWindowMgr:
def has_window(self, jid, acct):
return self.get_window(jid, acct) != None
def one_window_opened(self, contact, acct, type):
def one_window_opened(self, contact=None, acct=None, type=None):
try:
return self._windows[self._mode_to_key(contact, acct, type)] != None
except KeyError:
@ -739,10 +782,14 @@ class MessageWindowMgr:
'''Resizes window according to config settings'''
if not gajim.config.get('saveposition'):
return
if self.mode == self.ONE_MSG_WINDOW_ALWAYS:
if self.mode in (self.ONE_MSG_WINDOW_ALWAYS,
self.ONE_MSG_WINDOW_ALWAYS_WITH_ROSTER):
size = (gajim.config.get('msgwin-width'),
gajim.config.get('msgwin-height'))
if self.mode == self.ONE_MSG_WINDOW_ALWAYS_WITH_ROSTER:
parent_size = win.window.get_size()
size = (parent_size[0] + size[0], size[1])
elif self.mode == self.ONE_MSG_WINDOW_PERACCT:
size = (gajim.config.get_per('accounts', acct, 'msgwin-width'),
gajim.config.get_per('accounts', acct, 'msgwin-height'))
@ -754,13 +801,13 @@ class MessageWindowMgr:
size = (gajim.config.get(opt_width), gajim.config.get(opt_height))
else:
return
win.resize(size[0], size[1])
gtkgui_helpers.resize_window(win.window, size[0], size[1])
def _position_window(self, win, acct, type):
'''Moves window according to config settings'''
if not gajim.config.get('saveposition') or\
self.mode == self.ONE_MSG_WINDOW_NEVER:
if (not gajim.config.get('saveposition') or
self.mode in [self.ONE_MSG_WINDOW_NEVER,
self.ONE_MSG_WINDOW_ALWAYS_WITH_ROSTER]):
return
if self.mode == self.ONE_MSG_WINDOW_ALWAYS:
@ -782,21 +829,22 @@ class MessageWindowMgr:
key = acct + contact.jid
if resource:
key += '/' + resource
return key
elif self.mode == self.ONE_MSG_WINDOW_ALWAYS:
key = self.MAIN_WIN
return self.MAIN_WIN
elif self.mode == self.ONE_MSG_WINDOW_ALWAYS_WITH_ROSTER:
return self.ROSTER_MAIN_WIN
elif self.mode == self.ONE_MSG_WINDOW_PERACCT:
key = acct
return acct
elif self.mode == self.ONE_MSG_WINDOW_PERTYPE:
key = type
return key
return type
def create_window(self, contact, acct, type, resource = None):
key = None
win_acct = None
win_type = None
win_role = 'messages'
win_role = None # X11 window role
key = self._mode_to_key(contact, acct, type, resource)
win_key = self._mode_to_key(contact, acct, type, resource)
if self.mode == self.ONE_MSG_WINDOW_PERACCT:
win_acct = acct
win_role = acct
@ -806,21 +854,24 @@ class MessageWindowMgr:
elif self.mode == self.ONE_MSG_WINDOW_NEVER:
win_type = type
win_role = contact.jid
elif self.mode == self.ONE_MSG_WINDOW_ALWAYS:
win_role = 'messages'
win = None
try:
win = self._windows[key]
win = self._windows[win_key]
except KeyError:
win = self._new_window(win_acct, win_type)
win.window.set_role(win_role)
if win_role:
win.window.set_role(win_role)
# Position and size window based on saved state and window mode
if not self.one_window_opened(contact, acct, type):
self._position_window(win, acct, type)
self._resize_window(win, acct, type)
self._windows[key] = win
self._windows[win_key] = win
return win
def change_key(self, old_jid, new_jid, acct):
@ -842,6 +893,7 @@ class MessageWindowMgr:
def _on_window_destroy(self, win):
for k in self._windows.keys():
if self._windows[k].window == win:
self.emit('window-delete', self._windows[k])
del self._windows[k]
return
@ -870,17 +922,17 @@ class MessageWindowMgr:
for c in w.controls():
yield c
def shutdown(self):
def shutdown(self, width_adjust=0):
for w in self.windows():
self.save_state(w)
self.save_state(w, width_adjust)
w.window.hide()
w.window.destroy()
gajim.interface.save_config()
def save_state(self, msg_win):
def save_state(self, msg_win, width_adjust=0):
if not gajim.config.get('saveposition'):
return
# Save window size and position
pos_x_key = 'msgwin-x-position'
pos_y_key = 'msgwin-y-position'
@ -907,6 +959,9 @@ class MessageWindowMgr:
type = msg_win.type
size_width_key = type + '-msgwin-width'
size_height_key = type + '-msgwin-height'
elif self.mode == self.ONE_MSG_WINDOW_ALWAYS_WITH_ROSTER:
# Ignore any hpaned width
width = msg_win.notebook.allocation.width
if acct:
gajim.config.set_per('accounts', acct, size_width_key, width)
@ -915,11 +970,12 @@ class MessageWindowMgr:
if self.mode != self.ONE_MSG_WINDOW_NEVER:
gajim.config.set_per('accounts', acct, pos_x_key, x)
gajim.config.set_per('accounts', acct, pos_y_key, y)
else:
width += width_adjust
gajim.config.set(size_width_key, width)
gajim.config.set(size_height_key, height)
if self.mode != self.ONE_MSG_WINDOW_NEVER:
gajim.config.set(pos_x_key, x)
gajim.config.set(pos_y_key, y)
@ -944,8 +1000,7 @@ class MessageWindowMgr:
w.notebook.remove_page(0)
page.unparent()
controls.append(ctrl)
# Must clear _controls from window to prevent
# MessageControl.shutdown calls
# Must clear _controls from window to prevent MessageControl.shutdown calls
w._controls = {}
w.window.destroy()

View File

@ -6,6 +6,7 @@
## Copyright (C) 2005-2006 Andrew Sayman <lorien420@myrealbox.com>
## Copyright (C) 2007 Lukas Petrovicky <lukas@petrovicky.net>
## Copyright (C) 2007 Julien Pivotto <roidelapluie@gmail.com>
## Copyright (C) 2007 Travis Shirk <travis@pobox.com>
##
## This file is part of Gajim.
##

View File

@ -3,6 +3,7 @@
##
## Copyright (C) 2003-2007 Yann Leboulanger <asterix@lagaule.org>
## Copyright (C) 2005-2007 Nikos Kouremenos <kourem@gmail.com>
## Travis Shirk <travis@pobox.com>
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov@gmail.com>
## Copyright (C) 2007 Lukas Petrovicky <lukas@petrovicky.net>
## Copyright (C) 2007 Julien Pivotto <roidelapluie@gmail.com>
@ -1019,7 +1020,7 @@ class RosterWindow:
if gajim.config.get_per('accounts', account, 'is_zeroconf'):
continue
# single message
single_message_item = gtk.MenuItem(_('using account %s') % account,
False)
@ -1378,8 +1379,7 @@ class RosterWindow:
if contact.resource != '':
name += '/' + contact.resource
jid_with_resource = contact.jid + '/' + contact.resource
if gajim.interface.msg_win_mgr.has_window(jid_with_resource,
account):
if gajim.interface.msg_win_mgr.has_window(jid_with_resource, account):
win = gajim.interface.msg_win_mgr.get_window(jid_with_resource,
account)
ctrl = win.get_control(jid_with_resource, account)
@ -2661,7 +2661,7 @@ class RosterWindow:
ctrl = gajim.interface.minimized_controls[account][jid]
mw = gajim.interface.msg_win_mgr.get_window(ctrl.contact.jid, ctrl.account)
if not mw:
mw = gajim.interface.msg_win_mgr.create_window(ctrl.contact, \
mw = gajim.interface.msg_win_mgr.create_window(ctrl.contact,
ctrl.account, ctrl.type_id)
ctrl.parent_win = mw
mw.new_tab(ctrl)
@ -3085,8 +3085,8 @@ class RosterWindow:
contact = gajim.contacts.create_contact(jid = hostname) # Fake contact
execute_command_menuitem.connect('activate',
self.on_execute_command, contact, account)
start_chat_menuitem.connect('activate',
start_chat_menuitem.connect('activate',
self.on_new_chat_menuitem_activate, account)
gc_sub_menu = gtk.Menu() # gc is always a submenu
@ -4150,7 +4150,7 @@ class RosterWindow:
gajim.interface.instances['file_transfers'].window.present()
else:
gajim.interface.instances['file_transfers'].window.show_all()
def on_history_menuitem_activate(self, widget):
if gajim.interface.instances.has_key('logs'):
gajim.interface.instances['logs'].window.present()
@ -4252,6 +4252,7 @@ class RosterWindow:
def quit_gtkgui_interface(self):
'''When we quit the gtk interface :
tell that to the core and exit gtk'''
msgwin_width_adjust = 0
if gajim.config.get('saveposition'):
# in case show_roster_on_start is False and roster is never shown
# window.window is None
@ -4260,12 +4261,21 @@ class RosterWindow:
gajim.config.set('roster_x-position', x)
gajim.config.set('roster_y-position', y)
width, height = self.window.get_size()
# For the width use the size of the vbox containing the tree and
# status combo, this will cancel out any hpaned width
width = self.xml.get_widget('roster_vbox2').allocation.width
gajim.config.set('roster_width', width)
gajim.config.set('roster_height', height)
if not self.xml.get_widget('roster_vbox2').get_property('visible'):
# The roster vbox is hidden, so the message window is larger
# then we want to save (i.e. the window will grow every startup)
# so adjust.
msgwin_width_adjust = -1 * width
gajim.config.set('show_roster_on_startup',
self.window.get_property('visible'))
gajim.interface.msg_win_mgr.shutdown()
gajim.interface.msg_win_mgr.shutdown(msgwin_width_adjust)
gajim.config.set('collapsed_rows', '\t'.join(self.collapsed_rows))
gajim.interface.save_config()
@ -4667,6 +4677,25 @@ class RosterWindow:
gajim.config.set('showoffline', not gajim.config.get('showoffline'))
self.draw_roster()
def on_view_menu_activate(self, widget):
# Hide the show roster menu if we are not in the right windowing mode.
if self.hpaned.get_child2() is not None:
self.xml.get_widget('show_roster_menuitem').show()
else:
self.xml.get_widget('show_roster_menuitem').hide()
def on_show_roster_menuitem_toggled(self, widget):
# when num controls is 0 this menuitem is hidden, but still need to
# disable keybinding
if self.hpaned.get_child2() is not None:
self.show_roster_vbox(widget.get_active())
def show_roster_vbox(self, active):
if active:
self.xml.get_widget('roster_vbox2').show()
else:
self.xml.get_widget('roster_vbox2').hide()
def set_renderer_color(self, renderer, style, set_background = True):
'''set style for treeview cell, using PRELIGHT system color'''
if set_background:
@ -4986,7 +5015,7 @@ class RosterWindow:
# FIXME: Why do groups have to be redrawn by hand?
for g in old_groups:
self.draw_group(g, account_source)
self.draw_account(account_source)
self.draw_account(account_source)
context.finish(True, True, etime)
confirm_metacontacts = gajim.config.get('confirm_metacontacts')
@ -5058,7 +5087,7 @@ class RosterWindow:
return
if position == gtk.TREE_VIEW_DROP_BEFORE and len(path_dest) == 2:
# dropped before a group: we drop it in the previous group every time
path_dest = (path_dest[0], path_dest[1]-1)
path_dest = (path_dest[0], path_dest[1]-1)
# destination: the row something got dropped on
iter_dest = model.get_iter(path_dest)
type_dest = model[iter_dest][C_TYPE].decode('utf-8')
@ -5112,7 +5141,7 @@ class RosterWindow:
uri_splitted))
dialog.popup()
return
# a roster entry was dragged and dropped somewhere in the roster
# source: the row that was dragged
@ -5120,9 +5149,9 @@ class RosterWindow:
iter_source = model.get_iter(path_source)
type_source = model[iter_source][C_TYPE]
account_source = model[iter_source][C_ACCOUNT].decode('utf-8')
# Only normal contacts can be dragged
if type_source != 'contact':
if type_source != 'contact':
return
if gajim.config.get_per('accounts', account_source, 'is_zeroconf'):
return
@ -5172,7 +5201,7 @@ class RosterWindow:
if grp_source == grp_dest and account_source == account_dest:
# Drop on self
return
# contact drop somewhere in or on a foreign account
if (type_dest == 'account' or not self.regroup) and \
account_source != account_dest:
@ -5180,7 +5209,7 @@ class RosterWindow:
dialogs.AddNewContactWindow(account = account_dest, jid = jid_source,
user_nick = c_source.name, group = grp_dest)
return
# we may not add contacts from special_groups
if grp_source in helpers.special_groups :
return
@ -5194,7 +5223,7 @@ class RosterWindow:
self.on_drop_in_group(None, account_source, c_source, grp_dest,
is_big_brother, context, etime, grp_source)
return
# Contact drop on another contact, make meta contacts
if position == gtk.TREE_VIEW_DROP_INTO_OR_AFTER or \
position == gtk.TREE_VIEW_DROP_INTO_OR_BEFORE:
@ -5209,21 +5238,30 @@ class RosterWindow:
def show_title(self):
change_title_allowed = gajim.config.get('change_roster_title')
if not change_title_allowed:
return
if gajim.config.get('one_message_window') == 'always_with_roster':
# always_with_roster mode defers to the MessageWindow
if not gajim.interface.msg_win_mgr.one_window_opened():
# No MessageWindow to defer to
self.window.set_title('Gajim')
return
nb_unread = 0
if change_title_allowed:
start = ''
for account in gajim.connections:
# Count events in roster title only if we don't auto open them
if not helpers.allow_popup_window(account):
nb_unread += gajim.events.get_nb_events(['chat', 'normal',
'file-request', 'file-error', 'file-completed',
'file-request-error', 'file-send-error', 'file-stopped',
'printed_chat'], account)
if nb_unread > 1:
start = '[' + str(nb_unread) + '] '
elif nb_unread == 1:
start = '* '
self.window.set_title(start + 'Gajim')
start = ''
for account in gajim.connections:
# Count events in roster title only if we don't auto open them
if not helpers.allow_popup_window(account):
nb_unread += gajim.events.get_nb_events(['chat', 'normal',
'file-request', 'file-error', 'file-completed',
'file-request-error', 'file-send-error', 'file-stopped',
'printed_chat'], account)
if nb_unread > 1:
start = '[' + str(nb_unread) + '] '
elif nb_unread == 1:
start = '* '
self.window.set_title(start + 'Gajim')
gtkgui_helpers.set_unset_urgency_hint(self.window, nb_unread)
@ -5304,7 +5342,7 @@ class RosterWindow:
accels = gtk.AccelGroup()
self.xml.get_widget('roster_window').add_accel_group(accels)
prefs_item.add_accelerator('activate', accels, ord(','),
gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE)
gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE)
app_menu.append(prefs_item)
app_menu.append(gtk.MenuItem('__SKIP__'))
app_menu.append(gtk.MenuItem('__SKIP__'))
@ -5322,11 +5360,20 @@ class RosterWindow:
#self.xml.get_widget('menubar').hide()
return
def _on_message_window_delete(self, win_mgr, msg_win):
if gajim.config.get('one_message_window') == 'always_with_roster':
self.show_roster_vbox(True)
gtkgui_helpers.resize_window(self.window,
gajim.config.get('roster_width'),
gajim.config.get('roster_height'))
def __init__(self):
self.xml = gtkgui_helpers.get_glade('roster_window.glade')
self.window = self.xml.get_widget('roster_window')
self.hpaned = self.xml.get_widget('roster_hpaned')
self._music_track_changed_signal = None
gajim.interface.msg_win_mgr = MessageWindowMgr()
gajim.interface.msg_win_mgr = MessageWindowMgr(self.window, self.hpaned)
gajim.interface.msg_win_mgr.connect('window-delete', self._on_message_window_delete)
self.advanced_menus = [] # We keep them to destroy them
if gajim.config.get('roster_window_skip_taskbar'):
self.window.set_property('skip-taskbar-hint', True)
@ -5460,6 +5507,8 @@ class RosterWindow:
self.xml.get_widget('show_transports_menuitem').set_active(
show_transports_group)
self.xml.get_widget('show_roster_menuitem').set_active(True)
# columns
# this col has 3 cells:
@ -5549,3 +5598,4 @@ class RosterWindow:
if sys.platform == 'darwin':
self.setup_for_osx()