merged trunk r6780
removed i18n imports from zeroconf/connection files
This commit is contained in:
parent
61dab0d59a
commit
2b3120244f
11
src/Makefile
11
src/Makefile
|
@ -1,21 +1,24 @@
|
|||
# Set the C flags to include the GTK+ and Python libraries
|
||||
PYTHON ?= python
|
||||
PYTHONVER = `$(PYTHON) -c 'import sys; print sys.version[:3]'`
|
||||
CFLAGS = `pkg-config --cflags gtk+-2.0 pygtk-2.0` -fPIC -I/usr/include/python$(PYTHONVER) -I.
|
||||
LDFLAGS = `pkg-config --libs gtk+-2.0 pygtk-2.0` -lpython$(PYTHONVER)
|
||||
gtk_CFLAGS = `pkg-config --cflags gtk+-2.0 pygtk-2.0` -fPIC -I/usr/include/python$(PYTHONVER) -I.
|
||||
gtk_LDFLAGS = `pkg-config --libs gtk+-2.0 pygtk-2.0` -lpython$(PYTHONVER)
|
||||
|
||||
all: trayicon.so gtkspell.so
|
||||
|
||||
# Build the shared objects
|
||||
trayicon.so: trayicon.o eggtrayicon.o trayiconmodule.o
|
||||
$(CC) -shared $^ -o $@ $(LDFLAGS)
|
||||
$(CC) -shared $^ -o $@ $(LDFLAGS) $(gtk_LDFLAGS)
|
||||
|
||||
gtkspell.so:
|
||||
$(CC) $(OPTFLAGS) $(CFLAGS) `pkg-config --cflags gtkspell-2.0` -shared gtkspellmodule.c $^ -o $@ $(LDFLAGS) `pkg-config --libs gtkspell-2.0`
|
||||
$(CC) $(OPTFLAGS) $(CFLAGS) $(LDFLAGS) $(gtk_CFLAGS) $(gtk_LDFLAGS) `pkg-config --libs --cflags gtkspell-2.0` -shared gtkspellmodule.c $^ -o $@
|
||||
|
||||
# The path to the GTK+ python types
|
||||
DEFS=`pkg-config --variable=defsdir pygtk-2.0`
|
||||
|
||||
%.o: %.c
|
||||
$(CC) -o $@ -c $< $(CFLAGS) $(gtk_CFLAGS)
|
||||
|
||||
# Generate the C wrapper from the defs and our override file
|
||||
trayicon.c: trayicon.defs trayicon.override
|
||||
pygtk-codegen-2.0 --prefix trayicon \
|
||||
|
|
|
@ -25,16 +25,9 @@
|
|||
##
|
||||
|
||||
import gtk
|
||||
import gtk.glade
|
||||
import gtkgui_helpers
|
||||
|
||||
from common import gajim
|
||||
from common import i18n
|
||||
|
||||
_ = i18n._
|
||||
APP = i18n.APP
|
||||
gtk.glade.bindtextdomain(APP, i18n.DIR)
|
||||
gtk.glade.textdomain(APP)
|
||||
|
||||
(
|
||||
OPT_TYPE,
|
||||
|
@ -53,6 +46,8 @@ class AdvancedConfigurationWindow:
|
|||
def __init__(self):
|
||||
self.xml = gtkgui_helpers.get_glade('advanced_configuration_window.glade')
|
||||
self.window = self.xml.get_widget('advanced_configuration_window')
|
||||
self.window.set_transient_for(
|
||||
gajim.interface.instances['preferences'].window)
|
||||
self.entry = self.xml.get_widget('advanced_entry')
|
||||
self.desc_label = self.xml.get_widget('advanced_desc_label')
|
||||
self.restart_label = self.xml.get_widget('restart_label')
|
||||
|
|
|
@ -18,13 +18,13 @@
|
|||
import os
|
||||
import time
|
||||
import gtk
|
||||
import gtk.glade
|
||||
import pango
|
||||
import gobject
|
||||
import gtkgui_helpers
|
||||
import message_control
|
||||
import dialogs
|
||||
import history_window
|
||||
import notify
|
||||
|
||||
from common import gajim
|
||||
from common import helpers
|
||||
|
@ -41,11 +41,14 @@ try:
|
|||
except:
|
||||
HAS_GTK_SPELL = False
|
||||
|
||||
####################
|
||||
# FIXME: Can't this stuff happen once?
|
||||
from common import i18n
|
||||
_ = i18n._
|
||||
APP = i18n.APP
|
||||
|
||||
# the next script, executed in the "po" directory,
|
||||
# generates the following list.
|
||||
##!/bin/sh
|
||||
#LANG=$(for i in *.po; do j=${i/.po/}; echo -n "_('"$j"')":" '"$j"', " ; done)
|
||||
#echo "{_('en'):'en'",$LANG"}"
|
||||
langs = {_('English'): 'en', _('Bulgarian'): 'bg', _('Briton'): 'br', _('Czech'): 'cs', _('German'): 'de', _('Greek'): 'el', _('Esperanto'): 'eo', _('Spanish'): 'es', _('Basc'): 'eu', _('French'): 'fr', _('Croatian'): 'hr', _('Italian'): 'it', _('Norvegian b'): 'nb', _('Dutch'): 'nl', _('Norvegian'): 'no', _('Polish'): 'pl', _('Portuguese'): 'pt', _('Brazilian Portuguese'): 'pt_BR', _('Russian'): 'ru', _('Slovak'): 'sk', _('Swedish'): 'sv', _('Chinese (Ch)'): 'zh_CN'}
|
||||
|
||||
|
||||
################################################################################
|
||||
class ChatControlBase(MessageControl):
|
||||
|
@ -56,7 +59,7 @@ class ChatControlBase(MessageControl):
|
|||
theme = gajim.config.get('roster_theme')
|
||||
bannerfont = gajim.config.get_per('themes', theme, 'bannerfont')
|
||||
bannerfontattrs = gajim.config.get_per('themes', theme, 'bannerfontattrs')
|
||||
|
||||
|
||||
if bannerfont:
|
||||
font = pango.FontDescription(bannerfont)
|
||||
else:
|
||||
|
@ -67,16 +70,24 @@ class ChatControlBase(MessageControl):
|
|||
font.set_weight(pango.WEIGHT_HEAVY)
|
||||
if 'I' in bannerfontattrs:
|
||||
font.set_style(pango.STYLE_ITALIC)
|
||||
|
||||
|
||||
font_attrs = 'font_desc="%s"' % font.to_string()
|
||||
|
||||
|
||||
# in case there is no font specified we use x-large font size
|
||||
if font.get_size() == 0:
|
||||
font_attrs = '%s size="x-large"' % font_attrs
|
||||
font.set_weight(pango.WEIGHT_NORMAL)
|
||||
font_attrs_small = 'font_desc="%s" size="small"' % font.to_string()
|
||||
return (font_attrs, font_attrs_small)
|
||||
|
||||
|
||||
def get_nb_unread(self):
|
||||
jid = self.contact.jid
|
||||
if self.resource:
|
||||
jid += '/' + self.resource
|
||||
type_ = self.type_id
|
||||
return len(gajim.events.get_events(self.account, jid, ['printed_' + type_,
|
||||
type_]))
|
||||
|
||||
def draw_banner(self):
|
||||
self._paint_banner()
|
||||
self._update_banner_state_image()
|
||||
|
@ -97,14 +108,15 @@ class ChatControlBase(MessageControl):
|
|||
event_keymod):
|
||||
pass # Derived should implement this rather than connecting to the event itself.
|
||||
|
||||
def __init__(self, type_id, parent_win, widget_name, display_names, contact, acct, resource = None):
|
||||
def __init__(self, type_id, parent_win, widget_name, display_names, contact,
|
||||
acct, resource = None):
|
||||
MessageControl.__init__(self, type_id, parent_win, widget_name,
|
||||
display_names, contact, acct, resource = resource);
|
||||
# when/if we do XHTML we will but formatting buttons back
|
||||
widget = self.xml.get_widget('emoticons_button')
|
||||
id = widget.connect('clicked', self.on_emoticons_button_clicked)
|
||||
self.handlers[id] = widget
|
||||
|
||||
|
||||
id = self.widget.connect('key_press_event', self._on_keypress_event)
|
||||
self.handlers[id] = self.widget
|
||||
|
||||
|
@ -112,10 +124,10 @@ class ChatControlBase(MessageControl):
|
|||
id = widget.connect('button-press-event',
|
||||
self._on_banner_eventbox_button_press_event)
|
||||
self.handlers[id] = widget
|
||||
|
||||
|
||||
# Create textviews and connect signals
|
||||
self.conv_textview = ConversationTextview(self.account)
|
||||
|
||||
|
||||
self.conv_scrolledwindow = self.xml.get_widget(
|
||||
'conversation_scrolledwindow')
|
||||
self.conv_scrolledwindow.add(self.conv_textview.tv)
|
||||
|
@ -127,20 +139,23 @@ class ChatControlBase(MessageControl):
|
|||
self.msg_scrolledwindow = self.xml.get_widget('message_scrolledwindow')
|
||||
self.msg_textview = MessageTextView()
|
||||
id = self.msg_textview.connect('mykeypress',
|
||||
self._on_message_textview_mykeypress_event)
|
||||
self._on_message_textview_mykeypress_event)
|
||||
self.handlers[id] = self.msg_textview
|
||||
self.msg_scrolledwindow.add(self.msg_textview)
|
||||
id = self.msg_textview.connect('key_press_event',
|
||||
self._on_message_textview_key_press_event)
|
||||
self._on_message_textview_key_press_event)
|
||||
self.handlers[id] = self.msg_textview
|
||||
id = self.msg_textview.connect('size-request', self.size_request)
|
||||
self.handlers[id] = self.msg_textview
|
||||
id = self.msg_textview.connect('populate_popup',
|
||||
self.on_msg_textview_populate_popup)
|
||||
self.handlers[id] = self.msg_textview
|
||||
|
||||
self.update_font()
|
||||
|
||||
# Hook up send button
|
||||
widget = self.xml.get_widget('send_button')
|
||||
id = widget.connect('clicked',
|
||||
self._on_send_button_clicked)
|
||||
id = widget.connect('clicked', self._on_send_button_clicked)
|
||||
self.handlers[id] = widget
|
||||
|
||||
# the following vars are used to keep history of user's messages
|
||||
|
@ -149,8 +164,6 @@ class ChatControlBase(MessageControl):
|
|||
self.typing_new = False
|
||||
self.orig_msg = ''
|
||||
|
||||
self.nb_unread = 0
|
||||
|
||||
# Emoticons menu
|
||||
# set image no matter if user wants at this time emoticons or not
|
||||
# (so toggle works ok)
|
||||
|
@ -162,8 +175,27 @@ class ChatControlBase(MessageControl):
|
|||
# Attach speller
|
||||
if gajim.config.get('use_speller') and HAS_GTK_SPELL:
|
||||
try:
|
||||
gtkspell.Spell(self.msg_textview)
|
||||
except gobject.GError, msg:
|
||||
spell = gtkspell.Spell(self.msg_textview)
|
||||
# loop removing non-existant dictionaries
|
||||
# iterating on a copy
|
||||
for lang in dict(langs):
|
||||
try:
|
||||
spell.set_language(langs[lang])
|
||||
except:
|
||||
del langs[lang]
|
||||
# now set the one the user selected
|
||||
per_type = 'contacts'
|
||||
if self.type_id == message_control.TYPE_GC:
|
||||
per_type = 'rooms'
|
||||
lang = gajim.config.get_per(per_type, self.contact.jid,
|
||||
'speller_language')
|
||||
if not lang:
|
||||
# use the default one
|
||||
lang = gajim.config.get('speller_language')
|
||||
if lang:
|
||||
self.msg_textview.lang = lang
|
||||
spell.set_language(lang)
|
||||
except (gobject.GError, RuntimeError), msg:
|
||||
#FIXME: add a ui for this use spell.set_language()
|
||||
dialogs.ErrorDialog(unicode(msg), _('If that is not your language '
|
||||
'for which you want to highlight misspelled words, then please '
|
||||
|
@ -175,6 +207,48 @@ class ChatControlBase(MessageControl):
|
|||
|
||||
self.style_event_id = 0
|
||||
self.conv_textview.tv.show()
|
||||
|
||||
# For JEP-0172
|
||||
self.user_nick = None
|
||||
|
||||
def on_msg_textview_populate_popup(self, textview, menu):
|
||||
'''we override the default context menu and we prepend an option to switch languages'''
|
||||
def _on_select_dictionary(widget, lang):
|
||||
per_type = 'contacts'
|
||||
if self.type_id == message_control.TYPE_GC:
|
||||
per_type = 'rooms'
|
||||
if not gajim.config.get_per(per_type, self.contact.jid):
|
||||
gajim.config.add_per(per_type, self.contact.jid)
|
||||
gajim.config.set_per(per_type, self.contact.jid, 'speller_language',
|
||||
lang)
|
||||
spell = gtkspell.get_from_text_view(self.msg_textview)
|
||||
self.msg_textview.lang = lang
|
||||
spell.set_language(lang)
|
||||
widget.set_active(True)
|
||||
|
||||
item = gtk.SeparatorMenuItem()
|
||||
menu.prepend(item)
|
||||
|
||||
if gajim.config.get('use_speller') and HAS_GTK_SPELL:
|
||||
item = gtk.MenuItem(_('Spelling language'))
|
||||
menu.prepend(item)
|
||||
submenu = gtk.Menu()
|
||||
item.set_submenu(submenu)
|
||||
for lang in sorted(langs):
|
||||
item = gtk.CheckMenuItem(lang)
|
||||
if langs[lang] == self.msg_textview.lang:
|
||||
item.set_active(True)
|
||||
submenu.append(item)
|
||||
id = item.connect('activate', _on_select_dictionary, langs[lang])
|
||||
self.handlers[id] = item
|
||||
|
||||
item = gtk.ImageMenuItem(gtk.STOCK_CLEAR)
|
||||
menu.prepend(item)
|
||||
id = item.connect('activate', self.msg_textview.clear)
|
||||
self.handlers[id] = item
|
||||
|
||||
menu.show_all()
|
||||
|
||||
# moved from ChatControl
|
||||
def _on_banner_eventbox_button_press_event(self, widget, event):
|
||||
'''If right-clicked, show popup'''
|
||||
|
@ -251,7 +325,7 @@ class ChatControlBase(MessageControl):
|
|||
if event.state & gtk.gdk.CONTROL_MASK:
|
||||
# CTRL + l|L: clear conv_textview
|
||||
if event.keyval == gtk.keysyms.l or event.keyval == gtk.keysyms.L:
|
||||
self.conv_textview.tv.get_buffer().set_text('')
|
||||
self.conv_textview.clear()
|
||||
return True
|
||||
# CTRL + v: Paste into msg_textview
|
||||
elif event.keyval == gtk.keysyms.v:
|
||||
|
@ -425,13 +499,18 @@ class ChatControlBase(MessageControl):
|
|||
if not message or message == '\n':
|
||||
return
|
||||
|
||||
|
||||
if not self._process_command(message):
|
||||
MessageControl.send_message(self, message, keyID, type = type,
|
||||
chatstate = chatstate, msg_id = msg_id,
|
||||
composing_jep = composing_jep, resource = resource)
|
||||
composing_jep = composing_jep, resource = resource,
|
||||
user_nick = self.user_nick)
|
||||
# Record message history
|
||||
self.save_sent_message(message)
|
||||
|
||||
# Be sure to send user nickname only once according to JEP-0172
|
||||
self.user_nick = None
|
||||
|
||||
# Clear msg input
|
||||
message_buffer = self.msg_textview.get_buffer()
|
||||
message_buffer.set_text('') # clear message buffer (and tv of course)
|
||||
|
@ -473,12 +552,25 @@ class ChatControlBase(MessageControl):
|
|||
gajim.last_message_time[self.account][full_jid] = time.time()
|
||||
urgent = True
|
||||
if (not self.parent_win.get_active_jid() or \
|
||||
full_jid != self.parent_win.get_active_jid() or \
|
||||
not self.parent_win.is_active() or not end) and \
|
||||
kind in ('incoming', 'incoming_queue'):
|
||||
self.nb_unread += 1
|
||||
if gajim.interface.systray_enabled and self.notify_on_new_messages():
|
||||
gajim.interface.systray.add_jid(full_jid, self.account, self.type_id)
|
||||
full_jid != self.parent_win.get_active_jid() or \
|
||||
not self.parent_win.is_active() or not end) and \
|
||||
kind in ('incoming', 'incoming_queue'):
|
||||
if self.notify_on_new_messages():
|
||||
type_ = 'printed_' + self.type_id
|
||||
if self.type_id == message_control.TYPE_GC:
|
||||
type_ = 'printed_gc_msg'
|
||||
show_in_roster = notify.get_show_in_roster('message_received',
|
||||
self.account, self.contact)
|
||||
show_in_systray = notify.get_show_in_systray('message_received',
|
||||
self.account, self.contact)
|
||||
event = gajim.events.create_event(type_, None,
|
||||
show_in_roster = show_in_roster,
|
||||
show_in_systray = show_in_systray)
|
||||
gajim.events.add_event(self.account, full_jid, event)
|
||||
# We need to redraw contact if we show in roster
|
||||
if show_in_roster:
|
||||
gajim.interface.roster.draw_contact(self.contact.jid,
|
||||
self.account)
|
||||
self.parent_win.redraw_tab(self)
|
||||
if not self.parent_win.is_active():
|
||||
ctrl = gajim.interface.msg_win_mgr.get_control(full_jid,
|
||||
|
@ -503,6 +595,7 @@ class ChatControlBase(MessageControl):
|
|||
else: # we are the beginning of buffer
|
||||
buffer.insert_at_cursor('%s ' % str_)
|
||||
self.msg_textview.grab_focus()
|
||||
|
||||
def on_emoticons_button_clicked(self, widget):
|
||||
'''popup emoticons menu'''
|
||||
gajim.interface.emoticon_menuitem_clicked = self.append_emoticon
|
||||
|
@ -510,12 +603,10 @@ class ChatControlBase(MessageControl):
|
|||
|
||||
def on_actions_button_clicked(self, widget):
|
||||
'''popup action menu'''
|
||||
#FIXME: BUG http://bugs.gnome.org/show_bug.cgi?id=316786
|
||||
self.button_clicked = widget
|
||||
|
||||
menu = self.prepare_context_menu()
|
||||
menu.show_all()
|
||||
gtkgui_helpers.popup_emoticons_under_button(menu, widget, self.parent_win)
|
||||
gtkgui_helpers.popup_emoticons_under_button(menu, widget,
|
||||
self.parent_win)
|
||||
|
||||
def update_font(self):
|
||||
font = pango.FontDescription(gajim.config.get('conversation_font'))
|
||||
|
@ -549,15 +640,24 @@ class ChatControlBase(MessageControl):
|
|||
if state:
|
||||
jid = self.contact.jid
|
||||
if self.conv_textview.at_the_end():
|
||||
#we are at the end
|
||||
if self.nb_unread > 0:
|
||||
self.nb_unread = self.get_specific_unread()
|
||||
# we are at the end
|
||||
type_ = 'printed_' + self.type_id
|
||||
if self.type_id == message_control.TYPE_GC:
|
||||
type_ = 'printed_gc_msg'
|
||||
if not gajim.events.remove_events(self.account, self.get_full_jid(),
|
||||
types = [type_]):
|
||||
# There were events to remove
|
||||
self.parent_win.redraw_tab(self)
|
||||
self.parent_win.show_title()
|
||||
if gajim.interface.systray_enabled:
|
||||
gajim.interface.systray.remove_jid(self.get_full_jid(),
|
||||
self.account,
|
||||
self.type_id)
|
||||
# redraw roster
|
||||
if self.type_id == message_control.TYPE_PM:
|
||||
room_jid, nick = gajim.get_room_and_nick_from_fjid(jid)
|
||||
groupchat_control = gajim.interface.msg_win_mgr.get_control(
|
||||
room_jid, self.account)
|
||||
groupchat_control.draw_contact(nick)
|
||||
else:
|
||||
gajim.interface.roster.draw_contact(jid, self.account)
|
||||
gajim.interface.roster.show_title()
|
||||
self.msg_textview.grab_focus()
|
||||
# Note, we send None chatstate to preserve current
|
||||
self.parent_win.redraw_tab(self)
|
||||
|
@ -630,19 +730,28 @@ class ChatControlBase(MessageControl):
|
|||
return True
|
||||
|
||||
def on_conversation_vadjustment_value_changed(self, widget):
|
||||
if not self.nb_unread:
|
||||
if self.resource:
|
||||
jid = self.contact.get_full_jid()
|
||||
else:
|
||||
jid = self.contact.jid
|
||||
type_ = self.type_id
|
||||
if type_ == message_control.TYPE_GC:
|
||||
type_ = 'gc_msg'
|
||||
if not len(gajim.events.get_events(self.account, jid, ['printed_' + type_,
|
||||
type_])):
|
||||
return
|
||||
jid = self.contact.jid
|
||||
if self.conv_textview.at_the_end() and \
|
||||
self.parent_win.get_active_control() == self and \
|
||||
self.parent_win.window.is_active():
|
||||
#we are at the end
|
||||
self.nb_unread = self.get_specific_unread()
|
||||
self.parent_win.redraw_tab(self)
|
||||
self.parent_win.show_title()
|
||||
if gajim.interface.systray_enabled:
|
||||
gajim.interface.systray.remove_jid(jid, self.account,
|
||||
self.type_id)
|
||||
# we are at the end
|
||||
type_ = self.type_id
|
||||
if type_ == message_control.TYPE_GC:
|
||||
type_ = 'gc_msg'
|
||||
if not gajim.events.remove_events(self.account, self.get_full_jid(),
|
||||
types = ['printed_' + type_, type_]):
|
||||
# There were events to remove
|
||||
self.parent_win.redraw_tab(self)
|
||||
self.parent_win.show_title()
|
||||
|
||||
def sent_messages_scroll(self, direction, conv_buf):
|
||||
size = len(self.sent_history)
|
||||
|
@ -701,6 +810,7 @@ class ChatControlBase(MessageControl):
|
|||
def got_disconnected(self):
|
||||
self.msg_textview.set_sensitive(False)
|
||||
self.msg_textview.set_editable(False)
|
||||
self.conv_textview.tv.grab_focus()
|
||||
self.xml.get_widget('send_button').set_sensitive(False)
|
||||
|
||||
################################################################################
|
||||
|
@ -754,10 +864,12 @@ class ChatControl(ChatControlBase):
|
|||
id = widget.connect('enter-notify-event', self.on_avatar_eventbox_enter_notify_event)
|
||||
self.handlers[id] = widget
|
||||
|
||||
widget = self.xml.get_widget('avatar_eventbox')
|
||||
id = widget.connect('leave-notify-event', self.on_avatar_eventbox_leave_notify_event)
|
||||
self.handlers[id] = widget
|
||||
|
||||
id = widget.connect('button-press-event', self.on_avatar_eventbox_button_press_event)
|
||||
self.handlers[id] = widget
|
||||
|
||||
widget = self.xml.get_widget('gpg_togglebutton')
|
||||
id = widget.connect('clicked', self.on_toggle_gpg_togglebutton)
|
||||
self.handlers[id] = widget
|
||||
|
@ -769,6 +881,8 @@ class ChatControl(ChatControlBase):
|
|||
self.update_ui()
|
||||
# restore previous conversation
|
||||
self.restore_conversation()
|
||||
# is account displayed after nick in banner ?
|
||||
self.account_displayed= False
|
||||
|
||||
def notify_on_new_messages(self):
|
||||
return gajim.config.get('trayicon_notification_on_new_messages')
|
||||
|
@ -803,6 +917,23 @@ class ChatControl(ChatControlBase):
|
|||
if self.show_bigger_avatar_timeout_id is not None:
|
||||
gobject.source_remove(self.show_bigger_avatar_timeout_id)
|
||||
|
||||
def on_avatar_eventbox_button_press_event(self, widget, event):
|
||||
'''If right-clicked, show popup'''
|
||||
if event.button == 3: # right click
|
||||
menu = gtk.Menu()
|
||||
menuitem = gtk.ImageMenuItem(gtk.STOCK_SAVE_AS)
|
||||
id = menuitem.connect('activate',
|
||||
gtkgui_helpers.on_avatar_save_as_menuitem_activate,
|
||||
self.contact.jid, self.account, self.contact.name + '.jpeg')
|
||||
self.handlers[id] = menuitem
|
||||
menu.append(menuitem)
|
||||
menu.show_all()
|
||||
menu.connect('selection-done', lambda w:w.destroy())
|
||||
# show the menu
|
||||
menu.show_all()
|
||||
menu.popup(None, None, None, event.button, event.time)
|
||||
return True
|
||||
|
||||
def _on_window_motion_notify(self, widget, event):
|
||||
'''it gets called no matter if it is the active window or not'''
|
||||
if self.parent_win.get_active_jid() == self.contact.jid:
|
||||
|
@ -874,17 +1005,27 @@ class ChatControl(ChatControlBase):
|
|||
if self.resource:
|
||||
name += '/' + self.resource
|
||||
avoid_showing_account_too = True
|
||||
if self.TYPE_ID == message_control.TYPE_PM:
|
||||
room_jid = self.contact.jid.split('/')[0]
|
||||
room_ctrl = gajim.interface.msg_win_mgr.get_control(room_jid,
|
||||
self.account)
|
||||
name = _('%s from room %s') % (name, room_ctrl.name)
|
||||
name = gtkgui_helpers.escape_for_pango_markup(name)
|
||||
|
||||
# We know our contacts nick, but if there are any other controls
|
||||
# with the same nick we need to also display the account
|
||||
# except if we are talking to two different resources of the same contact
|
||||
acct_info = ''
|
||||
self.account_displayed = False
|
||||
for ctrl in self.parent_win.controls():
|
||||
if ctrl == self:
|
||||
continue
|
||||
if self.contact.get_shown_name() == ctrl.contact.get_shown_name()\
|
||||
and not avoid_showing_account_too:
|
||||
self.account_displayed = True
|
||||
if not ctrl.account_displayed:
|
||||
# do that after this instance exists
|
||||
gobject.idle_add(ctrl.draw_banner)
|
||||
acct_info = ' (%s)' % \
|
||||
gtkgui_helpers.escape_for_pango_markup(self.account)
|
||||
break
|
||||
|
@ -901,9 +1042,9 @@ class ChatControl(ChatControlBase):
|
|||
if cs and st in ('composing_only', 'all'):
|
||||
if contact.show == 'offline':
|
||||
chatstate = ''
|
||||
elif st == 'all' and contact.composing_jep == 'JEP-0085':
|
||||
elif contact.composing_jep == 'JEP-0085':
|
||||
chatstate = helpers.get_uf_chatstate(cs)
|
||||
elif st == 'composing_only' or contact.composing_jep == 'JEP-0022':
|
||||
elif contact.composing_jep == 'JEP-0022':
|
||||
if cs in ('composing', 'paused'):
|
||||
# only print composing, paused
|
||||
chatstate = helpers.get_uf_chatstate(cs)
|
||||
|
@ -911,16 +1052,16 @@ class ChatControl(ChatControlBase):
|
|||
chatstate = ''
|
||||
elif chatstate is None:
|
||||
chatstate = helpers.get_uf_chatstate(cs)
|
||||
|
||||
|
||||
label_text = '<span %s>%s</span><span %s>%s %s</span>' % \
|
||||
(font_attrs, name, font_attrs_small, acct_info, chatstate)
|
||||
(font_attrs, name, font_attrs_small, acct_info, chatstate)
|
||||
else:
|
||||
# weight="heavy" size="x-large"
|
||||
label_text = '<span %s>%s</span><span %s>%s</span>' % \
|
||||
(font_attrs, name, font_attrs_small, acct_info)
|
||||
(font_attrs, name, font_attrs_small, acct_info)
|
||||
if status_escaped:
|
||||
label_text += '\n<span %s>%s</span>' %\
|
||||
(font_attrs_small, status_escaped)
|
||||
(font_attrs_small, status_escaped)
|
||||
banner_eventbox = self.xml.get_widget('banner_eventbox')
|
||||
self.status_tooltip.set_tip(banner_eventbox, status)
|
||||
self.status_tooltip.enable()
|
||||
|
@ -928,7 +1069,7 @@ class ChatControl(ChatControlBase):
|
|||
self.status_tooltip.disable()
|
||||
# setup the label that holds name and jid
|
||||
banner_name_label.set_markup(label_text)
|
||||
|
||||
|
||||
def on_toggle_gpg_togglebutton(self, widget):
|
||||
gajim.config.set_per('contacts', self.contact.get_full_jid(),
|
||||
'gpg_enabled', widget.get_active())
|
||||
|
@ -943,12 +1084,12 @@ class ChatControl(ChatControlBase):
|
|||
tt = _('OpenPGP Encryption')
|
||||
|
||||
# restore gpg pref
|
||||
gpg_pref = gajim.config.get_per('contacts',
|
||||
self.contact.get_full_jid(), 'gpg_enabled')
|
||||
gpg_pref = gajim.config.get_per('contacts', self.contact.jid,
|
||||
'gpg_enabled')
|
||||
if gpg_pref == None:
|
||||
gajim.config.add_per('contacts', self.contact.get_full_jid())
|
||||
gpg_pref = gajim.config.get_per('contacts',
|
||||
self.contact.get_full_jid(), 'gpg_enabled')
|
||||
gajim.config.add_per('contacts', self.contact.jid)
|
||||
gpg_pref = gajim.config.get_per('contacts', self.contact.jid,
|
||||
'gpg_enabled')
|
||||
tb.set_active(gpg_pref)
|
||||
|
||||
else:
|
||||
|
@ -1108,7 +1249,12 @@ class ChatControl(ChatControlBase):
|
|||
|
||||
def get_tab_label(self, chatstate):
|
||||
unread = ''
|
||||
num_unread = self.nb_unread
|
||||
if self.resource:
|
||||
jid = self.contact.get_full_jid()
|
||||
else:
|
||||
jid = self.contact.jid
|
||||
num_unread = len(gajim.events.get_events(self.account, jid,
|
||||
['printed_' + self.type_id, self.type_id]))
|
||||
if num_unread == 1 and not gajim.config.get('show_unread_tab_icon'):
|
||||
unread = '*'
|
||||
elif num_unread > 1:
|
||||
|
@ -1151,7 +1297,12 @@ class ChatControl(ChatControlBase):
|
|||
return (label_str, color)
|
||||
|
||||
def get_tab_image(self):
|
||||
num_unread = self.nb_unread
|
||||
if self.resource:
|
||||
jid = self.contact.get_full_jid()
|
||||
else:
|
||||
jid = self.contact.jid
|
||||
num_unread = len(gajim.events.get_events(self.account, jid,
|
||||
['printed_' + self.type_id, self.type_id]))
|
||||
# Set tab image (always 16x16); unread messages show the 'message' image
|
||||
tab_img = None
|
||||
|
||||
|
@ -1160,8 +1311,8 @@ class ChatControl(ChatControlBase):
|
|||
self.contact.jid, icon_name = 'message')
|
||||
tab_img = img_16['message']
|
||||
else:
|
||||
contact = gajim.contacts.get_contact_with_highest_priority(self.account,
|
||||
self.contact.jid)
|
||||
contact = gajim.contacts.get_contact_with_highest_priority(
|
||||
self.account, self.contact.jid)
|
||||
if not contact or self.resource:
|
||||
# For transient contacts
|
||||
contact = self.contact
|
||||
|
@ -1334,10 +1485,12 @@ class ChatControl(ChatControlBase):
|
|||
# Disconnect timer callbacks
|
||||
gobject.source_remove(self.possible_paused_timeout_id)
|
||||
gobject.source_remove(self.possible_inactive_timeout_id)
|
||||
# Clean up systray
|
||||
if gajim.interface.systray_enabled and self.nb_unread > 0:
|
||||
gajim.interface.systray.remove_jid(self.contact.jid, self.account,
|
||||
self.type_id)
|
||||
# Remove bigger avatar window
|
||||
if self.bigger_avatar_window:
|
||||
self.bigger_avatar_window.destroy()
|
||||
# Clean events
|
||||
gajim.events.remove_events(self.account, self.get_full_jid(),
|
||||
types = ['printed_' + self.type_id, self.type_id])
|
||||
# remove all register handlers on wigets, created by self.xml
|
||||
# to prevent circular references among objects
|
||||
for i in self.handlers.keys():
|
||||
|
@ -1354,7 +1507,7 @@ class ChatControl(ChatControlBase):
|
|||
# 2 seconds
|
||||
dialog = dialogs.ConfirmationDialog(
|
||||
#%s is being replaced in the code with JID
|
||||
_('You just received a new message from "%s"' % self.contact.jid),
|
||||
_('You just received a new message from "%s"') % self.contact.jid,
|
||||
_('If you close this tab and you have history disabled, '\
|
||||
'this message will be lost.'))
|
||||
if dialog.get_response() != gtk.RESPONSE_OK:
|
||||
|
@ -1439,17 +1592,14 @@ class ChatControl(ChatControlBase):
|
|||
if restore_how_many <= 0:
|
||||
return
|
||||
timeout = gajim.config.get('restore_timeout') # in minutes
|
||||
# number of messages that are in queue and are already logged
|
||||
pending_how_many = 0 # we want to avoid duplication
|
||||
|
||||
if gajim.awaiting_events[self.account].has_key(jid):
|
||||
events = gajim.awaiting_events[self.account][jid]
|
||||
for event in events:
|
||||
if event[0] == 'chat':
|
||||
pending_how_many += 1
|
||||
events = gajim.events.get_events(self.account, jid, ['chat'])
|
||||
# number of messages that are in queue and are already logged, we want
|
||||
# to avoid duplication
|
||||
pending_how_many = len(events)
|
||||
|
||||
rows = gajim.logger.get_last_conversation_lines(jid, restore_how_many,
|
||||
pending_how_many, timeout)
|
||||
pending_how_many, timeout, self.account)
|
||||
local_old_kind = None
|
||||
for row in rows: # row[0] time, row[1] has kind, row[2] the message
|
||||
if not row[2]: # message is empty, we don't print it
|
||||
|
@ -1465,10 +1615,14 @@ class ChatControl(ChatControlBase):
|
|||
|
||||
tim = time.localtime(float(row[0]))
|
||||
|
||||
if gajim.config.get('restored_messages_small'):
|
||||
small_attr = ['small']
|
||||
else:
|
||||
small_attr = []
|
||||
ChatControlBase.print_conversation_line(self, row[2], kind, name, tim,
|
||||
['small'],
|
||||
['small', 'restored_message'],
|
||||
['small', 'restored_message'],
|
||||
small_attr,
|
||||
small_attr + ['restored_message'],
|
||||
small_attr + ['restored_message'],
|
||||
False, old_kind = local_old_kind)
|
||||
if row[2].startswith('/me ') or row[2].startswith('/me\n'):
|
||||
local_old_kind = None
|
||||
|
@ -1483,7 +1637,7 @@ class ChatControl(ChatControlBase):
|
|||
jid_with_resource = jid
|
||||
if self.resource:
|
||||
jid_with_resource += '/' + self.resource
|
||||
l = gajim.awaiting_events[self.account][jid_with_resource]
|
||||
events = gajim.events.get_events(self.account, jid_with_resource)
|
||||
|
||||
# Is it a pm ?
|
||||
is_pm = False
|
||||
|
@ -1491,15 +1645,12 @@ class ChatControl(ChatControlBase):
|
|||
control = gajim.interface.msg_win_mgr.get_control(room_jid, self.account)
|
||||
if control and control.type_id == message_control.TYPE_GC:
|
||||
is_pm = True
|
||||
events_to_keep = []
|
||||
# list of message ids which should be marked as read
|
||||
message_ids = []
|
||||
for event in l:
|
||||
typ = event[0]
|
||||
if typ != 'chat':
|
||||
events_to_keep.append(event)
|
||||
for event in events:
|
||||
if event.type_ != self.type_id:
|
||||
continue
|
||||
data = event[1]
|
||||
data = event.parameters
|
||||
kind = data[2]
|
||||
if kind == 'error':
|
||||
kind = 'info'
|
||||
|
@ -1509,33 +1660,31 @@ class ChatControl(ChatControlBase):
|
|||
encrypted = data[4], subject = data[1])
|
||||
if len(data) > 6 and isinstance(data[6], int):
|
||||
message_ids.append(data[6])
|
||||
# remove from gc nb_unread if it's pm or from roster
|
||||
if is_pm:
|
||||
control.nb_unread -= 1
|
||||
else:
|
||||
gajim.interface.roster.nb_unread -= 1
|
||||
if message_ids:
|
||||
gajim.logger.set_read_messages(message_ids)
|
||||
if is_pm:
|
||||
control.parent_win.show_title()
|
||||
else:
|
||||
gajim.interface.roster.show_title()
|
||||
# Keep only non-messages events
|
||||
if len(events_to_keep):
|
||||
gajim.awaiting_events[self.account][jid_with_resource] = events_to_keep
|
||||
else:
|
||||
del gajim.awaiting_events[self.account][jid_with_resource]
|
||||
gajim.events.remove_events(self.account, jid_with_resource,
|
||||
types = [self.type_id])
|
||||
|
||||
self.parent_win.show_title()
|
||||
self.parent_win.redraw_tab(self)
|
||||
# redraw roster
|
||||
gajim.interface.roster.show_title()
|
||||
|
||||
typ = 'chat' # Is it a normal chat or a pm ?
|
||||
# reset to status image in gc if it is a pm
|
||||
if is_pm:
|
||||
control.update_ui()
|
||||
typ = 'pm'
|
||||
|
||||
gajim.interface.roster.draw_contact(jid, self.account)
|
||||
if is_pm:
|
||||
room_jid, nick = gajim.get_room_and_nick_from_fjid(jid)
|
||||
groupchat_control = gajim.interface.msg_win_mgr.get_control(
|
||||
room_jid, self.account)
|
||||
groupchat_control.draw_contact(nick)
|
||||
else:
|
||||
gajim.interface.roster.draw_contact(jid, self.account)
|
||||
# Redraw parent too
|
||||
gajim.interface.roster.draw_parent_contact(jid, self.account)
|
||||
if gajim.interface.systray_enabled:
|
||||
gajim.interface.systray.remove_jid(jid_with_resource, self.account, typ)
|
||||
if (self.contact.show == 'offline' or self.contact.show == 'error'):
|
||||
showOffline = gajim.config.get('showoffline')
|
||||
if not showOffline and typ == 'chat' and \
|
||||
|
@ -1548,6 +1697,9 @@ class ChatControl(ChatControlBase):
|
|||
def show_bigger_avatar(self, small_avatar):
|
||||
'''resizes the avatar, if needed, so it has at max half the screen size
|
||||
and shows it'''
|
||||
if not small_avatar.window:
|
||||
# Tab has been closed since we hovered the avatar
|
||||
return
|
||||
is_fake = False
|
||||
if self.type_id == message_control.TYPE_PM:
|
||||
is_fake = True
|
||||
|
@ -1562,7 +1714,7 @@ class ChatControl(ChatControlBase):
|
|||
# It's why I set it transparent.
|
||||
image = self.xml.get_widget('avatar_image')
|
||||
pixbuf = image.get_pixbuf()
|
||||
pixbuf.fill(0xffffff00) # RGBA
|
||||
pixbuf.fill(0xffffff00L) # RGBA
|
||||
image.queue_draw()
|
||||
|
||||
screen_w = gtk.gdk.screen_width()
|
||||
|
@ -1612,6 +1764,7 @@ class ChatControl(ChatControlBase):
|
|||
def _on_window_avatar_leave_notify_event(self, widget, event):
|
||||
'''we just left the popup window that holds avatar'''
|
||||
self.bigger_avatar_window.destroy()
|
||||
self.bigger_avatar_window = None
|
||||
# Re-show the small avatar
|
||||
self.show_avatar()
|
||||
|
||||
|
|
|
@ -138,7 +138,7 @@ else:
|
|||
def verify(self, str, sign):
|
||||
if not USE_GPG:
|
||||
return str
|
||||
if not str:
|
||||
if str == None:
|
||||
return ''
|
||||
f = tmpfile()
|
||||
fd = f.fileno()
|
||||
|
|
|
@ -6,19 +6,19 @@ HAVE_XSCRNSAVER = $(shell pkg-config --exists xscrnsaver && echo 'YES')
|
|||
|
||||
ifeq ($(HAVE_XSCRNSAVER),YES)
|
||||
# We link with libXScrnsaver from modular X.Org X11
|
||||
CFLAGS = `pkg-config --cflags gtk+-2.0 pygtk-2.0 xscrnsaver` -fpic -I/usr/include/python$(PYTHONVER) -I.
|
||||
LDFLAGS = `pkg-config --libs gtk+-2.0 pygtk-2.0 xscrnsaver` -lpython$(PYTHONVER)
|
||||
gtk_and_x_CFLAGS = `pkg-config --cflags gtk+-2.0 pygtk-2.0 xscrnsaver` -fpic -I/usr/include/python$(PYTHONVER) -I.
|
||||
gtk_and_x_LDFLAGS = `pkg-config --libs gtk+-2.0 pygtk-2.0 xscrnsaver` -lpython$(PYTHONVER)
|
||||
else
|
||||
# # We link with libXScrnsaver from monolithic X.Org X11
|
||||
CFLAGS = `pkg-config --cflags gtk+-2.0 pygtk-2.0` -fpic -I/usr/include/python$(PYTHONVER) -I.
|
||||
LDFLAGS = `pkg-config --libs gtk+-2.0 pygtk-2.0` -L/usr/X11R6$(LIBDIR) -lX11 \
|
||||
-lXss -lXext -lpython$(PYTHONVER)
|
||||
gtk_and_x_CFLAGS = `pkg-config --cflags gtk+-2.0 pygtk-2.0` -fpic -I/usr/include/python$(PYTHONVER) -I.
|
||||
gtk_and_x_LDFLAGS = `pkg-config --libs gtk+-2.0 pygtk-2.0` \
|
||||
-L/usr/X11R6$(LIBDIR) -lX11 -lXss -lXext -lpython$(PYTHONVER)
|
||||
endif
|
||||
|
||||
all: idle.so
|
||||
|
||||
idle.so:
|
||||
$(CC) $(OPTFLAGS) $(CFLAGS) $(LDFLAGS) -shared idle.c $^ -o $@
|
||||
$(CC) $(OPTFLAGS) $(CFLAGS) $(LDFLAGS) $(gtk_and_x_CFLAGS) $(gtk_and_x_LDFLAGS) -shared idle.c $^ -o $@
|
||||
|
||||
clean:
|
||||
rm -f *.so
|
||||
|
|
|
@ -28,10 +28,6 @@ import stat
|
|||
|
||||
from common import gajim
|
||||
import logger
|
||||
import i18n
|
||||
|
||||
_ = i18n._
|
||||
Q_ = i18n.Q_
|
||||
|
||||
from pysqlite2 import dbapi2 as sqlite # DO NOT MOVE ABOVE OF import gajim
|
||||
|
||||
|
@ -61,6 +57,11 @@ def create_log_db():
|
|||
jid_id INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE transports_cache (
|
||||
transport TEXT UNIQUE,
|
||||
type INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE logs(
|
||||
log_line_id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
|
||||
jid_id INTEGER,
|
||||
|
|
|
@ -20,8 +20,6 @@
|
|||
|
||||
import sre
|
||||
import copy
|
||||
import i18n
|
||||
_ = i18n._
|
||||
|
||||
|
||||
(
|
||||
|
@ -50,7 +48,7 @@ class Config:
|
|||
'notify_on_signout': [ opt_bool, False ],
|
||||
'notify_on_new_message': [ opt_bool, True ],
|
||||
'autopopupaway': [ opt_bool, False ],
|
||||
'use_notif_daemon': [ opt_bool, True , _('Use DBus and Notification-Daemon to show notifications') ],
|
||||
'use_notif_daemon': [ opt_bool, True , _('Use D-Bus and Notification-Daemon to show notifications') ],
|
||||
'ignore_unknown_contacts': [ opt_bool, False ],
|
||||
'showoffline': [ opt_bool, False, '', True ],
|
||||
'autoaway': [ opt_bool, True ],
|
||||
|
@ -76,17 +74,19 @@ class Config:
|
|||
'statusmsgcolor': [ opt_color, '#1eaa1e', '', True ],
|
||||
'markedmsgcolor': [ opt_color, '#ff8080', '', True ],
|
||||
'urlmsgcolor': [ opt_color, '#0000ff', '', True ],
|
||||
'collapsed_rows': [ opt_str, '', _('List (space separated) of rows (accounts and groups) that are collapsed'), True ],
|
||||
'collapsed_rows': [ opt_str, '', _('List (space separated) of rows (accounts and groups) that are collapsed.'), True ],
|
||||
'roster_theme': [ opt_str, 'gtk+', '', True ],
|
||||
'saveposition': [ opt_bool, True ],
|
||||
'mergeaccounts': [ opt_bool, False, '', True ],
|
||||
'sort_by_show': [ opt_bool, True, '', True ],
|
||||
'use_speller': [ opt_bool, False, ],
|
||||
'speller_language': [ opt_str, '', _('Language used by speller')],
|
||||
'print_time': [ opt_str, 'always', _('\'always\' - print time for every message.\n\'sometimes\' - print time every print_ichat_every_foo_minutes minute.\n\'never\' - never print time.')],
|
||||
'print_time_fuzzy': [ opt_int, 0, _('Value of fuzziness from 1 to 4 or 0 to disable fuzzyclock. 1 is the most precise clock, 4 the less precise one.') ],
|
||||
'emoticons_theme': [opt_str, 'static', '', True ],
|
||||
'ascii_formatting': [ opt_bool, True,
|
||||
_('Treat * / _ pairs as possible formatting characters.'), True],
|
||||
'show_ascii_formatting_chars': [ opt_bool, False , _('If True, do not '
|
||||
'show_ascii_formatting_chars': [ opt_bool, True , _('If True, do not '
|
||||
'remove */_ . So *abc* will be bold but with * * not removed.')],
|
||||
'sounds_on': [ opt_bool, True ],
|
||||
# 'aplay', 'play', 'esdplay', 'artsplay' detected first time only
|
||||
|
@ -95,12 +95,12 @@ class Config:
|
|||
'custombrowser': [ opt_str, 'firefox' ],
|
||||
'custommailapp': [ opt_str, 'mozilla-thunderbird -compose' ],
|
||||
'custom_file_manager': [ opt_str, 'xffm' ],
|
||||
'gc-hpaned-position': [opt_int, 540],
|
||||
'gc_refer_to_nick_char': [opt_str, ',', _('Character to add after nickname when using nick completion (tab) in group chat')],
|
||||
'gc_proposed_nick_char': [opt_str, '_', _('Character to propose to add after desired nickname when desired nickname is used by someone else in group chat')],
|
||||
'gc-hpaned-position': [opt_int, 430],
|
||||
'gc_refer_to_nick_char': [opt_str, ',', _('Character to add after nickname when using nick completion (tab) in group chat.')],
|
||||
'gc_proposed_nick_char': [opt_str, '_', _('Character to propose to add after desired nickname when desired nickname is used by someone else in group chat.')],
|
||||
'msgwin-x-position': [opt_int, -1], # Default is to let the window manager decide
|
||||
'msgwin-y-position': [opt_int, -1], # Default is to let the window manager decide
|
||||
'msgwin-width': [opt_int, 480],
|
||||
'msgwin-width': [opt_int, 500],
|
||||
'msgwin-height': [opt_int, 440],
|
||||
'chat-msgwin-x-position': [opt_int, -1], # Default is to let the window manager decide
|
||||
'chat-msgwin-y-position': [opt_int, -1], # Default is to let the window manager decide
|
||||
|
@ -108,7 +108,7 @@ class Config:
|
|||
'chat-msgwin-height': [opt_int, 440],
|
||||
'gc-msgwin-x-position': [opt_int, -1], # Default is to let the window manager decide
|
||||
'gc-msgwin-y-position': [opt_int, -1], # Default is to let the window manager decide
|
||||
'gc-msgwin-width': [opt_int, 480],
|
||||
'gc-msgwin-width': [opt_int, 600],
|
||||
'gc-msgwin-height': [opt_int, 440],
|
||||
'single-msg-x-position': [opt_int, 0],
|
||||
'single-msg-y-position': [opt_int, 0],
|
||||
|
@ -126,6 +126,7 @@ class Config:
|
|||
'after_nickname': [ opt_str, ':' ],
|
||||
'send_os_info': [ opt_bool, True ],
|
||||
'notify_on_new_gmail_email': [ opt_bool, True ],
|
||||
'notify_on_new_gmail_email_extra': [ opt_bool, False ],
|
||||
'usegpg': [ opt_bool, False, '', True ],
|
||||
'use_gpg_agent': [ opt_bool, False ],
|
||||
'change_roster_title': [ opt_bool, True, _('Add * and [n] in roster title?')],
|
||||
|
@ -134,7 +135,7 @@ class Config:
|
|||
'send_on_ctrl_enter': [opt_bool, False, _('Send message on Ctrl+Enter and with Enter make new line (Mirabilis ICQ Client default behaviour).')],
|
||||
'show_roster_on_startup': [opt_bool, True],
|
||||
'key_up_lines': [opt_int, 25, _('How many lines to store for Ctrl+KeyUP.')],
|
||||
'version': [ opt_str, '0.10.0.1' ], # which version created the config
|
||||
'version': [ opt_str, '0.10.1.3' ], # which version created the config
|
||||
'search_engine': [opt_str, 'http://www.google.com/search?&q=%s&sourceid=gajim'],
|
||||
'dictionary_url': [opt_str, 'WIKTIONARY', _("Either custom url with %s in it where %s is the word/phrase or 'WIKTIONARY' which means use wiktionary.")],
|
||||
'always_english_wikipedia': [opt_bool, False],
|
||||
|
@ -142,7 +143,7 @@ class Config:
|
|||
'remote_control': [opt_bool, True, _('If checked, Gajim can be controlled remotely using gajim-remote.'), True],
|
||||
'chat_state_notifications': [opt_str, 'all'], # 'all', 'composing_only', 'disabled'
|
||||
'autodetect_browser_mailer': [opt_bool, False, '', True],
|
||||
'print_ichat_every_foo_minutes': [opt_int, 5, _('When not printing time for every message (print_time==sometimes), print it every x minutes')],
|
||||
'print_ichat_every_foo_minutes': [opt_int, 5, _('When not printing time for every message (print_time==sometimes), print it every x minutes.')],
|
||||
'confirm_close_muc': [opt_bool, True, _('Ask before closing a group chat tab/window.')],
|
||||
'confirm_close_muc_rooms': [opt_str, '', _('Always ask before closing group chat tab/window in this space separated list of room jids.')],
|
||||
'noconfirm_close_muc_rooms': [opt_str, '', _('Never ask before closing group chat tab/window in this space separated list of room jids.')],
|
||||
|
@ -177,29 +178,33 @@ class Config:
|
|||
'quit_on_roster_x_button': [opt_bool, False, _('If True, quits Gajim when X button of Window Manager is clicked. This setting is taken into account only if trayicon is used.')],
|
||||
'set_xmpp://_handler_everytime': [opt_bool, False, _('If True, Gajim registers for xmpp:// on each startup.')],
|
||||
'show_unread_tab_icon': [opt_bool, False, _('If True, Gajim will display an icon on each tab containing unread messages. Depending on the theme, this icon may be animated.')],
|
||||
'show_status_msgs_in_roster': [opt_bool, True, _('If True, Gajim will display the status message, if not empty, for every contact under the contact name in roster window'), True],
|
||||
'show_status_msgs_in_roster': [opt_bool, True, _('If True, Gajim will display the status message, if not empty, for every contact under the contact name in roster window.'), True],
|
||||
'show_avatars_in_roster': [opt_bool, True, '', True],
|
||||
'ask_avatars_on_startup': [opt_bool, True, _('If True, Gajim will ask for avatar each contact that did not have an avatar last time or has one cached that is too old.')],
|
||||
'print_status_in_chats': [opt_bool, True, _('If False, Gajim will no longer print status line in chats when a contact changes his or her status and/or his or her status message.')],
|
||||
'print_status_in_muc': [opt_str, 'in_and_out', _('can be "none", "all" or "in_and_out". If "none", Gajim will no longer print status line in groupchats when a member changes his or her status and/or his or her status message. If "all" Gajim will print all status messages. If "in_and_out", gajim will only print FOO enters/leaves room')],
|
||||
'print_status_in_muc': [opt_str, 'in_and_out', _('can be "none", "all" or "in_and_out". If "none", Gajim will no longer print status line in groupchats when a member changes his or her status and/or his or her status message. If "all" Gajim will print all status messages. If "in_and_out", gajim will only print FOO enters/leaves room.')],
|
||||
'log_contact_status_changes': [opt_bool, False],
|
||||
'restored_messages_color': [opt_str, 'grey'],
|
||||
'restored_messages_small': [opt_bool, True, _('If True, restored messages will use a smaller font than the default one.')],
|
||||
'hide_avatar_of_transport': [opt_bool, False, _('Don\'t show avatar for the transport itself.')],
|
||||
'roster_window_skip_taskbar': [opt_bool, False],
|
||||
'use_urgency_hint': [opt_bool, True, _('If True and installed GTK+ and PyGTK versions are at least 2.8, make the window flash (the default behaviour in most Window Managers) when holding pending events.')],
|
||||
'notification_timeout': [opt_int, 5],
|
||||
'send_sha_in_gc_presence': [opt_bool, True, _('Jabberd1.4 does not like sha info when one join a password protected room. Turn this option to False to stop sending sha info in groupchat presences')],
|
||||
'send_sha_in_gc_presence': [opt_bool, True, _('Jabberd1.4 does not like sha info when one join a password protected room. Turn this option to False to stop sending sha info in group chat presences.')],
|
||||
'one_message_window': [opt_str, 'always',
|
||||
_('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')],
|
||||
'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')],
|
||||
'always_hide_groupchat_buttons': [opt_bool, False, _('Hides the buttons in group chat window')],
|
||||
'always_hide_chat_buttons': [opt_bool, False, _('Hides the buttons in two persons chat window')],
|
||||
#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.')],
|
||||
'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.')],
|
||||
'always_hide_groupchat_buttons': [opt_bool, False, _('Hides the buttons in group chat window.')],
|
||||
'always_hide_chat_buttons': [opt_bool, False, _('Hides the buttons in two persons chat window.')],
|
||||
'hide_groupchat_banner': [opt_bool, False, _('Hides the banner in a group chat window')],
|
||||
'hide_chat_banner': [opt_bool, False, _('Hides the banner in two persons chat window')],
|
||||
'hide_groupchat_occupants_list': [opt_bool, False, _('Hides the room occupants list in groupchat window')],
|
||||
'chat_merge_consecutive_nickname': [opt_bool, False, _('Merge consecutive nickname in chat window')],
|
||||
'chat_merge_consecutive_nickname_indent': [opt_str, ' ', _('Indentation when using merge consecutive nickame')],
|
||||
'hide_groupchat_occupants_list': [opt_bool, False, _('Hides the room occupants list in group chat window.')],
|
||||
'chat_merge_consecutive_nickname': [opt_bool, False, _('Merge consecutive nickname in chat window.')],
|
||||
'chat_merge_consecutive_nickname_indent': [opt_str, ' ', _('Indentation when using merge consecutive nickame.')],
|
||||
'gc_nicknames_colors': [ opt_str, '#a34526:#c000ff:#0012ff:#388a99:#38995d:#519938:#ff8a00:#94452d:#244b5a:#32645a', _('List of colors that will be used to color nicknames in group chats.'), True ],
|
||||
'ctrl_tab_go_to_next_composing': [opt_bool, True, _('Ctrl-Tab go to next composing tab when none is unread.')],
|
||||
'zeroconf_enabled': [opt_bool, True, _('Enable zeroconf network')],
|
||||
}
|
||||
|
||||
|
@ -246,6 +251,10 @@ class Config:
|
|||
'statusmsg': ({
|
||||
'message': [ opt_str, '' ],
|
||||
}, {}),
|
||||
'defaultstatusmsg': ({
|
||||
'enabled': [ opt_bool, False ],
|
||||
'message': [ opt_str, '' ],
|
||||
}, {}),
|
||||
'soundevents': ({
|
||||
'enabled': [ opt_bool, True ],
|
||||
'path': [ opt_str, '' ],
|
||||
|
@ -288,7 +297,27 @@ class Config:
|
|||
'state_muc_directed_msg_color': [ opt_color, 'red2' ],
|
||||
}, {}),
|
||||
'contacts': ({
|
||||
'gpg_enabled': [ opt_bool, True ],
|
||||
'gpg_enabled': [ opt_bool, True, _('Is OpenPGP enabled for this contact?')],
|
||||
'speller_language': [ opt_str, '', _('Language for which we want to check misspelled words')],
|
||||
}, {}),
|
||||
'rooms': ({
|
||||
'speller_language': [ opt_str, '', _('Language for which we want to check misspelled words')],
|
||||
}, {}),
|
||||
'notifications': ({
|
||||
'event': [opt_str, ''],
|
||||
'recipient_type': [opt_str, 'all'],
|
||||
'recipients': [opt_str, ''],
|
||||
'status': [opt_str, 'all', _('all or space separated status')],
|
||||
'tab_opened': [opt_str, 'both', _("'yes', 'no', or 'both'")],
|
||||
'sound': [opt_str, '', _("'yes', 'no' or ''")],
|
||||
'sound_file': [opt_str, ''],
|
||||
'popup': [opt_str, '', _("'yes', 'no' or ''")],
|
||||
'auto_open': [opt_str, '', _("'yes', 'no' or ''")],
|
||||
'run_command': [opt_bool, False],
|
||||
'command': [opt_str, ''],
|
||||
'systray': [opt_str, '', _("'yes', 'no' or ''")],
|
||||
'roster': [opt_str, '', _("'yes', 'no' or ''")],
|
||||
'urgency_hint': [opt_bool, False],
|
||||
}, {}),
|
||||
}
|
||||
|
||||
|
@ -302,6 +331,16 @@ class Config:
|
|||
_('Out'): _("I'm out enjoying life"),
|
||||
}
|
||||
|
||||
defaultstatusmsg_default = {
|
||||
'online': [ False, _("I'm available") ],
|
||||
'chat': [ False, _("I'm free for chat") ],
|
||||
'away': [ False, _('Be right back') ],
|
||||
'xa': [ False, _("I'm not available") ],
|
||||
'dnd': [ False, _('Do not disturb') ],
|
||||
'invisible': [ False, _('Bye!') ],
|
||||
'offline': [ False, _('Bye!') ],
|
||||
}
|
||||
|
||||
soundevents_default = {
|
||||
'first_message_received': [ True, '../data/sounds/message1.wav' ],
|
||||
'next_message_received': [ True, '../data/sounds/message2.wav' ],
|
||||
|
@ -515,3 +554,8 @@ class Config:
|
|||
self.set_per('soundevents', event, 'enabled', default[0])
|
||||
self.set_per('soundevents', event, 'path', default[1])
|
||||
|
||||
for status in self.defaultstatusmsg_default:
|
||||
default = self.defaultstatusmsg_default[status]
|
||||
self.add_per('defaultstatusmsg', status)
|
||||
self.set_per('defaultstatusmsg', status, 'enabled', default[0])
|
||||
self.set_per('defaultstatusmsg', status, 'message', default[1])
|
||||
|
|
|
@ -42,9 +42,6 @@ from common import GnuPG
|
|||
from connection_handlers import *
|
||||
USE_GPG = GnuPG.USE_GPG
|
||||
|
||||
from common import i18n
|
||||
_ = i18n._
|
||||
|
||||
class Connection(ConnectionHandlers):
|
||||
'''Connection class'''
|
||||
def __init__(self, name):
|
||||
|
@ -88,8 +85,12 @@ class Connection(ConnectionHandlers):
|
|||
self.on_connect_failure = None
|
||||
self.retrycount = 0
|
||||
self.jids_for_auto_auth = [] # list of jid to auto-authorize
|
||||
|
||||
self.muc_jid = {} # jid of muc server for each transport type
|
||||
self.available_transports = {} # list of available transports on this
|
||||
# server {'icq': ['icq.server.com', 'icq2.server.com'], }
|
||||
self.vcard_supported = True
|
||||
# END __init__
|
||||
|
||||
def put_event(self, ev):
|
||||
if gajim.handlers.has_key(ev[0]):
|
||||
gajim.handlers[ev[0]](self.name, ev[1])
|
||||
|
@ -137,22 +138,21 @@ class Connection(ConnectionHandlers):
|
|||
if not self.on_purpose:
|
||||
self.disconnect()
|
||||
if gajim.config.get_per('accounts', self.name, 'autoreconnect') \
|
||||
and self.retrycount <= 10:
|
||||
and self.retrycount <= 10:
|
||||
self.connected = 1
|
||||
self.dispatch('STATUS', 'connecting')
|
||||
self.time_to_reconnect = 10
|
||||
# this check has moved from _reconnect method
|
||||
if self.retrycount > 5:
|
||||
self.time_to_reconnect = 20
|
||||
else:
|
||||
self.time_to_reconnect = 10
|
||||
gajim.idlequeue.set_alarm(self._reconnect_alarm,
|
||||
self.time_to_reconnect)
|
||||
gajim.idlequeue.set_alarm(self._reconnect_alarm,
|
||||
self.time_to_reconnect)
|
||||
elif self.on_connect_failure:
|
||||
self.on_connect_failure()
|
||||
self.on_connect_failure = None
|
||||
else:
|
||||
# show error dialog
|
||||
# show error dialog
|
||||
self._connection_lost()
|
||||
else:
|
||||
self.disconnect()
|
||||
|
@ -162,9 +162,9 @@ class Connection(ConnectionHandlers):
|
|||
def _connection_lost(self):
|
||||
self.disconnect(on_purpose = False)
|
||||
self.dispatch('STATUS', 'offline')
|
||||
self.dispatch('ERROR',
|
||||
(_('Connection with account "%s" has been lost') % self.name,
|
||||
_('To continue sending and receiving messages, you will need to reconnect.')))
|
||||
self.dispatch('CONNECTION_LOST',
|
||||
(_('Connection with account "%s" has been lost') % self.name,
|
||||
_('To continue sending and receiving messages, you will need to reconnect.')))
|
||||
|
||||
def _event_dispatcher(self, realm, event, data):
|
||||
if realm == common.xmpp.NS_REGISTER:
|
||||
|
@ -211,6 +211,34 @@ class Connection(ConnectionHandlers):
|
|||
else:
|
||||
conf = data[1].asDict()
|
||||
self.dispatch('REGISTER_AGENT_INFO', (data[0], conf, is_form))
|
||||
elif realm == common.xmpp.NS_PRIVACY:
|
||||
if event == common.xmpp.features_nb.PRIVACY_LISTS_RECEIVED:
|
||||
# data is (list)
|
||||
self.dispatch('PRIVACY_LISTS_RECEIVED', (data))
|
||||
elif event == common.xmpp.features_nb.PRIVACY_LIST_RECEIVED:
|
||||
# data is (resp)
|
||||
if not data:
|
||||
return
|
||||
rules = []
|
||||
name = data.getTag('query').getTag('list').getAttr('name')
|
||||
for child in data.getTag('query').getTag('list').getChildren():
|
||||
dict_item = child.getAttrs()
|
||||
childs = []
|
||||
if dict_item.has_key('type'):
|
||||
for scnd_child in child.getChildren():
|
||||
childs += [scnd_child.getName()]
|
||||
rules.append({'action':dict_item['action'],
|
||||
'type':dict_item['type'], 'order':dict_item['order'],
|
||||
'value':dict_item['value'], 'child':childs})
|
||||
else:
|
||||
for scnd_child in child.getChildren():
|
||||
childs.append(scnd_child.getName())
|
||||
rules.append({'action':dict_item['action'],
|
||||
'order':dict_item['order'], 'child':childs})
|
||||
self.dispatch('PRIVACY_LIST_RECEIVED', (name, rules))
|
||||
elif event == common.xmpp.features_nb.PRIVACY_LISTS_ACTIVE_DEFAULT:
|
||||
# data is (dict)
|
||||
self.dispatch('PRIVACY_LISTS_ACTIVE_DEFAULT', (data))
|
||||
elif realm == '':
|
||||
if event == common.xmpp.transports.DATA_RECEIVED:
|
||||
self.dispatch('STANZA_ARRIVED', unicode(data, errors = 'ignore'))
|
||||
|
@ -360,7 +388,8 @@ class Connection(ConnectionHandlers):
|
|||
if not self.retrycount and self.connected != 0:
|
||||
self.disconnect(on_purpose = True)
|
||||
self.dispatch('STATUS', 'offline')
|
||||
self.dispatch('ERROR', (_('Could not connect to "%s"') % self._hostname,
|
||||
self.dispatch('CONNECTION_LOST',
|
||||
(_('Could not connect to "%s"') % self._hostname,
|
||||
_('Check your connection or try again later.')))
|
||||
|
||||
def _connect_success(self, con, con_type):
|
||||
|
@ -396,7 +425,8 @@ class Connection(ConnectionHandlers):
|
|||
if not con:
|
||||
self.disconnect(on_purpose = True)
|
||||
self.dispatch('STATUS', 'offline')
|
||||
self.dispatch('ERROR', (_('Could not connect to "%s"') % self._hostname,
|
||||
self.dispatch('CONNECTION_LOST',
|
||||
(_('Could not connect to "%s"') % self._hostname,
|
||||
_('Check your connection or try again later')))
|
||||
if self.on_connect_auth:
|
||||
self.on_connect_auth(None)
|
||||
|
@ -433,6 +463,41 @@ class Connection(ConnectionHandlers):
|
|||
if kill_core and self.connected > 1:
|
||||
self.disconnect(on_purpose = True)
|
||||
|
||||
def get_privacy_lists(self):
|
||||
if not self.connection:
|
||||
return
|
||||
common.xmpp.features_nb.getPrivacyLists(self.connection)
|
||||
|
||||
def get_active_default_lists(self):
|
||||
if not self.connection:
|
||||
return
|
||||
common.xmpp.features_nb.getActiveAndDefaultPrivacyLists(self.connection)
|
||||
|
||||
def del_privacy_list(self, privacy_list):
|
||||
if not self.connection:
|
||||
return
|
||||
common.xmpp.features_nb.delPrivacyList(self.connection, privacy_list)
|
||||
|
||||
def get_privacy_list(self, title):
|
||||
if not self.connection:
|
||||
return
|
||||
common.xmpp.features_nb.getPrivacyList(self.connection, title)
|
||||
|
||||
def set_privacy_list(self, listname, tags):
|
||||
if not self.connection:
|
||||
return
|
||||
common.xmpp.features_nb.setPrivacyList(self.connection, listname, tags)
|
||||
|
||||
def set_active_list(self, listname):
|
||||
if not self.connection:
|
||||
return
|
||||
common.xmpp.features_nb.setActivePrivacyList(self.connection, listname, 'active')
|
||||
|
||||
def set_default_list(self, listname):
|
||||
if not self.connection:
|
||||
return
|
||||
common.xmpp.features_nb.setDefaultPrivacyList(self.connection, listname)
|
||||
|
||||
def build_privacy_rule(self, name, action):
|
||||
'''Build a Privacy rule stanza for invisibility'''
|
||||
iq = common.xmpp.Iq('set', common.xmpp.NS_PRIVACY, xmlns = '')
|
||||
|
@ -442,6 +507,8 @@ class Connection(ConnectionHandlers):
|
|||
return iq
|
||||
|
||||
def activate_privacy_rule(self, name):
|
||||
if not self.connection:
|
||||
return
|
||||
'''activate a privacy rule'''
|
||||
iq = common.xmpp.Iq('set', common.xmpp.NS_PRIVACY, xmlns = '')
|
||||
iq.getTag('query').setTag('active', {'name': name})
|
||||
|
@ -528,7 +595,7 @@ class Connection(ConnectionHandlers):
|
|||
# Ask metacontacts before roster
|
||||
self.get_metacontacts()
|
||||
|
||||
def change_status(self, show, msg, sync = False, auto = False):
|
||||
def change_status(self, show, msg, auto = False):
|
||||
if not show in STATUS_LIST:
|
||||
return -1
|
||||
sshow = helpers.get_xmpp_show(show)
|
||||
|
@ -607,7 +674,8 @@ class Connection(ConnectionHandlers):
|
|||
self.connection.send(msg_iq)
|
||||
|
||||
def send_message(self, jid, msg, keyID, type = 'chat', subject='',
|
||||
chatstate = None, msg_id = None, composing_jep = None, resource = None):
|
||||
chatstate = None, msg_id = None, composing_jep = None, resource = None,
|
||||
user_nick = None):
|
||||
if not self.connection:
|
||||
return
|
||||
if not msg and chatstate is None:
|
||||
|
@ -638,6 +706,11 @@ class Connection(ConnectionHandlers):
|
|||
if msgenc:
|
||||
msg_iq.setTag(common.xmpp.NS_ENCRYPTED + ' x').setData(msgenc)
|
||||
|
||||
# JEP-0172: user_nickname
|
||||
if user_nick:
|
||||
msg_iq.setTag('nick', namespace = common.xmpp.NS_NICK).setData(
|
||||
user_nick)
|
||||
|
||||
# chatstates - if peer supports jep85 or jep22, send chatstates
|
||||
# please note that the only valid tag inside a message containing a <body>
|
||||
# tag is the active event
|
||||
|
@ -692,7 +765,7 @@ class Connection(ConnectionHandlers):
|
|||
self.connection.send(p)
|
||||
|
||||
def request_subscription(self, jid, msg = '', name = '', groups = [],
|
||||
auto_auth = False):
|
||||
auto_auth = False, user_nick = ''):
|
||||
if not self.connection:
|
||||
return
|
||||
gajim.log.debug('subscription request for %s' % jid)
|
||||
|
@ -710,10 +783,11 @@ class Connection(ConnectionHandlers):
|
|||
self.connection.send(iq)
|
||||
|
||||
p = common.xmpp.Presence(jid, 'subscribe')
|
||||
if user_nick:
|
||||
p.setTag('nick', namespace = common.xmpp.NS_NICK).setData(user_nick)
|
||||
p = self.add_sha(p)
|
||||
if not msg:
|
||||
msg = _('I would like to add you to my roster.')
|
||||
p.setStatus(msg)
|
||||
if msg:
|
||||
p.setStatus(msg)
|
||||
self.connection.send(p)
|
||||
|
||||
def send_authorization(self, jid):
|
||||
|
@ -796,6 +870,10 @@ class Connection(ConnectionHandlers):
|
|||
def request_os_info(self, jid, resource):
|
||||
if not self.connection:
|
||||
return
|
||||
# If we are invisible, do not request
|
||||
if self.connected == gajim.SHOW_LIST.index('invisible'):
|
||||
self.dispatch('OS_INFO', (jid, resource, _('Not fetched because of invisible status'), _('Not fetched because of invisible status')))
|
||||
return
|
||||
to_whom_jid = jid
|
||||
if resource:
|
||||
to_whom_jid += '/' + resource
|
||||
|
@ -852,6 +930,9 @@ class Connection(ConnectionHandlers):
|
|||
iq = common.xmpp.Iq(typ='get')
|
||||
iq2 = iq.addChild(name='query', namespace='jabber:iq:private')
|
||||
iq2.addChild(name='storage', namespace='storage:metacontacts')
|
||||
id = self.connection.getAnID()
|
||||
iq.setID(id)
|
||||
self.awaiting_answers[id] = (METACONTACTS_ARRIVED, )
|
||||
self.connection.send(iq)
|
||||
|
||||
def store_metacontacts(self, tags_list):
|
||||
|
@ -873,7 +954,8 @@ class Connection(ConnectionHandlers):
|
|||
def send_agent_status(self, agent, ptype):
|
||||
if not self.connection:
|
||||
return
|
||||
p = common.xmpp.Presence(to = agent, typ = ptype)
|
||||
show = helpers.get_xmpp_show(STATUS_LIST[self.connected])
|
||||
p = common.xmpp.Presence(to = agent, typ = ptype, show = show)
|
||||
p = self.add_sha(p, ptype != 'unavailable')
|
||||
self.connection.send(p)
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ import sha
|
|||
import socket
|
||||
import sys
|
||||
|
||||
from time import localtime, strftime, gmtime
|
||||
from calendar import timegm
|
||||
|
||||
import socks5
|
||||
|
@ -32,8 +33,6 @@ import common.xmpp
|
|||
from common import GnuPG
|
||||
from common import helpers
|
||||
from common import gajim
|
||||
from common import i18n
|
||||
_ = i18n._
|
||||
|
||||
STATUS_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd',
|
||||
'invisible']
|
||||
|
@ -41,6 +40,7 @@ STATUS_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd',
|
|||
VCARD_PUBLISHED = 'vcard_published'
|
||||
VCARD_ARRIVED = 'vcard_arrived'
|
||||
AGENT_REMOVED = 'agent_removed'
|
||||
METACONTACTS_ARRIVED = 'metacontacts_arrived'
|
||||
HAS_IDLE = True
|
||||
try:
|
||||
import common.idle as idle # when we launch gajim from sources
|
||||
|
@ -90,7 +90,7 @@ class ConnectionBytestream:
|
|||
if contact.jid == receiver_jid:
|
||||
file_props['error'] = -5
|
||||
self.remove_transfer(file_props)
|
||||
self.dispatch('FILE_REQUEST_ERROR', (contact.jid, file_props))
|
||||
self.dispatch('FILE_REQUEST_ERROR', (contact.jid, file_props, ''))
|
||||
sender_jid = unicode(file_props['sender']).split('/')[0]
|
||||
if contact.jid == sender_jid:
|
||||
file_props['error'] = -3
|
||||
|
@ -169,12 +169,18 @@ class ConnectionBytestream:
|
|||
file_props['sha_str'] = sha_str
|
||||
if not ft_override_host_to_send:
|
||||
ft_override_host_to_send = self.peerhost[0]
|
||||
ft_override_host_to_send = socket.gethostbyname(ft_override_host_to_send)
|
||||
try:
|
||||
ft_override_host_to_send = socket.gethostbyname(
|
||||
ft_override_host_to_send)
|
||||
except socket.gaierror:
|
||||
self.dispatch('ERROR', (_('Wrong host'), _('The host you configured as the ft_override_host_to_send advanced option is not valid, so ignored.')))
|
||||
ft_override_host_to_send = self.peerhost[0]
|
||||
listener = gajim.socks5queue.start_listener(self.peerhost[0], port,
|
||||
sha_str, self._result_socks5_sid, file_props['sid'])
|
||||
if listener == None:
|
||||
file_props['error'] = -5
|
||||
self.dispatch('FILE_REQUEST_ERROR', (unicode(receiver), file_props))
|
||||
self.dispatch('FILE_REQUEST_ERROR', (unicode(receiver), file_props,
|
||||
''))
|
||||
self._connect_error(unicode(receiver), file_props['sid'],
|
||||
file_props['sid'], code = 406)
|
||||
return
|
||||
|
@ -216,8 +222,8 @@ class ConnectionBytestream:
|
|||
iq = common.xmpp.Protocol(name = 'iq',
|
||||
to = unicode(file_props['sender']), typ = 'error')
|
||||
iq.setAttr('id', file_props['request-id'])
|
||||
err = common.xmpp.ErrorNode(code = '406', typ = 'auth', name =
|
||||
'not-acceptable')
|
||||
err = common.xmpp.ErrorNode(code = '403', typ = 'cancel', name =
|
||||
'forbidden', text = 'Offer Declined')
|
||||
iq.addChild(node=err)
|
||||
self.connection.send(iq)
|
||||
|
||||
|
@ -309,8 +315,8 @@ class ConnectionBytestream:
|
|||
if file_props is not None:
|
||||
self.disconnect_transfer(file_props)
|
||||
file_props['error'] = -3
|
||||
self.dispatch('FILE_REQUEST_ERROR', (to, file_props))
|
||||
|
||||
self.dispatch('FILE_REQUEST_ERROR', (to, file_props, msg))
|
||||
|
||||
def _proxy_auth_ok(self, proxy):
|
||||
'''cb, called after authentication to proxy server '''
|
||||
file_props = self.files_props[proxy['sid']]
|
||||
|
@ -339,7 +345,7 @@ class ConnectionBytestream:
|
|||
return
|
||||
file_props = self.files_props[id]
|
||||
file_props['error'] = -4
|
||||
self.dispatch('FILE_REQUEST_ERROR', (jid, file_props))
|
||||
self.dispatch('FILE_REQUEST_ERROR', (jid, file_props, ''))
|
||||
raise common.xmpp.NodeProcessed
|
||||
|
||||
def _bytestreamSetCB(self, con, iq_obj):
|
||||
|
@ -556,7 +562,7 @@ class ConnectionBytestream:
|
|||
return
|
||||
jid = helpers.get_jid_from_iq(iq_obj)
|
||||
file_props['error'] = -3
|
||||
self.dispatch('FILE_REQUEST_ERROR', (jid, file_props))
|
||||
self.dispatch('FILE_REQUEST_ERROR', (jid, file_props, ''))
|
||||
raise common.xmpp.NodeProcessed
|
||||
|
||||
class ConnectionDisco:
|
||||
|
@ -580,8 +586,8 @@ class ConnectionDisco:
|
|||
iq.setID(id)
|
||||
# Wait the answer during 30 secondes
|
||||
self.awaiting_timeouts[gajim.idlequeue.current_time() + 30] = (id,
|
||||
_('Registration information for transport %s has not arrived in time' % \
|
||||
agent))
|
||||
_('Registration information for transport %s has not arrived in time') % \
|
||||
agent)
|
||||
self.connection.SendAndCallForResponse(iq, self._ReceivedRegInfo,
|
||||
{'agent': agent})
|
||||
|
||||
|
@ -726,17 +732,28 @@ class ConnectionDisco:
|
|||
qc = iq_obj.getQueryChildren()
|
||||
if not qc:
|
||||
qc = []
|
||||
is_muc = False
|
||||
transport_type = ''
|
||||
for i in qc:
|
||||
if i.getName() == 'identity':
|
||||
attr = {}
|
||||
for key in i.getAttrs().keys():
|
||||
attr[key] = i.getAttr(key)
|
||||
if attr.has_key('category') and attr['category'] in ('gateway', 'headline')\
|
||||
and attr.has_key('type'):
|
||||
transport_type = attr['type']
|
||||
if attr.has_key('category') and attr['category'] == 'conference' \
|
||||
and attr.has_key('type') and attr['type'] == 'text':
|
||||
is_muc = True
|
||||
identities.append(attr)
|
||||
elif i.getName() == 'feature':
|
||||
features.append(i.getAttr('var'))
|
||||
elif i.getName() == 'x' and i.getAttr('xmlns') == common.xmpp.NS_DATA:
|
||||
elif i.getName() == 'x' and i.getNamespace() == common.xmpp.NS_DATA:
|
||||
data.append(common.xmpp.DataForm(node=i))
|
||||
jid = helpers.get_full_jid_from_iq(iq_obj)
|
||||
if transport_type and jid not in gajim.transport_type:
|
||||
gajim.transport_type[jid] = transport_type
|
||||
gajim.logger.save_transport_type(jid, transport_type)
|
||||
id = iq_obj.getID()
|
||||
if not identities: # ejabberd doesn't send identities when we browse online users
|
||||
#FIXME: see http://www.jabber.ru/bugzilla/show_bug.cgi?id=225
|
||||
|
@ -744,6 +761,14 @@ class ConnectionDisco:
|
|||
if id[0] == 'p':
|
||||
if features.__contains__(common.xmpp.NS_BYTESTREAM):
|
||||
gajim.proxy65_manager.resolve(jid, self.connection, self.name)
|
||||
if features.__contains__(common.xmpp.NS_MUC) and is_muc:
|
||||
type_ = transport_type or 'jabber'
|
||||
self.muc_jid[type_] = jid
|
||||
if transport_type:
|
||||
if self.available_transports.has_key(transport_type):
|
||||
self.available_transports[transport_type].append(jid)
|
||||
else:
|
||||
self.available_transports[transport_type] = [jid]
|
||||
self.dispatch('AGENT_INFO_INFO', (jid, node, identities,
|
||||
features, data))
|
||||
|
||||
|
@ -851,7 +876,10 @@ class ConnectionVcard:
|
|||
|
||||
id = self.connection.getAnID()
|
||||
iq.setID(id)
|
||||
self.awaiting_answers[id] = (VCARD_ARRIVED, jid)
|
||||
j = jid
|
||||
if not j:
|
||||
j = gajim.get_jid_from_account(self.name)
|
||||
self.awaiting_answers[id] = (VCARD_ARRIVED, j)
|
||||
if is_fake_jid:
|
||||
room_jid, nick = gajim.get_room_and_nick_from_fjid(jid)
|
||||
if not room_jid in self.room_jids:
|
||||
|
@ -939,9 +967,12 @@ class ConnectionVcard:
|
|||
elif self.awaiting_answers[id][0] == VCARD_ARRIVED:
|
||||
# If vcard is empty, we send to the interface an empty vcard so that
|
||||
# it knows it arrived
|
||||
if not iq_obj.getTag('vCard'):
|
||||
jid = self.awaiting_answers[id][1]
|
||||
our_jid = gajim.get_jid_from_account(self.name)
|
||||
jid = self.awaiting_answers[id][1]
|
||||
our_jid = gajim.get_jid_from_account(self.name)
|
||||
if iq_obj.getType() == 'error' and jid == our_jid:
|
||||
# our server doesn't support vcard
|
||||
self.vcard_supported = False
|
||||
if not iq_obj.getTag('vCard') or iq_obj.getType() == 'error':
|
||||
if jid and jid != our_jid:
|
||||
# Write an empty file
|
||||
self.save_vcard_to_hd(jid, '')
|
||||
|
@ -951,6 +982,29 @@ class ConnectionVcard:
|
|||
elif self.awaiting_answers[id][0] == AGENT_REMOVED:
|
||||
jid = self.awaiting_answers[id][1]
|
||||
self.dispatch('AGENT_REMOVED', jid)
|
||||
elif self.awaiting_answers[id][0] == METACONTACTS_ARRIVED:
|
||||
if iq_obj.getType() == 'result':
|
||||
# Metacontact tags
|
||||
# http://www.jabber.org/jeps/jep-XXXX.html
|
||||
meta_list = {}
|
||||
query = iq_obj.getTag('query')
|
||||
storage = query.getTag('storage')
|
||||
metas = storage.getTags('meta')
|
||||
for meta in metas:
|
||||
jid = meta.getAttr('jid')
|
||||
tag = meta.getAttr('tag')
|
||||
data = {'jid': jid}
|
||||
order = meta.getAttr('order')
|
||||
if order != None:
|
||||
data['order'] = order
|
||||
if meta_list.has_key(tag):
|
||||
meta_list[tag].append(data)
|
||||
else:
|
||||
meta_list[tag] = [data]
|
||||
self.dispatch('METACONTACTS', meta_list)
|
||||
# We can now continue connection by requesting the roster
|
||||
self.connection.initRoster()
|
||||
|
||||
del self.awaiting_answers[id]
|
||||
|
||||
def _vCardCB(self, con, vc):
|
||||
|
@ -1051,6 +1105,9 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco)
|
|||
# keep the jids we auto added (transports contacts) to not send the
|
||||
# SUBSCRIBED event to gui
|
||||
self.automatically_added = []
|
||||
# keep the latest subscribed event for each jid to prevent loop when we
|
||||
# acknoledge presences
|
||||
self.subscribed_events = {}
|
||||
try:
|
||||
idle.init()
|
||||
except:
|
||||
|
@ -1077,7 +1134,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco)
|
|||
raise common.xmpp.NodeProcessed
|
||||
|
||||
def _ErrorCB(self, con, iq_obj):
|
||||
errmsg = iq_obj.getError()
|
||||
errmsg = iq_obj.getErrorMsg()
|
||||
errcode = iq_obj.getErrorCode()
|
||||
jid_from = helpers.get_full_jid_from_iq(iq_obj)
|
||||
id = unicode(iq_obj.getID())
|
||||
|
@ -1113,26 +1170,6 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco)
|
|||
self.bookmarks.append(bm)
|
||||
self.dispatch('BOOKMARKS', self.bookmarks)
|
||||
|
||||
elif ns == 'storage:metacontacts':
|
||||
# Metacontact tags
|
||||
# http://www.jabber.org/jeps/jep-XXXX.html
|
||||
meta_list = {}
|
||||
metas = storage.getTags('meta')
|
||||
for meta in metas:
|
||||
jid = meta.getAttr('jid')
|
||||
tag = meta.getAttr('tag')
|
||||
data = {'jid': jid}
|
||||
order = meta.getAttr('order')
|
||||
if order != None:
|
||||
data['order'] = order
|
||||
if meta_list.has_key(tag):
|
||||
meta_list[tag].append(data)
|
||||
else:
|
||||
meta_list[tag] = [data]
|
||||
self.dispatch('METACONTACTS', meta_list)
|
||||
# We can now continue connection by requesting the roster
|
||||
self.connection.initRoster()
|
||||
|
||||
elif ns == 'gajim:prefs':
|
||||
# Preferences data
|
||||
# http://www.jabber.org/jeps/jep-0049.html
|
||||
|
@ -1215,6 +1252,16 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco)
|
|||
jid_stripped, resource = gajim.get_room_and_nick_from_fjid(who)
|
||||
self.dispatch('OS_INFO', (jid_stripped, resource, client_info, os_info))
|
||||
|
||||
def _TimeCB(self, con, iq_obj):
|
||||
gajim.log.debug('TimeCB')
|
||||
iq_obj = iq_obj.buildReply('result')
|
||||
qp = iq_obj.getTag('query')
|
||||
qp.setTagData('utc', strftime("%Y%m%dT%T", gmtime()))
|
||||
qp.setTagData('tz', strftime("%Z", gmtime()))
|
||||
qp.setTagData('display', strftime("%c", localtime()))
|
||||
self.connection.send(iq_obj)
|
||||
raise common.xmpp.NodeProcessed
|
||||
|
||||
|
||||
def _gMailNewMailCB(self, con, gm):
|
||||
'''Called when we get notified of new mail messages in gmail account'''
|
||||
|
@ -1239,9 +1286,20 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco)
|
|||
newmsgs = gm.getTag('mailbox').getAttr('total-matched')
|
||||
if newmsgs != '0':
|
||||
# there are new messages
|
||||
gmail_messages_list = []
|
||||
if gm.getTag('mailbox').getTag('mail-thread-info'):
|
||||
gmail_messages = gm.getTag('mailbox').getTags('mail-thread-info')
|
||||
for gmessage in gmail_messages:
|
||||
gmail_from = gmessage.getTag('senders').getTag('sender').getAttr('address')
|
||||
gmail_subject = gmessage.getTag('subject').getData()
|
||||
gmail_snippet = gmessage.getTag('snippet').getData()
|
||||
gmail_messages_list.append({ \
|
||||
'From': gmail_from, \
|
||||
'Subject': gmail_subject, \
|
||||
'Snippet': gmail_snippet})
|
||||
jid = gajim.get_jid_from_account(self.name)
|
||||
gajim.log.debug(('You have %s new gmail e-mails on %s.') % (newmsgs, jid))
|
||||
self.dispatch('GMAIL_NOTIFY', (jid, newmsgs))
|
||||
self.dispatch('GMAIL_NOTIFY', (jid, newmsgs, gmail_messages_list))
|
||||
raise common.xmpp.NodeProcessed
|
||||
|
||||
def _messageCB(self, con, msg):
|
||||
|
@ -1295,7 +1353,11 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco)
|
|||
composing_jep = 'JEP-0022'
|
||||
if not msgtxt and chatstate_child.getTag('composing'):
|
||||
chatstate = 'composing'
|
||||
|
||||
# JEP-0172 User Nickname
|
||||
user_nick = msg.getTagData('nick')
|
||||
if not user_nick:
|
||||
user_nick = ''
|
||||
|
||||
if encTag and GnuPG.USE_GPG:
|
||||
#decrypt
|
||||
encmsg = encTag.getData()
|
||||
|
@ -1325,9 +1387,11 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco)
|
|||
# Ignore message from room in which we are not
|
||||
if not self.last_history_line.has_key(jid):
|
||||
return
|
||||
self.dispatch('GC_MSG', (frm, msgtxt, tim))
|
||||
if self.name not in no_log_for and jid in self.last_history_line \
|
||||
and not int(float(time.mktime(tim))) <= \
|
||||
has_timestamp = False
|
||||
if msg.timestamp:
|
||||
has_timestamp = True
|
||||
self.dispatch('GC_MSG', (frm, msgtxt, tim, has_timestamp))
|
||||
if self.name not in no_log_for and not int(float(time.mktime(tim))) <= \
|
||||
self.last_history_line[jid] and msgtxt:
|
||||
gajim.logger.write('gc_msg', frm, msgtxt, tim = tim)
|
||||
elif mtype == 'chat': # it's type 'chat'
|
||||
|
@ -1338,7 +1402,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco)
|
|||
msg_id = gajim.logger.write('chat_msg_recv', frm, msgtxt, tim = tim,
|
||||
subject = subject)
|
||||
self.dispatch('MSG', (frm, msgtxt, tim, encrypted, mtype, subject,
|
||||
chatstate, msg_id, composing_jep))
|
||||
chatstate, msg_id, composing_jep, user_nick))
|
||||
else: # it's single message
|
||||
if self.name not in no_log_for and jid not in no_log_for and msgtxt:
|
||||
gajim.logger.write('single_msg_recv', frm, msgtxt, tim = tim,
|
||||
|
@ -1352,7 +1416,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco)
|
|||
self.dispatch('GC_INVITATION',(frm, jid_from, reason, password))
|
||||
else:
|
||||
self.dispatch('MSG', (frm, msgtxt, tim, encrypted, 'normal',
|
||||
subject, chatstate, msg_id, composing_jep))
|
||||
subject, chatstate, msg_id, composing_jep, user_nick))
|
||||
# END messageCB
|
||||
|
||||
def _presenceCB(self, con, prs):
|
||||
|
@ -1361,27 +1425,42 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco)
|
|||
if ptype == 'available':
|
||||
ptype = None
|
||||
gajim.log.debug('PresenceCB: %s' % ptype)
|
||||
who = helpers.get_full_jid_from_iq(prs)
|
||||
try:
|
||||
who = helpers.get_full_jid_from_iq(prs)
|
||||
except:
|
||||
if prs.getTag('error').getTag('jid-malformed'):
|
||||
# wrong jid, we probably tried to change our nick in a room to a non valid
|
||||
# one
|
||||
who = str(prs.getFrom())
|
||||
jid_stripped, resource = gajim.get_room_and_nick_from_fjid(who)
|
||||
self.dispatch('GC_MSG', (jid_stripped, _('Nickname not allowed: %s') % \
|
||||
resource, None, False))
|
||||
return
|
||||
jid_stripped, resource = gajim.get_room_and_nick_from_fjid(who)
|
||||
timestamp = None
|
||||
is_gc = False # is it a GC presence ?
|
||||
sigTag = None
|
||||
avatar_sha = None
|
||||
# JEP-0172 User Nickname
|
||||
user_nick = prs.getTagData('nick')
|
||||
if not user_nick:
|
||||
user_nick = ''
|
||||
transport_auto_auth = False
|
||||
xtags = prs.getTags('x')
|
||||
for x in xtags:
|
||||
if x.getNamespace().startswith(common.xmpp.NS_MUC):
|
||||
namespace = x.getNamespace()
|
||||
if namespace.startswith(common.xmpp.NS_MUC):
|
||||
is_gc = True
|
||||
if x.getNamespace() == common.xmpp.NS_SIGNED:
|
||||
if namespace == common.xmpp.NS_SIGNED:
|
||||
sigTag = x
|
||||
if x.getNamespace() == common.xmpp.NS_VCARD_UPDATE:
|
||||
if namespace == common.xmpp.NS_VCARD_UPDATE:
|
||||
avatar_sha = x.getTagData('photo')
|
||||
if x.getNamespace() == common.xmpp.NS_DELAY:
|
||||
if namespace == common.xmpp.NS_DELAY:
|
||||
# JEP-0091
|
||||
tim = prs.getTimestamp()
|
||||
tim = time.strptime(tim, '%Y%m%dT%H:%M:%S')
|
||||
timestamp = time.localtime(timegm(tim))
|
||||
if x.getNamespace() == 'http://delx.cjb.net/protocol/roster-subsync':
|
||||
if namespace == 'http://delx.cjb.net/protocol/roster-subsync':
|
||||
# see http://trac.gajim.org/ticket/326
|
||||
agent = gajim.get_server_from_jid(jid_stripped)
|
||||
if self.connection.getRoster().getItem(agent): # to be sure it's a transport contact
|
||||
|
@ -1389,7 +1468,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco)
|
|||
|
||||
no_log_for = gajim.config.get_per('accounts', self.name,
|
||||
'no_log_for').split()
|
||||
status = prs.getStatus()
|
||||
status = prs.getStatus() or ''
|
||||
show = prs.getShow()
|
||||
if not show in STATUS_LIST:
|
||||
show = '' # We ignore unknown show
|
||||
|
@ -1448,7 +1527,16 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco)
|
|||
if not ptype or ptype == 'unavailable':
|
||||
if gajim.config.get('log_contact_status_changes') and self.name\
|
||||
not in no_log_for and jid_stripped not in no_log_for:
|
||||
gajim.logger.write('gcstatus', who, status, show)
|
||||
gc_c = gajim.contacts.get_gc_contact(self.name, jid_stripped, resource)
|
||||
st = status or ''
|
||||
if gc_c:
|
||||
jid = gc_c.jid
|
||||
else:
|
||||
jid = prs.getJid()
|
||||
if jid:
|
||||
# we know real jid, save it in db
|
||||
st += ' (%s)' % jid
|
||||
gajim.logger.write('gcstatus', who, st, show)
|
||||
if avatar_sha:
|
||||
if self.vcard_shas.has_key(who):
|
||||
if avatar_sha != self.vcard_shas[who]:
|
||||
|
@ -1475,23 +1563,49 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco)
|
|||
resource, prio, keyID, timestamp))
|
||||
if transport_auto_auth:
|
||||
self.automatically_added.append(jid_stripped)
|
||||
self.request_subscription(jid_stripped)
|
||||
self.request_subscription(jid_stripped, name = user_nick)
|
||||
else:
|
||||
if not status:
|
||||
status = _('I would like to add you to my roster.')
|
||||
self.dispatch('SUBSCRIBE', (who, status))
|
||||
self.dispatch('SUBSCRIBE', (who, status, user_nick))
|
||||
elif ptype == 'subscribed':
|
||||
if jid_stripped in self.automatically_added:
|
||||
self.automatically_added.remove(jid_stripped)
|
||||
else:
|
||||
self.dispatch('SUBSCRIBED', (jid_stripped, resource))
|
||||
# detect a subscription loop
|
||||
if not self.subscribed_events.has_key(jid_stripped):
|
||||
self.subscribed_events[jid_stripped] = []
|
||||
self.subscribed_events[jid_stripped].append(time.time())
|
||||
block = False
|
||||
if len(self.subscribed_events[jid_stripped]) > 5:
|
||||
if time.time() - self.subscribed_events[jid_stripped][0] < 5:
|
||||
block = True
|
||||
self.subscribed_events[jid_stripped] = self.subscribed_events[jid_stripped][1:]
|
||||
if block:
|
||||
gajim.config.set_per('account', self.name,
|
||||
'dont_ack_subscription', True)
|
||||
else:
|
||||
self.dispatch('SUBSCRIBED', (jid_stripped, resource))
|
||||
# BE CAREFUL: no con.updateRosterItem() in a callback
|
||||
gajim.log.debug(_('we are now subscribed to %s') % who)
|
||||
elif ptype == 'unsubscribe':
|
||||
gajim.log.debug(_('unsubscribe request from %s') % who)
|
||||
elif ptype == 'unsubscribed':
|
||||
gajim.log.debug(_('we are now unsubscribed from %s') % who)
|
||||
self.dispatch('UNSUBSCRIBED', jid_stripped)
|
||||
# detect a unsubscription loop
|
||||
if not self.subscribed_events.has_key(jid_stripped):
|
||||
self.subscribed_events[jid_stripped] = []
|
||||
self.subscribed_events[jid_stripped].append(time.time())
|
||||
block = False
|
||||
if len(self.subscribed_events[jid_stripped]) > 5:
|
||||
if time.time() - self.subscribed_events[jid_stripped][0] < 5:
|
||||
block = True
|
||||
self.subscribed_events[jid_stripped] = self.subscribed_events[jid_stripped][1:]
|
||||
if block:
|
||||
gajim.config.set_per('account', self.name, 'dont_ack_subscription',
|
||||
True)
|
||||
else:
|
||||
self.dispatch('UNSUBSCRIBED', jid_stripped)
|
||||
elif ptype == 'error':
|
||||
errmsg = prs.getError()
|
||||
errcode = prs.getErrorCode()
|
||||
|
@ -1650,13 +1764,18 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco)
|
|||
print >> sys.stderr, _('JID %s is not RFC compliant. It will not be added to your roster. Use roster management tools such as http://jru.jabberstudio.org/ to remove it') % jid
|
||||
else:
|
||||
infos = raw_roster[jid]
|
||||
if jid != our_jid and (not infos['subscription'] or infos['subscription'] == \
|
||||
'none') and (not infos['ask'] or infos['ask'] == 'none') and not infos['name'] \
|
||||
and not infos['groups']:
|
||||
if jid != our_jid and (not infos['subscription'] or \
|
||||
infos['subscription'] == 'none') and (not infos['ask'] or \
|
||||
infos['ask'] == 'none') and not infos['name'] and \
|
||||
not infos['groups']:
|
||||
# remove this useless item, it won't be shown in roster anyway
|
||||
self.connection.getRoster().delItem(jid)
|
||||
elif jid != our_jid: # don't add our jid
|
||||
roster[j] = raw_roster[jid]
|
||||
if gajim.jid_is_transport(jid) and \
|
||||
not gajim.get_transport_name_from_jid(jid):
|
||||
# we can't determine which iconset to use
|
||||
self.discoverInfo(jid)
|
||||
|
||||
self.dispatch('ROSTER', roster)
|
||||
|
||||
|
@ -1694,7 +1813,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco)
|
|||
|
||||
# If it's a gmail account,
|
||||
# inform the server that we want e-mail notifications
|
||||
if gajim.get_server_from_jid(our_jid) == 'gmail.com':
|
||||
if gajim.get_server_from_jid(our_jid) in gajim.gmail_domains:
|
||||
gajim.log.debug(('%s is a gmail account. Setting option '
|
||||
'to get e-mail notifications on the server.') % (our_jid))
|
||||
iq = common.xmpp.Iq(typ = 'set', to = our_jid)
|
||||
|
@ -1748,6 +1867,8 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco)
|
|||
common.xmpp.NS_DISCO_INFO)
|
||||
con.RegisterHandler('iq', self._VersionCB, 'get',
|
||||
common.xmpp.NS_VERSION)
|
||||
con.RegisterHandler('iq', self._TimeCB, 'get',
|
||||
common.xmpp.NS_TIME)
|
||||
con.RegisterHandler('iq', self._LastCB, 'get',
|
||||
common.xmpp.NS_LAST)
|
||||
con.RegisterHandler('iq', self._LastResultCB, 'result',
|
||||
|
@ -1762,8 +1883,6 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco)
|
|||
common.xmpp.NS_ROSTER)
|
||||
con.RegisterHandler('iq', self._PrivateCB, 'result',
|
||||
common.xmpp.NS_PRIVATE)
|
||||
con.RegisterHandler('iq', self._PrivateErrorCB, 'error',
|
||||
common.xmpp.NS_PRIVATE)
|
||||
con.RegisterHandler('iq', self._HttpAuthCB, 'get',
|
||||
common.xmpp.NS_HTTP_AUTH)
|
||||
con.RegisterHandler('iq', self._gMailNewMailCB, 'set',
|
||||
|
|
|
@ -210,7 +210,7 @@ class Contacts:
|
|||
contacts = self.get_contacts_from_jid(account, jid)
|
||||
if not contacts and '/' in jid:
|
||||
# jid may be a fake jid, try it
|
||||
room, nick = jid.split('/')
|
||||
room, nick = jid.split('/', 1)
|
||||
contact = self.get_gc_contact(account, room, nick)
|
||||
return contact
|
||||
return self.get_highest_prio_contact_from_contacts(contacts)
|
||||
|
@ -324,8 +324,11 @@ class Contacts:
|
|||
max_order = data_['order']
|
||||
contact = self.get_contact_with_highest_priority(account, jid)
|
||||
score = (max_order - order)*10000
|
||||
if not common.gajim.jid_is_transport(jid):
|
||||
score += contact.priority*10
|
||||
|
||||
if common.gajim.get_transport_name_from_jid(jid) is None:
|
||||
score += 10
|
||||
if contact.priority > 0:
|
||||
score += contact.priority * 10
|
||||
score += ['not in roster', 'error', 'offline', 'invisible', 'dnd', 'xa',
|
||||
'away', 'chat', 'online', 'requested', 'message'].index(contact.show)
|
||||
return score
|
||||
|
|
|
@ -0,0 +1,233 @@
|
|||
## common/events.py
|
||||
##
|
||||
## Contributors for this file:
|
||||
## - Yann Le Boulanger <asterix@lagaule.org>
|
||||
##
|
||||
## Copyright (C) 2006 Yann Le Boulanger <asterix@lagaule.org>
|
||||
## Vincent Hanquez <tab@snarc.org>
|
||||
## Nikos Kouremenos <nkour@jabber.org>
|
||||
## Dimitur Kirov <dkirov@gmail.com>
|
||||
## Travis Shirk <travis@pobox.com>
|
||||
## Norman Rasmussen <norman@rasmussen.co.za>
|
||||
##
|
||||
## This program is free software; you can redistribute it and/or modify
|
||||
## it under the terms of the GNU General Public License as published
|
||||
## by the Free Software Foundation; version 2 only.
|
||||
##
|
||||
## This program is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
|
||||
import time
|
||||
import gajim
|
||||
|
||||
class Event:
|
||||
'''Information concerning each event'''
|
||||
def __init__(self, type_, time_, parameters, show_in_roster = False,
|
||||
show_in_systray = True):
|
||||
''' type_ in chat, normal, file-request, file-error, file-completed,
|
||||
file-request-error, file-send-error, file-stopped, gc_msg, pm,
|
||||
printed_chat, printed_gc_msg, printed_pm
|
||||
parameters is (per type_):
|
||||
chat, normal: [message, subject, kind, time, encrypted, resource,
|
||||
msg_id]
|
||||
where kind in error, incoming
|
||||
file-*: file_props
|
||||
gc_msg: None
|
||||
printed_*: None
|
||||
messages that are already printed in chat, but not read'''
|
||||
self.type_ = type_
|
||||
self.time_ = time_
|
||||
self.parameters = parameters
|
||||
self.show_in_roster = show_in_roster
|
||||
self.show_in_systray = show_in_systray
|
||||
|
||||
class Events:
|
||||
'''Information concerning all events'''
|
||||
def __init__(self):
|
||||
self._events = {} # list of events {acct: {jid1: [E1, E2]}, }
|
||||
|
||||
def change_account_name(self, old_name, new_name):
|
||||
if self._events.has_key(old_name):
|
||||
self._events[new_name] = self._events[old_name]
|
||||
del self._events[old_name]
|
||||
|
||||
def add_account(self, account):
|
||||
self._events[account] = {}
|
||||
|
||||
def get_accounts(self):
|
||||
return self._events.keys()
|
||||
|
||||
def remove_account(self, account):
|
||||
del self._events[account]
|
||||
|
||||
def create_event(self, type_, parameters, time_ = time.time(),
|
||||
show_in_roster = False, show_in_systray = True):
|
||||
return Event(type_, time_, parameters, show_in_roster,
|
||||
show_in_systray)
|
||||
|
||||
def add_event(self, account, jid, event):
|
||||
# No such account before ?
|
||||
if not self._events.has_key(account):
|
||||
self._events[account] = {jid: [event]}
|
||||
# no such jid before ?
|
||||
elif not self._events[account].has_key(jid):
|
||||
self._events[account][jid] = [event]
|
||||
else:
|
||||
self._events[account][jid].append(event)
|
||||
if event.show_in_systray and gajim.interface.systray_capabilities:
|
||||
gajim.interface.systray.set_img()
|
||||
|
||||
def remove_events(self, account, jid, event = None, types = []):
|
||||
'''if event is not speficied, remove all events from this jid,
|
||||
optionnaly only from given type
|
||||
return True if no such event found'''
|
||||
if not self._events.has_key(account):
|
||||
return True
|
||||
if not self._events[account].has_key(jid):
|
||||
return True
|
||||
if event: # remove only one event
|
||||
if event in self._events[account][jid]:
|
||||
if len(self._events[account][jid]) == 1:
|
||||
del self._events[account][jid]
|
||||
else:
|
||||
self._events[account][jid].remove(event)
|
||||
if event.show_in_systray and gajim.interface.systray_capabilities:
|
||||
gajim.interface.systray.set_img()
|
||||
return
|
||||
else:
|
||||
return True
|
||||
if types:
|
||||
new_list = [] # list of events to keep
|
||||
for ev in self._events[account][jid]:
|
||||
if ev.type_ not in types:
|
||||
new_list.append(ev)
|
||||
if len(new_list) == len(self._events[account][jid]):
|
||||
return True
|
||||
if new_list:
|
||||
self._events[account][jid] = new_list
|
||||
else:
|
||||
del self._events[account][jid]
|
||||
if gajim.interface.systray_capabilities:
|
||||
gajim.interface.systray.set_img()
|
||||
return
|
||||
# no event nor type given, remove them all
|
||||
del self._events[account][jid]
|
||||
if gajim.interface.systray_capabilities:
|
||||
gajim.interface.systray.set_img()
|
||||
|
||||
def get_nb_events(self, types = []):
|
||||
return self._get_nb_events(types = types)
|
||||
|
||||
def get_events(self, account, jid = None, types = []):
|
||||
'''if event is not speficied, remove all events from this jid,
|
||||
optionnaly only from given type'''
|
||||
if not self._events.has_key(account):
|
||||
return []
|
||||
if not jid:
|
||||
return self._events[account]
|
||||
if not self._events[account].has_key(jid):
|
||||
return []
|
||||
events_list = [] # list of events
|
||||
for ev in self._events[account][jid]:
|
||||
if not types or ev.type_ in types:
|
||||
events_list.append(ev)
|
||||
return events_list
|
||||
|
||||
def get_first_event(self, account, jid = None, type_ = None):
|
||||
'''Return the first event of type type_ if given'''
|
||||
events_list = self.get_events(account, jid, type_)
|
||||
# be sure it's bigger than latest event
|
||||
first_event_time = time.time() + 1
|
||||
first_event = None
|
||||
for event in events_list:
|
||||
if event.time_ < first_event_time:
|
||||
first_event_time = event.time_
|
||||
first_event = event
|
||||
return first_event
|
||||
|
||||
def _get_nb_events(self, account = None, jid = None, attribute = None, types = []):
|
||||
'''return the number of events'''
|
||||
nb = 0
|
||||
if account:
|
||||
accounts = [account]
|
||||
else:
|
||||
accounts = self._events.keys()
|
||||
for acct in accounts:
|
||||
if not self._events.has_key(acct):
|
||||
continue
|
||||
if jid:
|
||||
jids = [jid]
|
||||
else:
|
||||
jids = self._events[acct].keys()
|
||||
for j in jids:
|
||||
if not self._events[acct].has_key(j):
|
||||
continue
|
||||
for event in self._events[acct][j]:
|
||||
if types and event.type_ not in types:
|
||||
continue
|
||||
if not attribute or \
|
||||
attribute == 'systray' and event.show_in_systray or \
|
||||
attribute == 'roster' and event.show_in_roster:
|
||||
nb += 1
|
||||
return nb
|
||||
|
||||
def _get_some_events(self, attribute):
|
||||
'''attribute in systray, roster'''
|
||||
events = {}
|
||||
for account in self._events:
|
||||
events[account] = {}
|
||||
for jid in self._events[account]:
|
||||
events[account][jid] = []
|
||||
for event in self._events[account][jid]:
|
||||
if attribute == 'systray' and event.show_in_systray or \
|
||||
attribute == 'roster' and event.show_in_roster:
|
||||
events[account][jid].append(event)
|
||||
if not events[account][jid]:
|
||||
del events[account][jid]
|
||||
if not events[account]:
|
||||
del events[account]
|
||||
return events
|
||||
|
||||
def _get_first_event_with_attribute(self, events):
|
||||
'''get the first event
|
||||
events is in the form {account1: {jid1: [ev1, ev2], },. }'''
|
||||
# be sure it's bigger than latest event
|
||||
first_event_time = time.time() + 1
|
||||
first_account = None
|
||||
first_jid = None
|
||||
first_event = None
|
||||
for account in events:
|
||||
for jid in events[account]:
|
||||
for event in events[account][jid]:
|
||||
if event.time_ < first_event_time:
|
||||
first_event_time = event.time_
|
||||
first_account = account
|
||||
first_jid = jid
|
||||
first_event = event
|
||||
return first_account, first_jid, first_event
|
||||
|
||||
def get_nb_systray_events(self, types = []):
|
||||
'''returns the number of events displayedin roster'''
|
||||
return self._get_nb_events(attribute = 'systray', types = types)
|
||||
|
||||
def get_systray_events(self):
|
||||
'''return all events that must be displayed in systray:
|
||||
{account1: {jid1: [ev1, ev2], },. }'''
|
||||
return self._get_some_events('systray')
|
||||
|
||||
def get_first_systray_event(self):
|
||||
events = self.get_systray_events()
|
||||
return self._get_first_event_with_attribute(events)
|
||||
|
||||
def get_nb_roster_events(self, account = None, jid = None, types = []):
|
||||
'''returns the number of events displayedin roster'''
|
||||
return self._get_nb_events(attribute = 'roster', account = account,
|
||||
jid = jid, types = types)
|
||||
|
||||
def get_roster_events(self):
|
||||
'''return all events that must be displayed in roster:
|
||||
{account1: {jid1: [ev1, ev2], },. }'''
|
||||
return self._get_some_events('roster')
|
|
@ -23,9 +23,6 @@
|
|||
## GNU General Public License for more details.
|
||||
##
|
||||
|
||||
from common import i18n
|
||||
_ = i18n._
|
||||
|
||||
class PysqliteNotAvailable(Exception):
|
||||
'''sqlite2 is not installed or python bindings are missing'''
|
||||
def __init__(self):
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
## fuzzyclock.py
|
||||
##
|
||||
## Contributors for this file:
|
||||
##
|
||||
## - Yann Le Boulanger <asterix@lagaule.org>
|
||||
## - Christoph Neuroth <delmonico@gmx.net>
|
||||
##
|
||||
## Copyright (C) 2006 Christoph Neuroth <delmonico@gmx.net>
|
||||
## Yann Le Boulanger <asterix@lagaule.org>
|
||||
## Dimitur Kirov <dkirov@gmail.com>
|
||||
## Travis Shirk <travis@pobox.com>
|
||||
##
|
||||
## This program is free software; you can redistribute it and/or modify
|
||||
## it under the terms of the GNU General Public License as published
|
||||
## by the Free Software Foundation; version 2 only.
|
||||
##
|
||||
## This program is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
|
||||
'''
|
||||
Python class to show a "fuzzy clock".
|
||||
Homepage: http://home.gna.org/fuzzyclock/
|
||||
Project Page: http://gna.org/projects/fuzzyclock
|
||||
|
||||
The class has been ported from PHP code by
|
||||
Henrique Recidive <henrique at recidive.com> which was
|
||||
in turn based on the Fuzzy Clock Applet of Frerich Raabe (KDE).
|
||||
So most of the credit goes to this guys, thanks :-)
|
||||
'''
|
||||
|
||||
import time
|
||||
|
||||
class FuzzyClock:
|
||||
def __init__(self):
|
||||
self.__hour = 0
|
||||
self.__minute = 0
|
||||
self.__dayOfWeek = 0
|
||||
|
||||
self.__hourNames = [ _('one'), _('two'), _('three'), _('four'), _('five'), _('six'),
|
||||
_('seven'), _('eight'), _('nine'), _('ten'), _('eleven'),
|
||||
_('twelve')]
|
||||
|
||||
#Strings to use for the output. %0 will be replaced with the preceding hour (e.g. "x PAST %0"), %1 with the coming hour (e.g. "x TO %1). '''
|
||||
self.__normalFuzzy = [ _("%0 o'clock"), _('five past %0'), _('ten past %0'),
|
||||
_('quarter past %0'), _('twenty past %0'),
|
||||
_('twenty five past %0'), _('half past %0'),
|
||||
_('twenty five to %1'), _('twenty to %1'),
|
||||
_('quarter to %1'), _('ten to %1'), _('five to %1'),
|
||||
_("%1 o'clock") ]
|
||||
|
||||
#A "singular-form". It is used when talking about hour 0
|
||||
self.__normalFuzzyOne = [ _("%0 o'clock"), _('five past %0'),
|
||||
_('ten past %0'), _('quarter past %0'),
|
||||
_('twenty past %0'), _('twenty five past %0'),
|
||||
_('half past %0'), _('twenty five to %1'),
|
||||
_('twenty to %1'), _('quarter to %1'),
|
||||
_('ten to %1'), _('five to %1'), _("%1 o'clock") ]
|
||||
|
||||
self.__dayTime = [ _('Night'), _('Early morning'), _('Morning'), _('Almost noon'),
|
||||
_('Noon'), _('Afternoon'), _('Evening'), _('Late evening') ]
|
||||
|
||||
self.__fuzzyWeek = [ _('Start of week'), _('Middle of week'), _('End of week'),
|
||||
_('Weekend!') ]
|
||||
|
||||
self.setCurrent()
|
||||
|
||||
def setHour(self,hour):
|
||||
self.__hour = int(hour)
|
||||
|
||||
def setMinute(self,minute):
|
||||
self.__minute=int(minute)
|
||||
|
||||
def setDayOfWeek(self,day):
|
||||
self.__dayOfWeek=int(day)
|
||||
|
||||
def setTime(self,time):
|
||||
timeArray = time.split(":")
|
||||
self.setHour(timeArray[0])
|
||||
self.setMinute(timeArray[1])
|
||||
|
||||
def setCurrent(self):
|
||||
hour=time.strftime("%H")
|
||||
minute=time.strftime("%M")
|
||||
day=time.strftime("%w")
|
||||
|
||||
self.setTime(hour+":"+minute)
|
||||
self.setDayOfWeek(day)
|
||||
|
||||
def getFuzzyTime(self, fuzzyness = 1):
|
||||
sector = 0
|
||||
realHour = 0
|
||||
|
||||
if fuzzyness == 1 or fuzzyness == 2:
|
||||
if fuzzyness == 1:
|
||||
if self.__minute >2:
|
||||
sector = (self.__minute - 3) / 5 +1
|
||||
else:
|
||||
if self.__minute > 6:
|
||||
sector = ((self.__minute - 7) / 15 + 1) * 3
|
||||
|
||||
newTimeStr = self.__normalFuzzy[sector]
|
||||
#%0 or %1?
|
||||
deltaHour = int(newTimeStr[newTimeStr.find("%")+1])
|
||||
|
||||
if (self.__hour + deltaHour) % 12 > 0:
|
||||
realHour = (self.__hour + deltaHour) % 12 - 1
|
||||
else:
|
||||
realHour = 12 - ((self.__hour + deltaHour) % 12 + 1)
|
||||
|
||||
if realHour == 0:
|
||||
newTimeStr = self.__normalFuzzyOne[sector]
|
||||
|
||||
newTimeStr = newTimeStr.replace("%"+str(deltaHour),
|
||||
self.__hourNames[realHour])
|
||||
|
||||
|
||||
elif fuzzyness == 3:
|
||||
newTimeStr = self.__dayTime[self.__hour / 3]
|
||||
|
||||
else:
|
||||
dayOfWeek = self.__dayOfWeek
|
||||
if dayOfWeek == 1:
|
||||
newTimeStr = self.__fuzzyWeek[0]
|
||||
elif dayOfWeek >= 2 and dayOfWeek <= 4:
|
||||
newTimeStr = self.__fuzzyWeek[1]
|
||||
elif dayOfWeek == 5:
|
||||
newTimeStr = self.__fuzzyWeek[2]
|
||||
else:
|
||||
newTimeStr = self.__fuzzyWeek[3]
|
||||
|
||||
return newTimeStr
|
|
@ -23,10 +23,11 @@ import locale
|
|||
|
||||
import config
|
||||
from contacts import Contacts
|
||||
from events import Events
|
||||
|
||||
interface = None # The actual interface (the gtk one for the moment)
|
||||
version = '0.10'
|
||||
config = config.Config()
|
||||
version = config.get('version')
|
||||
connections = {}
|
||||
verbose = False
|
||||
|
||||
|
@ -81,6 +82,10 @@ if LANG is None:
|
|||
else:
|
||||
LANG = LANG[:2] # en, fr, el etc..
|
||||
|
||||
gmail_domains = ['gmail.com', 'googlemail.com']
|
||||
|
||||
transport_type = {} # list the type of transport
|
||||
|
||||
last_message_time = {} # list of time of the latest incomming message
|
||||
# {acct1: {jid1: time1, jid2: time2}, }
|
||||
encrypted_chats = {} # list of encrypted chats {acct1: [jid1, jid2], ..}
|
||||
|
@ -88,23 +93,20 @@ encrypted_chats = {} # list of encrypted chats {acct1: [jid1, jid2], ..}
|
|||
contacts = Contacts()
|
||||
gc_connected = {} # tell if we are connected to the room or not {acct: {room_jid: True}}
|
||||
gc_passwords = {} # list of the pass required to enter a room {room_jid: password}
|
||||
automatic_rooms = {} # list of rooms that must be automaticaly configured and for which we have a list of invities {account: {room_jid: {'invities': []}}}
|
||||
|
||||
groups = {} # list of groups
|
||||
newly_added = {} # list of contacts that has just signed in
|
||||
to_be_removed = {} # list of contacts that has just signed out
|
||||
|
||||
awaiting_events = {} # list of messages/FT reveived but not printed
|
||||
# awaiting_events[jid] = (type, (data1, data2, ...))
|
||||
# if type in ('chat', 'normal'): data = (message, subject, kind, time,
|
||||
# encrypted, resource)
|
||||
# kind can be (incoming, error)
|
||||
# if type in file-request, file-request-error, file-send-error, file-error,
|
||||
# file-completed, file-stopped:
|
||||
# data = file_props
|
||||
events = Events()
|
||||
|
||||
nicks = {} # list of our nick names in each account
|
||||
# should we block 'contact signed in' notifications for this account?
|
||||
# this is only for the first 30 seconds after we change our show
|
||||
# to something else than offline
|
||||
# can also contain account/transport_jid to block notifications for contacts
|
||||
# from this transport
|
||||
block_signed_in_notifications = {}
|
||||
con_types = {} # type of each connection (ssl, tls, tcp, ...)
|
||||
|
||||
|
@ -218,8 +220,11 @@ def get_transport_name_from_jid(jid, use_config_setting = True):
|
|||
# jid was None. Yann why?
|
||||
if not jid or (use_config_setting and not config.get('use_transports_iconsets')):
|
||||
return
|
||||
|
||||
|
||||
host = get_server_from_jid(jid)
|
||||
if host in transport_type:
|
||||
return transport_type[host]
|
||||
|
||||
# host is now f.e. icq.foo.org or just icq (sometimes on hacky transports)
|
||||
host_splitted = host.split('.')
|
||||
if len(host_splitted) != 0:
|
||||
|
@ -229,7 +234,7 @@ def get_transport_name_from_jid(jid, use_config_setting = True):
|
|||
if host == 'aim':
|
||||
return 'aim'
|
||||
elif host == 'gg':
|
||||
return 'gadugadu'
|
||||
return 'gadu-gadu'
|
||||
elif host == 'irc':
|
||||
return 'irc'
|
||||
elif host == 'icq':
|
||||
|
@ -277,18 +282,6 @@ def get_hostname_from_account(account_name, use_srv = False):
|
|||
return config.get_per('accounts', account_name, 'custom_host')
|
||||
return config.get_per('accounts', account_name, 'hostname')
|
||||
|
||||
def get_first_event(account, jid, typ = None):
|
||||
'''returns the first event of the given type from the awaiting_events queue'''
|
||||
if not awaiting_events[account].has_key(jid):
|
||||
return None
|
||||
q = awaiting_events[account][jid]
|
||||
if not typ:
|
||||
return q[0]
|
||||
for ev in q:
|
||||
if ev[0] == typ:
|
||||
return ev
|
||||
return None
|
||||
|
||||
def get_notification_image_prefix(jid):
|
||||
'''returns the prefix for the notification images'''
|
||||
transport_name = get_transport_name_from_jid(jid)
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
import sre
|
||||
import os
|
||||
import subprocess
|
||||
import urllib
|
||||
import errno
|
||||
import select
|
||||
|
@ -26,7 +27,7 @@ import sha
|
|||
from encodings.punycode import punycode_encode
|
||||
|
||||
import gajim
|
||||
import i18n
|
||||
from i18n import Q_
|
||||
from xmpp_stringprep import nodeprep, resourceprep, nameprep
|
||||
|
||||
try:
|
||||
|
@ -36,9 +37,6 @@ try:
|
|||
except:
|
||||
pass
|
||||
|
||||
_ = i18n._
|
||||
Q_ = i18n.Q_
|
||||
|
||||
special_groups = (_('Transports'), _('Not in Roster'), _('Observers'))
|
||||
|
||||
class InvalidFormat(Exception):
|
||||
|
@ -363,6 +361,11 @@ def is_in_path(name_of_command, return_abs_path = False):
|
|||
else:
|
||||
return is_in_dir
|
||||
|
||||
def exec_command(command):
|
||||
'''command is a string that contain arguments'''
|
||||
# os.system(command)
|
||||
subprocess.Popen(command.split())
|
||||
|
||||
def launch_browser_mailer(kind, uri):
|
||||
#kind = 'url' or 'mail'
|
||||
if os.name == 'nt':
|
||||
|
@ -386,11 +389,9 @@ def launch_browser_mailer(kind, uri):
|
|||
command = gajim.config.get('custommailapp')
|
||||
if command == '': # if no app is configured
|
||||
return
|
||||
# we add the uri in "" so we have good parsing from shell
|
||||
uri = uri.replace('"', '\\"') # escape "
|
||||
command = command + ' "' + uri + '" &'
|
||||
try: #FIXME: when we require python2.4+ use subprocess module
|
||||
os.system(command)
|
||||
command = command + ' ' + uri
|
||||
try:
|
||||
exec_command(command)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
@ -409,11 +410,9 @@ def launch_file_manager(path_to_open):
|
|||
command = gajim.config.get('custom_file_manager')
|
||||
if command == '': # if no app is configured
|
||||
return
|
||||
# we add the path in "" so we have good parsing from shell
|
||||
path_to_open = path_to_open.replace('"', '\\"') # escape "
|
||||
command = command + ' "' + path_to_open + '" &'
|
||||
try: #FIXME: when we require python2.4+ use subprocess module
|
||||
os.system(command)
|
||||
command = command + ' ' + path_to_open
|
||||
try:
|
||||
exec_command(command)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
@ -421,6 +420,9 @@ def play_sound(event):
|
|||
if not gajim.config.get('sounds_on'):
|
||||
return
|
||||
path_to_soundfile = gajim.config.get_per('soundevents', event, 'path')
|
||||
play_sound_file(path_to_soundfile)
|
||||
|
||||
def play_sound_file(path_to_soundfile):
|
||||
if path_to_soundfile == 'beep':
|
||||
print '\a' # make a speaker beep
|
||||
return
|
||||
|
@ -436,11 +438,8 @@ def play_sound(event):
|
|||
if gajim.config.get('soundplayer') == '':
|
||||
return
|
||||
player = gajim.config.get('soundplayer')
|
||||
# we add the path in "" so we have good parsing from shell
|
||||
path_to_soundfile = path_to_soundfile.replace('"', '\\"') # escape "
|
||||
command = player + ' "' + path_to_soundfile + '" &'
|
||||
#FIXME: when we require 2.4+ use subprocess module
|
||||
os.system(command)
|
||||
command = player + ' ' + path_to_soundfile
|
||||
exec_command(command)
|
||||
|
||||
def get_file_path_from_dnd_dropped_uri(uri):
|
||||
path = urllib.url2pathname(uri) # escape special chars
|
||||
|
@ -464,14 +463,6 @@ def from_xs_boolean_to_python_boolean(value):
|
|||
|
||||
return val
|
||||
|
||||
def ensure_unicode_string(s):
|
||||
# py23 u'abc'.decode('utf-8') raises
|
||||
# python24 does not. if python23 is ooold we can remove this func
|
||||
# FIXME: remove this when we abandon py23
|
||||
if isinstance(s, str):
|
||||
s = s.decode('utf-8')
|
||||
return s
|
||||
|
||||
def get_xmpp_show(show):
|
||||
if show in ('online', 'offline'):
|
||||
return None
|
||||
|
@ -514,9 +505,9 @@ def get_global_status():
|
|||
|
||||
def get_icon_name_to_show(contact, account = None):
|
||||
'''Get the icon name to show in online, away, requested, ...'''
|
||||
if account and gajim.awaiting_events[account].has_key(contact.jid):
|
||||
if account and gajim.events.get_nb_roster_events(account, contact.jid):
|
||||
return 'message'
|
||||
if account and gajim.awaiting_events[account].has_key(
|
||||
if account and gajim.events.get_nb_roster_events(account,
|
||||
contact.get_full_jid()):
|
||||
return 'message'
|
||||
if contact.jid.find('@') <= 0: # if not '@' or '@' starts the jid ==> agent
|
||||
|
@ -543,6 +534,14 @@ def decode_string(string):
|
|||
|
||||
return string
|
||||
|
||||
def ensure_utf8_string(string):
|
||||
'''make sure string is in UTF-8'''
|
||||
try:
|
||||
string = decode_string(string).encode('utf-8')
|
||||
except:
|
||||
pass
|
||||
return string
|
||||
|
||||
def get_windows_reg_env(varname, default=''):
|
||||
'''asks for paths commonly used but not exposed as ENVs
|
||||
in english Windows 2003 those are:
|
||||
|
@ -692,8 +691,11 @@ def get_os_info():
|
|||
text = fd.readline().strip() # get only first line
|
||||
fd.close()
|
||||
if path_to_file.endswith('version'):
|
||||
# sourcemage_version has all the info we need
|
||||
if not os.path.basename(path_to_file).startswith('sourcemage'):
|
||||
# sourcemage_version and slackware-version files
|
||||
# have all the info we need (name and version of distro)
|
||||
if not os.path.basename(path_to_file).startswith(
|
||||
'sourcemage') or not\
|
||||
os.path.basename(path_to_file).startswith('slackware'):
|
||||
text = distro_name + ' ' + text
|
||||
elif path_to_file.endswith('aurox-release'):
|
||||
# file doesn't have version
|
||||
|
@ -724,20 +726,73 @@ def sanitize_filename(filename):
|
|||
|
||||
return filename
|
||||
|
||||
def allow_showing_notification(account):
|
||||
def allow_showing_notification(account, type = None, advanced_notif_num = None,
|
||||
first = True):
|
||||
'''is it allowed to show nofication?
|
||||
check OUR status and if we allow notifications for that status'''
|
||||
check OUR status and if we allow notifications for that status
|
||||
type is the option that need to be True ex: notify_on_signing
|
||||
first: set it to false when it's not the first message'''
|
||||
if advanced_notif_num != None:
|
||||
popup = gajim.config.get_per('notifications', str(advanced_notif_num),
|
||||
'popup')
|
||||
if popup == 'yes':
|
||||
return True
|
||||
if popup == 'no':
|
||||
return False
|
||||
if type and (not gajim.config.get(type) or not first):
|
||||
return False
|
||||
if type and gajim.config.get(type) and first:
|
||||
return True
|
||||
if gajim.config.get('autopopupaway'): # always show notification
|
||||
return True
|
||||
if gajim.connections[account].connected in (2, 3): # we're online or chat
|
||||
return True
|
||||
return False
|
||||
|
||||
def allow_popup_window(account):
|
||||
def allow_popup_window(account, advanced_notif_num = None):
|
||||
'''is it allowed to popup windows?'''
|
||||
if advanced_notif_num != None:
|
||||
popup = gajim.config.get_per('notifications', str(advanced_notif_num),
|
||||
'auto_open')
|
||||
if popup == 'yes':
|
||||
return True
|
||||
if popup == 'no':
|
||||
return False
|
||||
autopopup = gajim.config.get('autopopup')
|
||||
autopopupaway = gajim.config.get('autopopupaway')
|
||||
if autopopup and (autopopupaway or \
|
||||
gajim.connections[account].connected in (2, 3)): # we're online or chat
|
||||
return True
|
||||
return False
|
||||
|
||||
def allow_sound_notification(sound_event, advanced_notif_num = None):
|
||||
if advanced_notif_num != None:
|
||||
sound = gajim.config.get_per('notifications', str(advanced_notif_num),
|
||||
'sound')
|
||||
if sound == 'yes':
|
||||
return True
|
||||
if sound == 'no':
|
||||
return False
|
||||
if gajim.config.get_per('soundevents', sound_event, 'enabled'):
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_chat_control(account, contact):
|
||||
full_jid_with_resource = contact.jid
|
||||
if contact.resource:
|
||||
full_jid_with_resource += '/' + contact.resource
|
||||
highest_contact = gajim.contacts.get_contact_with_highest_priority(
|
||||
account, contact.jid)
|
||||
# Look for a chat control that has the given resource, or default to
|
||||
# one without resource
|
||||
ctrl = gajim.interface.msg_win_mgr.get_control(full_jid_with_resource,
|
||||
account)
|
||||
if ctrl:
|
||||
return ctrl
|
||||
elif not highest_contact or not highest_contact.resource:
|
||||
# unknow contact or offline message
|
||||
return gajim.interface.msg_win_mgr.get_control(contact.jid, account)
|
||||
elif highest_contact and contact.resource != \
|
||||
highest_contact.resource:
|
||||
return None
|
||||
return gajim.interface.msg_win_mgr.get_control(contact.jid, account)
|
||||
|
|
|
@ -48,21 +48,11 @@ if os.name == 'nt':
|
|||
if lang:
|
||||
os.environ['LANG'] = lang
|
||||
|
||||
_translation = None
|
||||
|
||||
def init():
|
||||
global _translation
|
||||
try:
|
||||
_translation = gettext.translation(APP, DIR)
|
||||
except IOError:
|
||||
_translation = gettext.NullTranslations()
|
||||
|
||||
init()
|
||||
|
||||
def _(s):
|
||||
if s == '':
|
||||
return s
|
||||
return _translation.ugettext(s)
|
||||
gettext.install(APP, DIR, unicode = True)
|
||||
if gettext._translations:
|
||||
_translation = gettext._translations.values()[0]
|
||||
else:
|
||||
_translation = gettext.NullTranslations()
|
||||
|
||||
def Q_(s):
|
||||
# Qualified translatable strings
|
||||
|
|
|
@ -29,8 +29,7 @@ import time
|
|||
import datetime
|
||||
|
||||
import exceptions
|
||||
import i18n
|
||||
_ = i18n._
|
||||
import gajim
|
||||
|
||||
try:
|
||||
from pysqlite2 import dbapi2 as sqlite
|
||||
|
@ -79,30 +78,50 @@ class Constants:
|
|||
self.SHOW_OFFLINE
|
||||
) = range(6)
|
||||
|
||||
(
|
||||
self.TYPE_AIM,
|
||||
self.TYPE_GG,
|
||||
self.TYPE_HTTP_WS,
|
||||
self.TYPE_ICQ,
|
||||
self.TYPE_MSN,
|
||||
self.TYPE_QQ,
|
||||
self.TYPE_SMS,
|
||||
self.TYPE_SMTP,
|
||||
self.TYPE_TLEN,
|
||||
self.TYPE_YAHOO,
|
||||
self.TYPE_NEWMAIL,
|
||||
self.TYPE_RSS,
|
||||
self.TYPE_WEATHER,
|
||||
) = range(13)
|
||||
|
||||
constants = Constants()
|
||||
|
||||
class Logger:
|
||||
def __init__(self):
|
||||
self.jids_already_in = [] # holds jids that we already have in DB
|
||||
|
||||
self.con = None
|
||||
|
||||
if not os.path.exists(LOG_DB_PATH):
|
||||
# this can happen only the first time (the time we create the db)
|
||||
# db is not created here but in src/common/checks_paths.py
|
||||
return
|
||||
self.init_vars()
|
||||
|
||||
|
||||
def init_vars(self):
|
||||
# if locked, wait up to 20 sec to unlock
|
||||
# before raise (hopefully should be enough)
|
||||
if self.con:
|
||||
self.con.close()
|
||||
self.con = sqlite.connect(LOG_DB_PATH, timeout = 20.0,
|
||||
isolation_level = 'IMMEDIATE')
|
||||
self.cur = self.con.cursor()
|
||||
|
||||
|
||||
self.get_jids_already_in_db()
|
||||
|
||||
def get_jids_already_in_db(self):
|
||||
self.cur.execute('SELECT jid FROM jids')
|
||||
rows = self.cur.fetchall() # list of tupples: [(u'aaa@bbb',), (u'cc@dd',)]
|
||||
self.jids_already_in = []
|
||||
for row in rows:
|
||||
# row[0] is first item of row (the only result here, the jid)
|
||||
self.jids_already_in.append(row[0])
|
||||
|
@ -193,7 +212,66 @@ class Logger:
|
|||
show_col = 'UNKNOWN'
|
||||
|
||||
return kind_col, show_col
|
||||
|
||||
|
||||
def convert_human_transport_type_to_db_api_values(self, type_):
|
||||
'''converts from string style to constant ints for db'''
|
||||
if type_ == 'aim':
|
||||
return constants.TYPE_AIM
|
||||
if type_ == 'gadu-gadu':
|
||||
return constants.TYPE_GG
|
||||
if type_ == 'http-ws':
|
||||
return constants.TYPE_HTTP_WS
|
||||
if type_ == 'icq':
|
||||
return constants.TYPE_ICQ
|
||||
if type_ == 'msn':
|
||||
return constants.TYPE_MSN
|
||||
if type_ == 'qq':
|
||||
return constants.TYPE_QQ
|
||||
if type_ == 'sms':
|
||||
return constants.TYPE_SMS
|
||||
if type_ == 'smtp':
|
||||
return constants.TYPE_SMTP
|
||||
if type_ == 'tlen':
|
||||
return constants.TYPE_TLEN
|
||||
if type_ == 'yahoo':
|
||||
return constants.TYPE_YAHOO
|
||||
if type_ == 'newmail':
|
||||
return constants.TYPE_NEWMAIL
|
||||
if type_ == 'rss':
|
||||
return constants.TYPE_RSS
|
||||
if type_ == 'weather':
|
||||
return constants.TYPE_WEATHER
|
||||
return None
|
||||
|
||||
def convert_api_values_to_human_transport_type(self, type_id):
|
||||
'''converts from constant ints for db to string style'''
|
||||
if type_id == constants.TYPE_AIM:
|
||||
return 'aim'
|
||||
if type_id == constants.TYPE_GG:
|
||||
return 'gadu-gadu'
|
||||
if type_id == constants.TYPE_HTTP_WS:
|
||||
return 'http-ws'
|
||||
if type_id == constants.TYPE_ICQ:
|
||||
return 'icq'
|
||||
if type_id == constants.TYPE_MSN:
|
||||
return 'msn'
|
||||
if type_id == constants.TYPE_QQ:
|
||||
return 'qq'
|
||||
if type_id == constants.TYPE_SMS:
|
||||
return 'sms'
|
||||
if type_id == constants.TYPE_SMTP:
|
||||
return 'smtp'
|
||||
if type_id == constants.TYPE_TLEN:
|
||||
return 'tlen'
|
||||
if type_id == constants.TYPE_YAHOO:
|
||||
return 'yahoo'
|
||||
if type_id == constants.TYPE_NEWMAIL:
|
||||
return 'newmail'
|
||||
if type_id == constants.TYPE_RSS:
|
||||
return 'rss'
|
||||
if type_id == constants.TYPE_WEATHER:
|
||||
return 'weather'
|
||||
|
||||
def commit_to_db(self, values, write_unread = False):
|
||||
#print 'saving', values
|
||||
sql = 'INSERT INTO logs (jid_id, contact_name, time, kind, show, message, subject) VALUES (?, ?, ?, ?, ?, ?, ?)'
|
||||
|
@ -239,25 +317,8 @@ class Logger:
|
|||
self.cur.execute(
|
||||
'SELECT message_id from unread_messages WHERE jid_id = %d' % jid_id)
|
||||
results = self.cur.fetchall()
|
||||
# Remove before 0.10
|
||||
except:
|
||||
try:
|
||||
self.cur.executescript('DROP TABLE unread_messages;')
|
||||
self.con.commit()
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
self.cur.executescript('''CREATE TABLE unread_messages(
|
||||
message_id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
|
||||
jid_id INTEGER
|
||||
);''')
|
||||
self.con.commit()
|
||||
except:
|
||||
pass
|
||||
self.con.close()
|
||||
self.jids_already_in = []
|
||||
self.init_vars()
|
||||
return []
|
||||
pass
|
||||
|
||||
for message in results:
|
||||
msg_id = message[0]
|
||||
|
@ -340,7 +401,7 @@ class Logger:
|
|||
return self.commit_to_db(values, write_unread)
|
||||
|
||||
def get_last_conversation_lines(self, jid, restore_how_many_rows,
|
||||
pending_how_many, timeout):
|
||||
pending_how_many, timeout, account):
|
||||
'''accepts how many rows to restore and when to time them out (in minutes)
|
||||
(mark them as too old) and number of messages that are in queue
|
||||
and are already logged but pending to be viewed,
|
||||
|
@ -348,15 +409,17 @@ class Logger:
|
|||
list with empty tupple if nothing found to meet our demands'''
|
||||
jid = jid.lower()
|
||||
jid_id = self.get_jid_id(jid)
|
||||
where_sql = self._build_contact_where(account, jid)
|
||||
|
||||
now = int(float(time.time()))
|
||||
timed_out = now - (timeout * 60) # before that they are too old
|
||||
# so if we ask last 5 lines and we have 2 pending we get
|
||||
# 3 - 8 (we avoid the last 2 lines but we still return 5 asked)
|
||||
self.cur.execute('''
|
||||
SELECT time, kind, message FROM logs
|
||||
WHERE jid_id = %d AND kind IN (%d, %d, %d, %d) AND time > %d
|
||||
WHERE (%s) AND kind IN (%d, %d, %d, %d) AND time > %d
|
||||
ORDER BY time DESC LIMIT %d OFFSET %d
|
||||
''' % (jid_id, constants.KIND_SINGLE_MSG_RECV, constants.KIND_CHAT_MSG_RECV,
|
||||
''' % (where_sql, constants.KIND_SINGLE_MSG_RECV, constants.KIND_CHAT_MSG_RECV,
|
||||
constants.KIND_SINGLE_MSG_SENT, constants.KIND_CHAT_MSG_SENT,
|
||||
timed_out, restore_how_many_rows, pending_how_many)
|
||||
)
|
||||
|
@ -374,35 +437,36 @@ class Logger:
|
|||
start_of_day = int(time.mktime(local_time)) # we have time since epoch baby :)
|
||||
return start_of_day
|
||||
|
||||
def get_conversation_for_date(self, jid, year, month, day):
|
||||
def get_conversation_for_date(self, jid, year, month, day, account):
|
||||
'''returns contact_name, time, kind, show, message
|
||||
for each row in a list of tupples,
|
||||
returns list with empty tupple if we found nothing to meet our demands'''
|
||||
jid = jid.lower()
|
||||
jid_id = self.get_jid_id(jid)
|
||||
|
||||
where_sql = self._build_contact_where(account, jid)
|
||||
|
||||
start_of_day = self.get_unix_time_from_date(year, month, day)
|
||||
|
||||
seconds_in_a_day = 86400 # 60 * 60 * 24
|
||||
last_second_of_day = start_of_day + seconds_in_a_day - 1
|
||||
|
||||
self.cur.execute('''
|
||||
SELECT contact_name, time, kind, show, message FROM logs
|
||||
WHERE jid_id = %d
|
||||
WHERE (%s)
|
||||
AND time BETWEEN %d AND %d
|
||||
ORDER BY time
|
||||
''' % (jid_id, start_of_day, last_second_of_day))
|
||||
''' % (where_sql, start_of_day, last_second_of_day))
|
||||
|
||||
results = self.cur.fetchall()
|
||||
return results
|
||||
|
||||
def get_search_results_for_query(self, jid, query):
|
||||
def get_search_results_for_query(self, jid, query, account):
|
||||
'''returns contact_name, time, kind, show, message
|
||||
for each row in a list of tupples,
|
||||
returns list with empty tupple if we found nothing to meet our demands'''
|
||||
jid = jid.lower()
|
||||
jid_id = self.get_jid_id(jid)
|
||||
if False: #query.startswith('SELECT '): # it's SQL query
|
||||
|
||||
if False: #query.startswith('SELECT '): # it's SQL query (FIXME)
|
||||
try:
|
||||
self.cur.execute(query)
|
||||
except sqlite.OperationalError, e:
|
||||
|
@ -410,22 +474,24 @@ class Logger:
|
|||
return results
|
||||
|
||||
else: # user just typed something, we search in message column
|
||||
where_sql = self._build_contact_where(account, jid)
|
||||
like_sql = '%' + query + '%'
|
||||
self.cur.execute('''
|
||||
SELECT contact_name, time, kind, show, message, subject FROM logs
|
||||
WHERE jid_id = ? AND message LIKE ?
|
||||
WHERE (%s) AND message LIKE '%s'
|
||||
ORDER BY time
|
||||
''', (jid_id, like_sql))
|
||||
''' % (where_sql, like_sql))
|
||||
|
||||
results = self.cur.fetchall()
|
||||
return results
|
||||
|
||||
def get_days_with_logs(self, jid, year, month, max_day):
|
||||
def get_days_with_logs(self, jid, year, month, max_day, account):
|
||||
'''returns the list of days that have logs (not status messages)'''
|
||||
jid = jid.lower()
|
||||
jid_id = self.get_jid_id(jid)
|
||||
days_with_logs = []
|
||||
|
||||
where_sql = self._build_contact_where(account, jid)
|
||||
|
||||
# First select all date of month whith logs we want
|
||||
start_of_month = self.get_unix_time_from_date(year, month, 1)
|
||||
seconds_in_a_day = 86400 # 60 * 60 * 24
|
||||
|
@ -433,11 +499,11 @@ class Logger:
|
|||
|
||||
self.cur.execute('''
|
||||
SELECT time FROM logs
|
||||
WHERE jid_id = %d
|
||||
WHERE (%s)
|
||||
AND time BETWEEN %d AND %d
|
||||
AND kind NOT IN (%d, %d)
|
||||
ORDER BY time
|
||||
''' % (jid_id, start_of_month, last_second_of_month,
|
||||
''' % (where_sql, start_of_month, last_second_of_month,
|
||||
constants.KIND_STATUS, constants.KIND_GCSTATUS))
|
||||
result = self.cur.fetchall()
|
||||
|
||||
|
@ -468,17 +534,23 @@ class Logger:
|
|||
result = self.cur.fetchone()
|
||||
return days_with_logs
|
||||
|
||||
def get_last_date_that_has_logs(self, jid, is_room = False):
|
||||
def get_last_date_that_has_logs(self, jid, account = None, is_room = False):
|
||||
'''returns last time (in seconds since EPOCH) for which
|
||||
we had logs (excluding statuses)'''
|
||||
jid = jid.lower()
|
||||
jid_id = self.get_jid_id(jid, 'ROOM')
|
||||
|
||||
where_sql = ''
|
||||
if not is_room:
|
||||
where_sql = self._build_contact_where(account, jid)
|
||||
else:
|
||||
jid_id = self.get_jid_id(jid, 'ROOM')
|
||||
where_sql = 'jid_id = %s' % jid_id
|
||||
self.cur.execute('''
|
||||
SELECT time FROM logs
|
||||
WHERE jid_id = ?
|
||||
AND kind NOT IN (?, ?)
|
||||
WHERE (%s)
|
||||
AND kind NOT IN (%d, %d)
|
||||
ORDER BY time DESC LIMIT 1
|
||||
''', (jid_id, constants.KIND_STATUS, constants.KIND_GCSTATUS))
|
||||
''' % (where_sql, constants.KIND_STATUS, constants.KIND_GCSTATUS))
|
||||
|
||||
results = self.cur.fetchone()
|
||||
if results is not None:
|
||||
|
@ -487,3 +559,61 @@ class Logger:
|
|||
result = None
|
||||
|
||||
return result
|
||||
|
||||
def _build_contact_where(self, account, jid):
|
||||
'''build the where clause for a jid, including metacontacts
|
||||
jid(s) if any'''
|
||||
where_sql = ''
|
||||
# will return empty list if jid is not associated with
|
||||
# any metacontacts
|
||||
family = gajim.contacts.get_metacontacts_family(account, jid)
|
||||
if family:
|
||||
for user in family:
|
||||
jid_id = self.get_jid_id(user['jid'])
|
||||
where_sql += 'jid_id = %s' % jid_id
|
||||
if user != family[-1]:
|
||||
where_sql += ' OR '
|
||||
else: # if jid was not associated with metacontacts
|
||||
jid_id = self.get_jid_id(jid)
|
||||
where_sql = 'jid_id = %s' % jid_id
|
||||
return where_sql
|
||||
|
||||
def save_transport_type(self, jid, type_):
|
||||
'''save the type of the transport in DB'''
|
||||
type_id = self.convert_human_transport_type_to_db_api_values(type_)
|
||||
if not type_id:
|
||||
# unknown type
|
||||
return
|
||||
self.cur.execute(
|
||||
'SELECT type from transports_cache WHERE transport = "%s"' % jid)
|
||||
results = self.cur.fetchall()
|
||||
if results:
|
||||
result = results[0][0]
|
||||
if result == type_id:
|
||||
return
|
||||
self.cur.execute(
|
||||
'UPDATE transports_cache SET type = %d WHERE transport = "%s"' % (type_id,
|
||||
jid))
|
||||
try:
|
||||
self.con.commit()
|
||||
except sqlite.OperationalError, e:
|
||||
print >> sys.stderr, str(e)
|
||||
return
|
||||
self.cur.execute(
|
||||
'INSERT INTO transports_cache VALUES ("%s", %d)' % (jid, type_id))
|
||||
try:
|
||||
self.con.commit()
|
||||
except sqlite.OperationalError, e:
|
||||
print >> sys.stderr, str(e)
|
||||
|
||||
def get_transports_type(self):
|
||||
'''return all the type of the transports in DB'''
|
||||
self.cur.execute(
|
||||
'SELECT * from transports_cache')
|
||||
results = self.cur.fetchall()
|
||||
if not results:
|
||||
return {}
|
||||
answer = {}
|
||||
for result in results:
|
||||
answer[result[0]] = self.convert_api_values_to_human_transport_type(result[1])
|
||||
return answer
|
||||
|
|
|
@ -26,8 +26,6 @@ import os
|
|||
import sys
|
||||
import locale
|
||||
from common import gajim
|
||||
from common import i18n
|
||||
_ = i18n._
|
||||
|
||||
class OptionsParser:
|
||||
def __init__(self, filename):
|
||||
|
@ -129,21 +127,28 @@ class OptionsParser:
|
|||
|
||||
def update_config(self, old_version, new_version):
|
||||
# Convert '0.x.y' to (0, x, y)
|
||||
old_version = old_version.split('.')
|
||||
old_version_list = old_version.split('.')
|
||||
old = []
|
||||
while len(old_version):
|
||||
old.append(int(old_version.pop(0)))
|
||||
new_version = new_version.split('.')
|
||||
while len(old_version_list):
|
||||
old.append(int(old_version_list.pop(0)))
|
||||
new_version_list = new_version.split('.')
|
||||
new = []
|
||||
while len(new_version):
|
||||
new.append(int(new_version.pop(0)))
|
||||
while len(new_version_list):
|
||||
new.append(int(new_version_list.pop(0)))
|
||||
|
||||
if old < [0, 9] and new >= [0, 9]:
|
||||
self.update_config_x_to_09()
|
||||
if old < [0, 10] and new >= [0, 10]:
|
||||
self.update_config_09_to_010()
|
||||
if old < [0, 10, 0, 1] and new >= [0, 10, 0, 1]:
|
||||
self.update_config_to_01001()
|
||||
if old < [0, 10, 1, 1] and new >= [0, 10, 1, 1]:
|
||||
self.update_config_to_01011()
|
||||
if old < [0, 10, 1, 2] and new >= [0, 10, 1, 2]:
|
||||
self.update_config_to_01012()
|
||||
if old < [0, 10, 1, 3] and new >= [0, 10, 1, 3]:
|
||||
self.update_config_to_01013()
|
||||
|
||||
gajim.logger.init_vars()
|
||||
gajim.config.set('version', new_version)
|
||||
|
||||
def update_config_x_to_09(self):
|
||||
# Var name that changed:
|
||||
|
@ -258,6 +263,41 @@ class OptionsParser:
|
|||
|
||||
gajim.config.set('version', '0.10')
|
||||
|
||||
def update_config_to_01001(self):
|
||||
gajim.config.set('print_status_in_muc', 'in_and_out')
|
||||
gajim.config.set('version', '0.10.0.1')
|
||||
def update_config_to_01011(self):
|
||||
if self.old_values.has_key('print_status_in_muc') and \
|
||||
self.old_values['print_status_in_muc'] in (True, False):
|
||||
gajim.config.set('print_status_in_muc', 'in_and_out')
|
||||
gajim.config.set('version', '0.10.1.1')
|
||||
|
||||
def update_config_to_01012(self):
|
||||
# See [6456]
|
||||
if self.old_values.has_key('emoticons_theme') and \
|
||||
self.old_values['emoticons_theme'] == 'Disabled':
|
||||
gajim.config.set('emoticons_theme', '')
|
||||
gajim.config.set('version', '0.10.1.2')
|
||||
|
||||
def update_config_to_01013(self):
|
||||
'''create table transports_cache if there is no such table'''
|
||||
import exceptions
|
||||
try:
|
||||
from pysqlite2 import dbapi2 as sqlite
|
||||
except ImportError:
|
||||
raise exceptions.PysqliteNotAvailable
|
||||
import logger
|
||||
|
||||
con = sqlite.connect(logger.LOG_DB_PATH)
|
||||
cur = con.cursor()
|
||||
try:
|
||||
cur.executescript(
|
||||
'''
|
||||
CREATE TABLE transports_cache (
|
||||
transport TEXT UNIQUE,
|
||||
type INTEGER
|
||||
);
|
||||
'''
|
||||
)
|
||||
con.commit()
|
||||
except sqlite.OperationalError, e:
|
||||
pass
|
||||
con.close()
|
||||
gajim.config.set('version', '0.10.1.3')
|
||||
|
|
|
@ -186,6 +186,9 @@ class HostTester(Socks5, IdleObject):
|
|||
|
||||
def connect(self):
|
||||
''' create the socket and plug it to the idlequeue '''
|
||||
if self.host is None:
|
||||
self.on_failure()
|
||||
return None
|
||||
self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self._sock.setblocking(False)
|
||||
self.fd = self._sock.fileno()
|
||||
|
|
|
@ -32,6 +32,7 @@ import os
|
|||
import struct
|
||||
import sha
|
||||
import time
|
||||
from dialogs import BindPortError
|
||||
|
||||
from errno import EWOULDBLOCK
|
||||
from errno import ENOBUFS
|
||||
|
@ -84,12 +85,9 @@ class SocksQueue:
|
|||
self.listener.bind()
|
||||
if self.listener.started is False:
|
||||
self.listener = None
|
||||
import sys
|
||||
print >> sys.stderr, '================================================='
|
||||
print >> sys.stderr, 'Unable to bind to port %s.' % port
|
||||
print >> sys.stderr, 'Maybe you have another running instance of Gajim.'
|
||||
print >> sys.stderr, 'File Transfer will be canceled.'
|
||||
print >> sys.stderr, '================================================='
|
||||
# We cannot bind port, call error
|
||||
# dialog from dialogs.py and fail
|
||||
BindPortError(port)
|
||||
return None
|
||||
self.connected += 1
|
||||
return self.listener
|
||||
|
@ -352,7 +350,10 @@ class SocksQueue:
|
|||
class Socks5:
|
||||
def __init__(self, idlequeue, host, port, initiator, target, sid):
|
||||
if host is not None:
|
||||
self.host = socket.gethostbyname(host)
|
||||
try:
|
||||
self.host = socket.gethostbyname(host)
|
||||
except socket.gaierror:
|
||||
self.host = None
|
||||
self.idlequeue = idlequeue
|
||||
self.fd = -1
|
||||
self.port = port
|
||||
|
|
|
@ -126,12 +126,11 @@ class NBCommonClient(CommonClient):
|
|||
|
||||
def _on_connected(self):
|
||||
self.connected = 'tcp'
|
||||
if (self._Ssl is None and self.Connection.getPort() in (5223, 443)) or self._Ssl:
|
||||
try:
|
||||
transports_nb.NonBlockingTLS().PlugIn(self, now=1)
|
||||
self.connected = 'ssl'
|
||||
except socket.sslerror:
|
||||
if self._Ssl:
|
||||
transports_nb.NonBlockingTLS().PlugIn(self, now=1)
|
||||
if not self.Connection: # ssl error, stream is closed
|
||||
return
|
||||
self.connected = 'ssl'
|
||||
self.onreceive(self._on_receive_document_attrs)
|
||||
dispatcher_nb.Dispatcher().PlugIn(self)
|
||||
|
||||
|
@ -194,6 +193,8 @@ class NonBlockingClient(NBCommonClient):
|
|||
self.isplugged = True
|
||||
self.onreceive(None)
|
||||
transports_nb.NonBlockingTLS().PlugIn(self)
|
||||
if not self.Connection: # ssl error, stream is closed
|
||||
return True
|
||||
if not self.Dispatcher.Stream._document_attrs.has_key('version') or \
|
||||
not self.Dispatcher.Stream._document_attrs['version']=='1.0':
|
||||
self._is_connected()
|
||||
|
|
|
@ -134,6 +134,7 @@ class Dispatcher(PlugIn):
|
|||
return 0
|
||||
except ExpatError:
|
||||
sys.exc_clear()
|
||||
self.DEBUG('Invalid XML received from server. Forcing disconnect.')
|
||||
self._owner.Connection.pollend()
|
||||
return 0
|
||||
if len(self._pendingExceptions) > 0:
|
||||
|
|
|
@ -27,6 +27,9 @@ All these methods takes 'disp' first argument that should be already connected
|
|||
from protocol import *
|
||||
|
||||
REGISTER_DATA_RECEIVED='REGISTER DATA RECEIVED'
|
||||
PRIVACY_LISTS_RECEIVED='PRIVACY LISTS RECEIVED'
|
||||
PRIVACY_LIST_RECEIVED='PRIVACY LIST RECEIVED'
|
||||
PRIVACY_LISTS_ACTIVE_DEFAULT='PRIVACY LISTS ACTIVE DEFAULT'
|
||||
|
||||
### DISCO ### http://jabber.org/protocol/disco ### JEP-0030 ####################
|
||||
### Browse ### jabber:iq:browse ### JEP-0030 ###################################
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
# $Id: features.py,v 1.22 2005/09/30 20:13:04 mikealbon Exp $
|
||||
|
||||
from features import REGISTER_DATA_RECEIVED
|
||||
from features import REGISTER_DATA_RECEIVED, PRIVACY_LISTS_RECEIVED, PRIVACY_LIST_RECEIVED, PRIVACY_LISTS_ACTIVE_DEFAULT
|
||||
from protocol import *
|
||||
|
||||
def _on_default_response(disp, iq, cb):
|
||||
|
@ -146,9 +146,9 @@ def register(disp, host, info, cb):
|
|||
attributes lastErrNode, lastErr and lastErrCode.
|
||||
"""
|
||||
iq=Iq('set', NS_REGISTER, to=host)
|
||||
if not isinstance(info, dict):
|
||||
if not isinstance(info, dict):
|
||||
info=info.asDict()
|
||||
for i in info.keys():
|
||||
for i in info.keys():
|
||||
iq.setTag('query').setTagData(i,info[i])
|
||||
disp.SendAndCallForResponse(iq, cb)
|
||||
|
||||
|
@ -172,37 +172,46 @@ def changePasswordTo(disp, newpassword, host=None, cb = None):
|
|||
#type=[jid|group|subscription]
|
||||
#action=[allow|deny]
|
||||
|
||||
def getPrivacyLists(disp, cb):
|
||||
def getPrivacyLists(disp):
|
||||
""" Requests privacy lists from connected server.
|
||||
Returns dictionary of existing lists on success."""
|
||||
iq = Iq('get', NS_PRIVACY)
|
||||
def _on_response(resp):
|
||||
dict = {'lists': []}
|
||||
try:
|
||||
if not isResultNode(resp):
|
||||
cb(False)
|
||||
return
|
||||
for list in resp.getQueryPayload():
|
||||
if list.getName()=='list':
|
||||
dict['lists'].append(list.getAttr('name'))
|
||||
else:
|
||||
dict[list.getName()]=list.getAttr('name')
|
||||
cb(dict)
|
||||
except:
|
||||
pass
|
||||
cb(False)
|
||||
disp.SendAndCallForResponse(iq, _on_respons)
|
||||
if not isResultNode(resp):
|
||||
disp.Event(NS_PRIVACY, PRIVACY_LISTS_RECEIVED, (False))
|
||||
return
|
||||
for list in resp.getQueryPayload():
|
||||
if list.getName()=='list':
|
||||
dict['lists'].append(list.getAttr('name'))
|
||||
else:
|
||||
dict[list.getName()]=list.getAttr('name')
|
||||
disp.Event(NS_PRIVACY, PRIVACY_LISTS_RECEIVED, (dict))
|
||||
disp.SendAndCallForResponse(iq, _on_response)
|
||||
|
||||
def getPrivacyList(disp, listname, cb):
|
||||
def getActiveAndDefaultPrivacyLists(disp):
|
||||
iq = Iq('get', NS_PRIVACY)
|
||||
def _on_response(resp):
|
||||
dict = {'active': '', 'default': ''}
|
||||
if not isResultNode(resp):
|
||||
disp.Event(NS_PRIVACY, PRIVACY_LISTS_ACTIVE_DEFAULT, (False))
|
||||
return
|
||||
for list in resp.getQueryPayload():
|
||||
if list.getName() == 'active':
|
||||
dict['active'] = list.getAttr('name')
|
||||
elif list.getName() == 'default':
|
||||
dict['default'] = list.getAttr('name')
|
||||
disp.Event(NS_PRIVACY, PRIVACY_LISTS_ACTIVE_DEFAULT, (dict))
|
||||
disp.SendAndCallForResponse(iq, _on_response)
|
||||
|
||||
def getPrivacyList(disp, listname):
|
||||
""" Requests specific privacy list listname. Returns list of XML nodes (rules)
|
||||
taken from the server responce."""
|
||||
def _on_response(resp):
|
||||
try:
|
||||
if isResultNode(resp):
|
||||
return cb(resp.getQueryPayload()[0])
|
||||
except:
|
||||
pass
|
||||
cb(False)
|
||||
if not isResultNode(resp):
|
||||
disp.Event(NS_PRIVACY, PRIVACY_LIST_RECEIVED, (False))
|
||||
return
|
||||
disp.Event(NS_PRIVACY, PRIVACY_LIST_RECEIVED, (resp))
|
||||
iq = Iq('get', NS_PRIVACY, payload=[Node('list', {'name': listname})])
|
||||
disp.SendAndCallForResponse(iq, _on_response)
|
||||
|
||||
|
@ -220,14 +229,25 @@ def setDefaultPrivacyList(disp, listname=None):
|
|||
""" Sets the default privacy list as 'listname'. Returns true on success."""
|
||||
return setActivePrivacyList(disp, listname,'default')
|
||||
|
||||
def setPrivacyList(disp, list, cb):
|
||||
def setPrivacyList(disp, listname, tags):
|
||||
""" Set the ruleset. 'list' should be the simpleXML node formatted
|
||||
according to RFC 3921 (XMPP-IM) (I.e. Node('list',{'name':listname},payload=[...]) )
|
||||
Returns true on success."""
|
||||
iq=Iq('set',NS_PRIVACY,payload=[list])
|
||||
_on_default_response(disp, iq, cb)
|
||||
iq = Iq('set', NS_PRIVACY, xmlns = '')
|
||||
list_query = iq.getTag('query').setTag('list', {'name': listname})
|
||||
for item in tags:
|
||||
if item.has_key('type') and item.has_key('value'):
|
||||
item_tag = list_query.setTag('item', {'action': item['action'],
|
||||
'order': item['order'], 'type': item['type'], 'value': item['value']})
|
||||
else:
|
||||
item_tag = list_query.setTag('item', {'action': item['action'],
|
||||
'order': item['order']})
|
||||
if item.has_key('child'):
|
||||
for child_tag in item['child']:
|
||||
item_tag.setTag(child_tag)
|
||||
_on_default_response(disp, iq, None)
|
||||
|
||||
def delPrivacyList(disp,listname, cb):
|
||||
def delPrivacyList(disp,listname):
|
||||
""" Deletes privacy list 'listname'. Returns true on success."""
|
||||
iq = Iq('set',NS_PRIVACY,payload=[Node('list',{'name':listname})])
|
||||
_on_default_response(disp, iq, cb)
|
||||
_on_default_response(disp, iq, None)
|
||||
|
|
|
@ -62,11 +62,13 @@ NS_MUC ='http://jabber.org/protocol/muc'
|
|||
NS_MUC_USER =NS_MUC+'#user'
|
||||
NS_MUC_ADMIN =NS_MUC+'#admin'
|
||||
NS_MUC_OWNER =NS_MUC+'#owner'
|
||||
NS_NICK ='http://jabber.org/protocol/nick' # JEP-0172
|
||||
NS_OFFLINE ='http://www.jabber.org/jeps/jep-0030.html' # JEP-0013
|
||||
NS_PHYSLOC ='http://jabber.org/protocol/physloc' # JEP-0112
|
||||
NS_PRESENCE ='presence' # Jabberd2
|
||||
NS_PRIVACY ='jabber:iq:privacy'
|
||||
NS_PRIVATE ='jabber:iq:private'
|
||||
NS_PROFILE ='http://jabber.org/protocol/profile' # JEP-0154
|
||||
NS_PUBSUB ='http://jabber.org/protocol/pubsub' # JEP-0060
|
||||
NS_REGISTER ='jabber:iq:register'
|
||||
NS_ROSTER ='jabber:iq:roster'
|
||||
|
@ -82,7 +84,7 @@ NS_SIGNED ='jabber:x:signed' # JEP-00
|
|||
NS_STANZAS ='urn:ietf:params:xml:ns:xmpp-stanzas'
|
||||
NS_STREAM ='http://affinix.com/jabber/stream'
|
||||
NS_STREAMS ='http://etherx.jabber.org/streams'
|
||||
NS_TIME ='jabber:iq:time'
|
||||
NS_TIME ='jabber:iq:time' # JEP-0900
|
||||
NS_TLS ='urn:ietf:params:xml:ns:xmpp-tls'
|
||||
NS_VACATION ='http://jabber.org/protocol/vacation'
|
||||
NS_VCARD ='vcard-temp'
|
||||
|
@ -346,6 +348,13 @@ class Protocol(Node):
|
|||
for tag in errtag.getChildren():
|
||||
if tag.getName()<>'text': return tag.getName()
|
||||
return errtag.getData()
|
||||
def getErrorMsg(self):
|
||||
""" Return the textual description of the error (if present) or the error condition """
|
||||
errtag=self.getTag('error')
|
||||
if errtag:
|
||||
for tag in errtag.getChildren():
|
||||
if tag.getName()=='text': return tag.getData()
|
||||
return self.getError()
|
||||
def getErrorCode(self):
|
||||
""" Return the error code. Obsolette. """
|
||||
return self.getTagAttr('error','code')
|
||||
|
|
|
@ -371,8 +371,12 @@ class NonBlockingTLS(PlugIn):
|
|||
PlugIn.PlugIn(self, owner)
|
||||
DBG_LINE='NonBlockingTLS'
|
||||
self.on_tls_start = on_tls_start
|
||||
if now:
|
||||
res = self._startSSL()
|
||||
if now:
|
||||
try:
|
||||
res = self._startSSL()
|
||||
except Exception, e:
|
||||
self._owner.socket.pollend()
|
||||
return
|
||||
self.tls_start()
|
||||
return res
|
||||
if self._owner.Dispatcher.Stream.features:
|
||||
|
@ -434,7 +438,11 @@ class NonBlockingTLS(PlugIn):
|
|||
self.DEBUG('Got starttls response: ' + self.starttls,'error')
|
||||
return
|
||||
self.DEBUG('Got starttls proceed response. Switching to TLS/SSL...','ok')
|
||||
self._startSSL()
|
||||
try:
|
||||
self._startSSL()
|
||||
except Exception, e:
|
||||
self._owner.socket.pollend()
|
||||
return
|
||||
self._owner.Dispatcher.PlugOut()
|
||||
dispatcher_nb.Dispatcher().PlugIn(self._owner)
|
||||
|
||||
|
|
|
@ -33,8 +33,6 @@ import common.xmpp
|
|||
from common import GnuPG
|
||||
from common import helpers
|
||||
from common import gajim
|
||||
from common import i18n
|
||||
_ = i18n._
|
||||
|
||||
STATUS_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd',
|
||||
'invisible']
|
||||
|
|
|
@ -48,9 +48,6 @@ from connection_handlers_zeroconf import *
|
|||
|
||||
USE_GPG = GnuPG.USE_GPG
|
||||
|
||||
from common import i18n
|
||||
_ = i18n._
|
||||
|
||||
class ConnectionZeroconf(ConnectionHandlersZeroconf):
|
||||
'''Connection class'''
|
||||
def __init__(self, name):
|
||||
|
|
243
src/config.py
243
src/config.py
|
@ -16,7 +16,6 @@
|
|||
##
|
||||
|
||||
import gtk
|
||||
import gtk.glade
|
||||
import gobject
|
||||
import os
|
||||
import common.config
|
||||
|
@ -38,12 +37,6 @@ except:
|
|||
from common import helpers
|
||||
from common import gajim
|
||||
from common import connection
|
||||
from common import i18n
|
||||
|
||||
_ = i18n._
|
||||
APP = i18n.APP
|
||||
gtk.glade.bindtextdomain (APP, i18n.DIR)
|
||||
gtk.glade.textdomain (APP)
|
||||
|
||||
#---------- PreferencesWindow class -------------#
|
||||
class PreferencesWindow:
|
||||
|
@ -60,6 +53,7 @@ class PreferencesWindow:
|
|||
'''Initialize Preferences window'''
|
||||
self.xml = gtkgui_helpers.get_glade('preferences_window.glade')
|
||||
self.window = self.xml.get_widget('preferences_window')
|
||||
self.window.set_transient_for(gajim.interface.roster.window)
|
||||
self.iconset_combobox = self.xml.get_widget('iconset_combobox')
|
||||
self.notify_on_new_message_radiobutton = self.xml.get_widget(
|
||||
'notify_on_new_message_radiobutton')
|
||||
|
@ -124,7 +118,7 @@ class PreferencesWindow:
|
|||
for dir in emoticons_list:
|
||||
if dir != '.svn':
|
||||
l.append(dir)
|
||||
l.append('Disabled')
|
||||
l.append(_('Disabled'))
|
||||
for i in xrange(len(l)):
|
||||
model.append([l[i]])
|
||||
if gajim.config.get('emoticons_theme') == l[i]:
|
||||
|
@ -256,11 +250,6 @@ class PreferencesWindow:
|
|||
# try to set default font for the current desktop env
|
||||
fontbutton = self.xml.get_widget('conversation_fontbutton')
|
||||
if font == '':
|
||||
font = gtkgui_helpers.get_default_font()
|
||||
if font is not None:
|
||||
font = 'Sans 10'
|
||||
gajim.config.set('conversation_font', font)
|
||||
fontbutton.set_font_name(font)
|
||||
fontbutton.set_sensitive(False)
|
||||
self.xml.get_widget('default_chat_font').set_active(True)
|
||||
else:
|
||||
|
@ -322,6 +311,8 @@ class PreferencesWindow:
|
|||
commands = ('aplay', 'play', 'esdplay', 'artsplay')
|
||||
for command in commands:
|
||||
if helpers.is_in_path(command):
|
||||
if command == 'aplay':
|
||||
command += ' -q'
|
||||
self.xml.get_widget('soundplayer_entry').set_text(command)
|
||||
gajim.config.set('soundplayer', command)
|
||||
break
|
||||
|
@ -385,6 +376,32 @@ class PreferencesWindow:
|
|||
self.xml.get_widget('prompt_offline_status_message_checkbutton').\
|
||||
set_active(st)
|
||||
|
||||
# Default Status messages
|
||||
self.default_msg_tree = self.xml.get_widget('default_msg_treeview')
|
||||
# (status, translated_status, message, enabled)
|
||||
model = gtk.ListStore(str, str, str, bool)
|
||||
self.default_msg_tree.set_model(model)
|
||||
col = gtk.TreeViewColumn('Status')
|
||||
self.default_msg_tree.append_column(col)
|
||||
renderer = gtk.CellRendererText()
|
||||
col.pack_start(renderer, False)
|
||||
col.set_attributes(renderer, text = 1)
|
||||
col = gtk.TreeViewColumn('Message')
|
||||
self.default_msg_tree.append_column(col)
|
||||
renderer = gtk.CellRendererText()
|
||||
col.pack_start(renderer, True)
|
||||
col.set_attributes(renderer, text = 2)
|
||||
renderer.connect('edited', self.on_default_msg_cell_edited)
|
||||
renderer.set_property('editable', True)
|
||||
col = gtk.TreeViewColumn('Enabled')
|
||||
self.default_msg_tree.append_column(col)
|
||||
renderer = gtk.CellRendererToggle()
|
||||
col.pack_start(renderer, False)
|
||||
col.set_attributes(renderer, active = 3)
|
||||
renderer.set_property('activatable', True)
|
||||
renderer.connect('toggled', self.default_msg_toggled_cb)
|
||||
self.fill_default_msg_treeview()
|
||||
|
||||
#Status messages
|
||||
self.msg_tree = self.xml.get_widget('msg_treeview')
|
||||
model = gtk.ListStore(str, str)
|
||||
|
@ -440,17 +457,22 @@ class PreferencesWindow:
|
|||
|
||||
# Notify user of new gmail e-mail messages,
|
||||
# only show checkbox if user has a gtalk account
|
||||
frame_gmail = self.xml.get_widget('frame_gmail')
|
||||
notify_gmail_checkbutton = self.xml.get_widget('notify_gmail_checkbutton')
|
||||
notify_gmail_checkbutton.set_no_show_all(True)
|
||||
notify_gmail_extra_checkbutton = self.xml.get_widget('notify_gmail_extra_checkbutton')
|
||||
frame_gmail.set_no_show_all(True)
|
||||
|
||||
for account in gajim.config.get_per('accounts'):
|
||||
jid = gajim.get_jid_from_account(account)
|
||||
if gajim.get_server_from_jid(jid) == 'gmail.com':
|
||||
if gajim.get_server_from_jid(jid) in gajim.gmail_domains:
|
||||
frame_gmail.show_all()
|
||||
st = gajim.config.get('notify_on_new_gmail_email')
|
||||
notify_gmail_checkbutton.set_active(st)
|
||||
notify_gmail_checkbutton.show()
|
||||
st = gajim.config.get('notify_on_new_gmail_email_extra')
|
||||
notify_gmail_extra_checkbutton.set_active(st)
|
||||
break
|
||||
else:
|
||||
notify_gmail_checkbutton.hide()
|
||||
frame_gmail.hide()
|
||||
|
||||
self.xml.signal_autoconnect(self)
|
||||
|
||||
|
@ -460,11 +482,14 @@ class PreferencesWindow:
|
|||
self.on_msg_treemodel_row_changed)
|
||||
self.msg_tree.get_model().connect('row-deleted',
|
||||
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)
|
||||
self.window.show_all()
|
||||
gtkgui_helpers.possibly_move_window_in_current_desktop(self.window)
|
||||
|
||||
def on_preferences_window_key_press_event(self, widget, event):
|
||||
if event.keyval == gtk.keysyms.Escape:
|
||||
|
@ -595,7 +620,23 @@ class PreferencesWindow:
|
|||
gajim.config.set('use_speller', active)
|
||||
gajim.interface.save_config()
|
||||
if active:
|
||||
self.apply_speller()
|
||||
lang = gajim.config.get('speller_language')
|
||||
if not lang:
|
||||
lang = gajim.LANG
|
||||
tv = gtk.TextView()
|
||||
try:
|
||||
spell = gtkspell.Spell(tv, lang)
|
||||
except:
|
||||
dialogs.ErrorDialog(
|
||||
_('Dictionary for lang %s not available') % lang,
|
||||
_('You have to install %s dictionary to use spellchecking, or '
|
||||
'choose another language by setting the speller_language option.'
|
||||
) % lang)
|
||||
gajim.config.set('use_speller', False)
|
||||
widget.set_active(False)
|
||||
else:
|
||||
gajim.config.set('speller_language', lang)
|
||||
self.apply_speller()
|
||||
else:
|
||||
self.remove_speller()
|
||||
|
||||
|
@ -792,6 +833,36 @@ class PreferencesWindow:
|
|||
def on_auto_xa_message_entry_changed(self, widget):
|
||||
gajim.config.set('autoxa_message', widget.get_text().decode('utf-8'))
|
||||
|
||||
def fill_default_msg_treeview(self):
|
||||
model = self.default_msg_tree.get_model()
|
||||
model.clear()
|
||||
status = []
|
||||
for status_ in gajim.config.get_per('defaultstatusmsg'):
|
||||
status.append(status_)
|
||||
status.sort()
|
||||
for status_ in status:
|
||||
msg = gajim.config.get_per('defaultstatusmsg', status_, 'message')
|
||||
enabled = gajim.config.get_per('defaultstatusmsg', status_, 'enabled')
|
||||
iter = model.append()
|
||||
uf_show = helpers.get_uf_show(status_)
|
||||
model.set(iter, 0, status_, 1, uf_show, 2, msg, 3, enabled)
|
||||
|
||||
def on_default_msg_cell_edited(self, cell, row, new_text):
|
||||
model = self.default_msg_tree.get_model()
|
||||
iter = model.get_iter_from_string(row)
|
||||
model.set_value(iter, 2, new_text)
|
||||
|
||||
def default_msg_toggled_cb(self, cell, path):
|
||||
model = self.default_msg_tree.get_model()
|
||||
model[path][3] = not model[path][3]
|
||||
|
||||
def on_default_msg_treemodel_row_changed(self, model, path, iter):
|
||||
status = model[iter][0]
|
||||
message = model[iter][2].decode('utf-8')
|
||||
gajim.config.set_per('defaultstatusmsg', status, 'enabled',
|
||||
model[iter][3])
|
||||
gajim.config.set_per('defaultstatusmsg', status, 'message', message)
|
||||
|
||||
def save_status_messages(self, model):
|
||||
for msg in gajim.config.get_per('statusmsg'):
|
||||
gajim.config.del_per('statusmsg', msg)
|
||||
|
@ -846,18 +917,25 @@ class PreferencesWindow:
|
|||
def on_send_os_info_checkbutton_toggled(self, widget):
|
||||
self.on_checkbutton_toggled(widget, 'send_os_info')
|
||||
|
||||
def on_notify_gmail_checkbutton_toggled(self, widget):
|
||||
def on_notify_gmail_checkbutton_toggled(self, widget):
|
||||
self.on_checkbutton_toggled(widget, 'notify_on_new_gmail_email')
|
||||
|
||||
def on_notify_gmail_extra_checkbutton_toggled(self, widget):
|
||||
self.on_checkbutton_toggled(widget, 'notify_on_new_gmail_email_extra')
|
||||
|
||||
def fill_msg_treeview(self):
|
||||
self.xml.get_widget('delete_msg_button').set_sensitive(False)
|
||||
model = self.msg_tree.get_model()
|
||||
model.clear()
|
||||
for msg in gajim.config.get_per('statusmsg'):
|
||||
preset_status = []
|
||||
for msg_name in gajim.config.get_per('statusmsg'):
|
||||
preset_status.append(msg_name)
|
||||
preset_status.sort()
|
||||
for msg_name in preset_status:
|
||||
msg_text = gajim.config.get_per('statusmsg', msg_name, 'message')
|
||||
msg_text = helpers.from_one_line(msg_text)
|
||||
iter = model.append()
|
||||
val = gajim.config.get_per('statusmsg', msg, 'message')
|
||||
val = helpers.from_one_line(val)
|
||||
model.set(iter, 0, msg, 1, val)
|
||||
model.set(iter, 0, msg_name, 1, msg_text)
|
||||
|
||||
def on_msg_cell_edited(self, cell, row, new_text):
|
||||
model = self.msg_tree.get_model()
|
||||
|
@ -970,7 +1048,6 @@ class PreferencesWindow:
|
|||
path_to_snd_file = widget.get_text()
|
||||
model, iter = self.sound_tree.get_selection().get_selected()
|
||||
model[iter][2] = path_to_snd_file # set new path to sounds_model
|
||||
model[iter][0] = True # set the sound to enabled
|
||||
|
||||
def on_play_button_clicked(self, widget):
|
||||
model, iter = self.sound_tree.get_selection().get_selected()
|
||||
|
@ -1004,6 +1081,7 @@ class AccountModificationWindow:
|
|||
def __init__(self, account):
|
||||
self.xml = gtkgui_helpers.get_glade('account_modification_window.glade')
|
||||
self.window = self.xml.get_widget('account_modification_window')
|
||||
self.window.set_transient_for(gajim.interface.roster.window)
|
||||
self.account = account
|
||||
|
||||
# init proxy list
|
||||
|
@ -1152,7 +1230,7 @@ class AccountModificationWindow:
|
|||
_('You are currently connected to the server'),
|
||||
_('To change the account name, you must be disconnected.'))
|
||||
return
|
||||
if len(gajim.awaiting_events[self.account]):
|
||||
if len(gajim.events.get_events(self.account)):
|
||||
dialogs.ErrorDialog(_('Unread events'),
|
||||
_('To change the account name, you must read all pending '
|
||||
'events.'))
|
||||
|
@ -1261,12 +1339,12 @@ class AccountModificationWindow:
|
|||
if name != self.account:
|
||||
#update variables
|
||||
gajim.interface.instances[name] = gajim.interface.instances[self.account]
|
||||
gajim.awaiting_events[name] = gajim.awaiting_events[self.account]
|
||||
gajim.nicks[name] = gajim.nicks[self.account]
|
||||
gajim.block_signed_in_notifications[name] = \
|
||||
gajim.block_signed_in_notifications[self.account]
|
||||
gajim.groups[name] = gajim.groups[self.account]
|
||||
gajim.gc_connected[name] = gajim.gc_connected[self.account]
|
||||
gajim.automatic_rooms[name] = gajim.automatic_rooms[self.account]
|
||||
gajim.newly_added[name] = gajim.newly_added[self.account]
|
||||
gajim.to_be_removed[name] = gajim.to_be_removed[self.account]
|
||||
gajim.sleeper_state[name] = gajim.sleeper_state[self.account]
|
||||
|
@ -1277,27 +1355,25 @@ class AccountModificationWindow:
|
|||
gajim.status_before_autoaway[self.account]
|
||||
|
||||
gajim.contacts.change_account_name(self.account, name)
|
||||
gajim.events.change_account_name(self.account, name)
|
||||
|
||||
#upgrade account variable in opened windows
|
||||
for kind in ('infos', 'disco', 'chats', 'gc', 'gc_config'):
|
||||
# change account variable for chat / gc controls
|
||||
for ctrl in gajim.interface.msg_win_mgr.get_controls():
|
||||
ctrl.account = name
|
||||
# upgrade account variable in opened windows
|
||||
for kind in ('infos', 'disco', 'gc_config'):
|
||||
for j in gajim.interface.instances[name][kind]:
|
||||
gajim.interface.instances[name][kind][j].account = name
|
||||
|
||||
#upgrade account in systray
|
||||
if gajim.interface.systray_enabled:
|
||||
for list in gajim.interface.systray.jids:
|
||||
if list[0] == self.account:
|
||||
list[0] = name
|
||||
|
||||
# ServiceCache object keep old property account
|
||||
if hasattr(gajim.connections[self.account], 'services_cache'):
|
||||
gajim.connections[self.account].services_cache.account = name
|
||||
del gajim.interface.instances[self.account]
|
||||
del gajim.awaiting_events[self.account]
|
||||
del gajim.nicks[self.account]
|
||||
del gajim.block_signed_in_notifications[self.account]
|
||||
del gajim.groups[self.account]
|
||||
del gajim.gc_connected[self.account]
|
||||
del gajim.automatic_rooms[self.account]
|
||||
del gajim.newly_added[self.account]
|
||||
del gajim.to_be_removed[self.account]
|
||||
del gajim.sleeper_state[self.account]
|
||||
|
@ -1316,11 +1392,13 @@ class AccountModificationWindow:
|
|||
relogin_needed = False
|
||||
else: # we're connected to the account we want to apply changes
|
||||
# check if relogin is needed
|
||||
relogin_needed = self.options_changed_need_relogin(config,
|
||||
relogin_needed = False
|
||||
if self.options_changed_need_relogin(config,
|
||||
('resource', 'proxy', 'usessl', 'keyname',
|
||||
'use_custom_host', 'custom_host'))
|
||||
'use_custom_host', 'custom_host')):
|
||||
relogin_needed = True
|
||||
|
||||
if config['use_custom_host'] and (self.option_changed(config,
|
||||
elif config['use_custom_host'] and (self.option_changed(config,
|
||||
'custom_host') or self.option_changed(config, 'custom_port')):
|
||||
relogin_needed = True
|
||||
|
||||
|
@ -1400,7 +1478,6 @@ class AccountModificationWindow:
|
|||
dialogs.ErrorDialog(_('No such account available'),
|
||||
_('You must create your account before editing your personal information.'))
|
||||
return
|
||||
jid = self.xml.get_widget('jid_entry').get_text().decode('utf-8')
|
||||
|
||||
# show error dialog if account is newly created (not in gajim.connections)
|
||||
if not gajim.connections.has_key(self.account) or \
|
||||
|
@ -1409,12 +1486,12 @@ class AccountModificationWindow:
|
|||
_('Without a connection, you can not edit your personal information.'))
|
||||
return
|
||||
|
||||
# in infos the key jid is OUR jid so we save the vcardwindow instance there
|
||||
if gajim.interface.instances[self.account]['infos'].has_key(jid):
|
||||
gajim.interface.instances[self.account]['infos'][jid].window.present()
|
||||
else:
|
||||
gajim.interface.instances[self.account]['infos'][jid] = \
|
||||
vcard.VcardWindow(jid, self.account, True)
|
||||
if not gajim.connections[self.account].vcard_supported:
|
||||
dialogs.ErrorDialog(_("Your server doesn't support Vcard"),
|
||||
_("Your server can't save your personal information."))
|
||||
return
|
||||
|
||||
gajim.interface.edit_own_details(self.account)
|
||||
|
||||
def on_manage_proxies_button_clicked(self, widget):
|
||||
if gajim.interface.instances.has_key('manage_proxies'):
|
||||
|
@ -1438,7 +1515,7 @@ class AccountModificationWindow:
|
|||
dialogs.ErrorDialog(_('Failed to get secret keys'),
|
||||
_('There was a problem retrieving your OpenPGP secret keys.'))
|
||||
return
|
||||
secret_keys['None'] = 'None'
|
||||
secret_keys[_('None')] = _('None')
|
||||
instance = dialogs.ChooseGPGKeyDialog(_('OpenPGP Key Selection'),
|
||||
_('Choose your OpenPGP key'), secret_keys)
|
||||
keyID = instance.run()
|
||||
|
@ -1447,7 +1524,7 @@ class AccountModificationWindow:
|
|||
checkbutton = self.xml.get_widget('gpg_save_password_checkbutton')
|
||||
gpg_key_label = self.xml.get_widget('gpg_key_label')
|
||||
gpg_name_label = self.xml.get_widget('gpg_name_label')
|
||||
if keyID[0] == 'None':
|
||||
if keyID[0] == _('None'):
|
||||
gpg_key_label.set_text(_('No key selected'))
|
||||
gpg_name_label.set_text('')
|
||||
checkbutton.set_sensitive(False)
|
||||
|
@ -1495,6 +1572,7 @@ class ManageProxiesWindow:
|
|||
def __init__(self):
|
||||
self.xml = gtkgui_helpers.get_glade('manage_proxies_window.glade')
|
||||
self.window = self.xml.get_widget('manage_proxies_window')
|
||||
self.window.set_transient_for(gajim.interface.roster.window)
|
||||
self.proxies_treeview = self.xml.get_widget('proxies_treeview')
|
||||
self.proxyname_entry = self.xml.get_widget('proxyname_entry')
|
||||
self.init_list()
|
||||
|
@ -1659,6 +1737,7 @@ class AccountsWindow:
|
|||
def __init__(self):
|
||||
self.xml = gtkgui_helpers.get_glade('accounts_window.glade')
|
||||
self.window = self.xml.get_widget('accounts_window')
|
||||
self.window.set_transient_for(gajim.interface.roster.window)
|
||||
self.accounts_treeview = self.xml.get_widget('accounts_treeview')
|
||||
self.modify_button = self.xml.get_widget('modify_button')
|
||||
self.remove_button = self.xml.get_widget('remove_button')
|
||||
|
@ -1714,7 +1793,7 @@ class AccountsWindow:
|
|||
if not iter:
|
||||
return
|
||||
account = model.get_value(iter, 0).decode('utf-8')
|
||||
if len(gajim.awaiting_events[account]):
|
||||
if len(gajim.events.get_events(account)):
|
||||
dialogs.ErrorDialog(_('Unread events'),
|
||||
_('Read all pending events before removing this account.'))
|
||||
return
|
||||
|
@ -1761,6 +1840,7 @@ class DataFormWindow:
|
|||
self.config = config
|
||||
self.xml = gtkgui_helpers.get_glade('data_form_window.glade')
|
||||
self.window = self.xml.get_widget('data_form_window')
|
||||
self.window.set_transient_for(gajim.interface.roster.window)
|
||||
self.config_vbox = self.xml.get_widget('config_vbox')
|
||||
if config:
|
||||
self.fill_vbox()
|
||||
|
@ -1910,10 +1990,11 @@ class ServiceRegistrationWindow(DataFormWindow):
|
|||
else:
|
||||
self.xml = gtkgui_helpers.get_glade('service_registration_window.glade')
|
||||
self.window = self.xml.get_widget('service_registration_window')
|
||||
self.window.set_transient_for(gajim.interface.roster.window)
|
||||
if infos.has_key('registered'):
|
||||
self.window.set_title(_('Edit %s' % service))
|
||||
self.window.set_title(_('Edit %s') % service)
|
||||
else:
|
||||
self.window.set_title(_('Register to %s' % service))
|
||||
self.window.set_title(_('Register to %s') % service)
|
||||
self.xml.get_widget('label').set_text(infos['instructions'])
|
||||
self.entries = {}
|
||||
self.draw_table()
|
||||
|
@ -1983,7 +2064,7 @@ class GroupchatConfigWindow(DataFormWindow):
|
|||
self.room_jid = room_jid
|
||||
self.remove_button = {}
|
||||
self.affiliation_treeview = {}
|
||||
self.removed_jid = {}
|
||||
self.list_init = {} # list at the begining
|
||||
ui_list = {'outcast': _('Ban List'),
|
||||
'member': _('Member List'),
|
||||
'owner': _('Owner List'),
|
||||
|
@ -1993,7 +2074,7 @@ class GroupchatConfigWindow(DataFormWindow):
|
|||
add_on_vbox = self.xml.get_widget('add_on_vbox')
|
||||
|
||||
for affiliation in ('outcast', 'member', 'owner', 'admin'):
|
||||
self.removed_jid[affiliation] = []
|
||||
self.list_init[affiliation] = {}
|
||||
hbox = gtk.HBox(spacing = 5)
|
||||
add_on_vbox.pack_start(hbox, False)
|
||||
|
||||
|
@ -2086,8 +2167,6 @@ class GroupchatConfigWindow(DataFormWindow):
|
|||
return
|
||||
model = self.affiliation_treeview[affiliation].get_model()
|
||||
model.append((jid,'', '', ''))
|
||||
if jid in self.removed_jid[affiliation]:
|
||||
self.removed_jid[affiliation].remove(jid)
|
||||
|
||||
def on_remove_button_clicked(self, widget, affiliation):
|
||||
selection = self.affiliation_treeview[affiliation].get_selection()
|
||||
|
@ -2100,7 +2179,6 @@ class GroupchatConfigWindow(DataFormWindow):
|
|||
iter = model.get_iter(path)
|
||||
jid = model[iter][0]
|
||||
model.remove(iter)
|
||||
self.removed_jid[affiliation].append(jid)
|
||||
self.remove_button[affiliation].set_sensitive(False)
|
||||
|
||||
def on_affiliation_treeview_cursor_changed(self, widget, affiliation):
|
||||
|
@ -2108,6 +2186,7 @@ class GroupchatConfigWindow(DataFormWindow):
|
|||
|
||||
def affiliation_list_received(self, affiliation, list):
|
||||
'''Fill the affiliation treeview'''
|
||||
self.list_init[affiliation] = list
|
||||
if not affiliation:
|
||||
return
|
||||
tv = self.affiliation_treeview[affiliation]
|
||||
|
@ -2134,18 +2213,28 @@ class GroupchatConfigWindow(DataFormWindow):
|
|||
self.config)
|
||||
for affiliation in ('outcast', 'member', 'owner', 'admin'):
|
||||
list = {}
|
||||
actual_jid_list = []
|
||||
model = self.affiliation_treeview[affiliation].get_model()
|
||||
iter = model.get_iter_first()
|
||||
# add new jid
|
||||
while iter:
|
||||
jid = model[iter][0].decode('utf-8')
|
||||
list[jid] = {'affiliation': affiliation}
|
||||
if affiliation == 'outcast':
|
||||
list[jid]['reason'] = model[iter][1].decode('utf-8')
|
||||
actual_jid_list.append(jid)
|
||||
if jid not in self.list_init[affiliation] or \
|
||||
(affiliation == 'outcast' and self.list_init[affiliation]\
|
||||
[jid].has_key('reason') and self.list_init[affiliation][jid]\
|
||||
['reason'] != model[iter][1].decode('utf-8')):
|
||||
list[jid] = {'affiliation': affiliation}
|
||||
if affiliation == 'outcast':
|
||||
list[jid]['reason'] = model[iter][1].decode('utf-8')
|
||||
iter = model.iter_next(iter)
|
||||
for jid in self.removed_jid[affiliation]:
|
||||
list[jid] = {'affiliation': 'none'}
|
||||
gajim.connections[self.account].send_gc_affiliation_list(self.room_jid,
|
||||
list)
|
||||
# remove removed one
|
||||
for jid in self.list_init[affiliation]:
|
||||
if jid not in actual_jid_list:
|
||||
list[jid] = {'affiliation': 'none'}
|
||||
if list:
|
||||
gajim.connections[self.account].send_gc_affiliation_list(
|
||||
self.room_jid, list)
|
||||
self.window.destroy()
|
||||
|
||||
#---------- RemoveAccountWindow class -------------#
|
||||
|
@ -2164,8 +2253,9 @@ class RemoveAccountWindow:
|
|||
self.account = account
|
||||
xml = gtkgui_helpers.get_glade('remove_account_window.glade')
|
||||
self.window = xml.get_widget('remove_account_window')
|
||||
self.window.set_transient_for(gajim.interface.roster.window)
|
||||
self.remove_and_unregister_radiobutton = xml.get_widget(
|
||||
'remove_and_unregister_radiobutton')
|
||||
'remove_and_unregister_radiobutton')
|
||||
self.window.set_title(_('Removing %s account') % self.account)
|
||||
xml.signal_autoconnect(self)
|
||||
self.window.show_all()
|
||||
|
@ -2198,7 +2288,7 @@ class RemoveAccountWindow:
|
|||
self.dialog = None
|
||||
if gajim.connections[self.account].connected:
|
||||
self.dialog = dialogs.ConfirmationDialog(
|
||||
_('Account "%s" is connected to the server' % self.account),
|
||||
_('Account "%s" is connected to the server') % self.account,
|
||||
_('If you remove it, the connection will be lost.'),
|
||||
on_response_ok = remove)
|
||||
else:
|
||||
|
@ -2210,18 +2300,18 @@ class RemoveAccountWindow:
|
|||
if not res:
|
||||
return
|
||||
# Close all opened windows
|
||||
gajim.interface.roster.close_all(gajim.interface.instances[self.account])
|
||||
gajim.interface.roster.close_all(self.account)
|
||||
gajim.connections[self.account].disconnect(on_purpose = True)
|
||||
del gajim.connections[self.account]
|
||||
gajim.config.del_per('accounts', self.account)
|
||||
gajim.interface.save_config()
|
||||
del gajim.interface.instances[self.account]
|
||||
del gajim.awaiting_events[self.account]
|
||||
del gajim.nicks[self.account]
|
||||
del gajim.block_signed_in_notifications[self.account]
|
||||
del gajim.groups[self.account]
|
||||
gajim.contacts.remove_account(self.account)
|
||||
del gajim.gc_connected[self.account]
|
||||
del gajim.automatic_rooms[self.account]
|
||||
del gajim.to_be_removed[self.account]
|
||||
del gajim.newly_added[self.account]
|
||||
del gajim.sleeper_state[self.account]
|
||||
|
@ -2243,6 +2333,7 @@ class ManageBookmarksWindow:
|
|||
def __init__(self):
|
||||
self.xml = gtkgui_helpers.get_glade('manage_bookmarks_window.glade')
|
||||
self.window = self.xml.get_widget('manage_bookmarks_window')
|
||||
self.window.set_transient_for(gajim.interface.roster.window)
|
||||
|
||||
#Account-JID, RoomName, Room-JID, Autojoin, Passowrd, Nick, Show_Status
|
||||
self.treestore = gtk.TreeStore(str, str, str, bool, str, str, str)
|
||||
|
@ -2541,8 +2632,11 @@ class AccountCreationWizardWindow:
|
|||
|
||||
# Connect events from comboboxentry.child
|
||||
server_comboboxentry = self.xml.get_widget('server_comboboxentry')
|
||||
server_comboboxentry.child.connect('key_press_event',
|
||||
self.on_server_comboboxentry_key_press_event)
|
||||
entry = server_comboboxentry.child
|
||||
entry.connect('key_press_event',
|
||||
self.on_server_comboboxentry_key_press_event)
|
||||
completion = gtk.EntryCompletion()
|
||||
entry.set_completion(completion)
|
||||
|
||||
# parse servers.xml
|
||||
servers_xml = os.path.join(gajim.DATA_DIR, 'other', 'servers.xml')
|
||||
|
@ -2551,6 +2645,9 @@ class AccountCreationWizardWindow:
|
|||
for server in servers:
|
||||
servers_model.append((str(server[0]), int(server[1])))
|
||||
|
||||
completion.set_model(servers_model)
|
||||
completion.set_text_column(0)
|
||||
|
||||
# Put servers into comboboxentries
|
||||
server_comboboxentry.set_model(servers_model)
|
||||
server_comboboxentry.set_text_column(0)
|
||||
|
@ -2563,7 +2660,8 @@ class AccountCreationWizardWindow:
|
|||
self.finish_button = self.xml.get_widget('finish_button')
|
||||
self.advanced_button = self.xml.get_widget('advanced_button')
|
||||
self.finish_label = self.xml.get_widget('finish_label')
|
||||
self.go_online_checkbutton = self.xml.get_widget('go_online_checkbutton')
|
||||
self.go_online_checkbutton = self.xml.get_widget(
|
||||
'go_online_checkbutton')
|
||||
self.show_vcard_checkbutton = self.xml.get_widget(
|
||||
'show_vcard_checkbutton')
|
||||
self.progressbar = self.xml.get_widget('progressbar')
|
||||
|
@ -2581,7 +2679,8 @@ class AccountCreationWizardWindow:
|
|||
del gajim.interface.instances['account_creation_wizard']
|
||||
|
||||
def on_register_server_features_button_clicked(self, widget):
|
||||
helpers.launch_browser_mailer('url', 'http://www.jabber.org/network/oldnetwork.shtml')
|
||||
helpers.launch_browser_mailer('url',
|
||||
'http://www.jabber.org/network/oldnetwork.shtml')
|
||||
|
||||
def on_save_password_checkbutton_toggled(self, widget):
|
||||
self.xml.get_widget('pass1_entry').grab_focus()
|
||||
|
@ -2837,12 +2936,12 @@ _('You can set advanced account options by pressing Advanced button, or later by
|
|||
|
||||
# update variables
|
||||
gajim.interface.instances[self.account] = {'infos': {}, 'disco': {},
|
||||
'chats': {}, 'gc': {}, 'gc_config': {}}
|
||||
gajim.awaiting_events[self.account] = {}
|
||||
'gc_config': {}}
|
||||
gajim.connections[self.account].connected = 0
|
||||
gajim.groups[self.account] = {}
|
||||
gajim.contacts.add_account(self.account)
|
||||
gajim.gc_connected[self.account] = {}
|
||||
gajim.automatic_rooms[self.account] = {}
|
||||
gajim.newly_added[self.account] = []
|
||||
gajim.to_be_removed[self.account] = []
|
||||
gajim.nicks[self.account] = config['name']
|
||||
|
|
|
@ -24,11 +24,11 @@
|
|||
##
|
||||
|
||||
import gtk
|
||||
import gtk.glade
|
||||
import pango
|
||||
import gobject
|
||||
import time
|
||||
import sys
|
||||
import os
|
||||
import tooltips
|
||||
import dialogs
|
||||
import locale
|
||||
|
@ -36,13 +36,8 @@ import locale
|
|||
import gtkgui_helpers
|
||||
from common import gajim
|
||||
from common import helpers
|
||||
from common import i18n
|
||||
from calendar import timegm
|
||||
|
||||
_ = i18n._
|
||||
APP = i18n.APP
|
||||
gtk.glade.bindtextdomain(APP, i18n.DIR)
|
||||
gtk.glade.textdomain(APP)
|
||||
from common.fuzzyclock import FuzzyClock
|
||||
|
||||
class ConversationTextview:
|
||||
'''Class for the conversation textview (where user reads already said messages)
|
||||
|
@ -56,7 +51,7 @@ class ConversationTextview:
|
|||
self.tv.set_accepts_tab(True)
|
||||
self.tv.set_editable(False)
|
||||
self.tv.set_cursor_visible(False)
|
||||
self.tv.set_wrap_mode(gtk.WRAP_WORD)
|
||||
self.tv.set_wrap_mode(gtk.WRAP_WORD_CHAR)
|
||||
self.tv.set_left_margin(2)
|
||||
self.tv.set_right_margin(2)
|
||||
self.handlers = {}
|
||||
|
@ -89,6 +84,14 @@ class ConversationTextview:
|
|||
color = gajim.config.get('statusmsgcolor')
|
||||
self.tagStatus.set_property('foreground', color)
|
||||
|
||||
colors = gajim.config.get('gc_nicknames_colors')
|
||||
colors = colors.split(':')
|
||||
for color in xrange(len(colors)):
|
||||
tagname = 'gc_nickname_color_' + str(color)
|
||||
tag = buffer.create_tag(tagname)
|
||||
color = colors[color]
|
||||
tag.set_property('foreground', color)
|
||||
|
||||
tag = buffer.create_tag('marked')
|
||||
color = gajim.config.get('markedmsgcolor')
|
||||
tag.set_property('foreground', color)
|
||||
|
@ -130,6 +133,10 @@ class ConversationTextview:
|
|||
|
||||
buffer.create_tag('focus-out-line', justification = gtk.JUSTIFY_CENTER)
|
||||
|
||||
self.allow_focus_out_line = True
|
||||
# holds the iter's offset which points to the end of --- line
|
||||
self.focus_out_end_iter_offset = None
|
||||
|
||||
self.line_tooltip = tooltips.BaseTooltip()
|
||||
|
||||
def del_handlers(self):
|
||||
|
@ -180,9 +187,73 @@ class ConversationTextview:
|
|||
def scroll_to_end_iter(self):
|
||||
buffer = self.tv.get_buffer()
|
||||
end_iter = buffer.get_end_iter()
|
||||
if not end_iter:
|
||||
return False
|
||||
self.tv.scroll_to_iter(end_iter, 0, False, 1, 1)
|
||||
return False # when called in an idle_add, just do it once
|
||||
|
||||
def show_focus_out_line(self):
|
||||
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.tv.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()
|
||||
# img_char+1 (the '\n')
|
||||
begin_iter_for_previous_line.backward_chars(2)
|
||||
|
||||
# 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.scroll_to_end)
|
||||
|
||||
def show_line_tooltip(self):
|
||||
pointer = self.tv.get_pointer()
|
||||
x, y = self.tv.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT, pointer[0],
|
||||
|
@ -237,6 +308,7 @@ class ConversationTextview:
|
|||
buffer = self.tv.get_buffer()
|
||||
start, end = buffer.get_bounds()
|
||||
buffer.delete(start, end)
|
||||
self.focus_out_end_iter_offset = None
|
||||
|
||||
def visit_url_from_menuitem(self, widget, link):
|
||||
'''basically it filters out the widget instance'''
|
||||
|
@ -572,9 +644,6 @@ class ConversationTextview:
|
|||
other_tags_for_name = [], other_tags_for_time = [],
|
||||
other_tags_for_text = [], subject = None, old_kind = None):
|
||||
'''prints 'chat' type messages'''
|
||||
# kind = info, we print things as if it was a status: same color, ...
|
||||
if kind == 'info':
|
||||
kind = 'status'
|
||||
buffer = self.tv.get_buffer()
|
||||
buffer.begin_user_action()
|
||||
end_iter = buffer.get_end_iter()
|
||||
|
@ -593,7 +662,7 @@ class ConversationTextview:
|
|||
# We don't have tim for outgoing messages...
|
||||
tim = time.localtime()
|
||||
current_print_time = gajim.config.get('print_time')
|
||||
if current_print_time == 'always':
|
||||
if current_print_time == 'always' and kind != 'info':
|
||||
before_str = gajim.config.get('before_time')
|
||||
after_str = gajim.config.get('after_time')
|
||||
# get difference in days since epoch (86400 = 24*3600)
|
||||
|
@ -612,25 +681,38 @@ class ConversationTextview:
|
|||
if day_str:
|
||||
format += day_str + ' '
|
||||
format += '%X' + after_str
|
||||
# format comes as unicode, because of day_str.
|
||||
# we convert it to the encoding that we want
|
||||
tim_format = time.strftime(format, tim).encode('utf-8')
|
||||
tim_format = time.strftime(format, tim)
|
||||
# if tim_format comes as unicode because of day_str.
|
||||
# we convert it to the encoding that we want (and that is utf-8)
|
||||
tim_format = helpers.ensure_utf8_string(tim_format)
|
||||
tim_format = tim_format.encode('utf-8')
|
||||
buffer.insert_with_tags_by_name(end_iter, tim_format + ' ',
|
||||
*other_tags_for_time)
|
||||
elif current_print_time == 'sometimes':
|
||||
elif current_print_time == 'sometimes' and kind != 'info':
|
||||
every_foo_seconds = 60 * gajim.config.get(
|
||||
'print_ichat_every_foo_minutes')
|
||||
seconds_passed = time.mktime(tim) - self.last_time_printout
|
||||
if seconds_passed > every_foo_seconds:
|
||||
self.last_time_printout = time.mktime(tim)
|
||||
end_iter = buffer.get_end_iter()
|
||||
tim_format = time.strftime('%H:%M', tim).decode(
|
||||
locale.getpreferredencoding())
|
||||
if gajim.config.get('print_time_fuzzy') > 0:
|
||||
fc = FuzzyClock()
|
||||
fc.setTime(time.strftime('%H:%M', tim))
|
||||
ft = fc.getFuzzyTime(gajim.config.get('print_time_fuzzy'))
|
||||
tim_format = ft.decode(locale.getpreferredencoding())
|
||||
else:
|
||||
tim_format = time.strftime('%H:%M', tim).decode(
|
||||
locale.getpreferredencoding())
|
||||
|
||||
buffer.insert_with_tags_by_name(end_iter, tim_format + '\n',
|
||||
'time_sometimes')
|
||||
# kind = info, we print things as if it was a status: same color, ...
|
||||
if kind == 'info':
|
||||
kind = 'status'
|
||||
other_text_tag = self.detect_other_text_tag(text, kind)
|
||||
text_tags = other_tags_for_text[:] # create a new list
|
||||
if other_text_tag:
|
||||
# note that color of /me may be overwritten in gc_control
|
||||
text_tags.append(other_text_tag)
|
||||
else: # not status nor /me
|
||||
if gajim.config.get(
|
||||
|
|
|
@ -20,8 +20,6 @@ import sys
|
|||
|
||||
from common import gajim
|
||||
from common import exceptions
|
||||
from common import i18n
|
||||
_ = i18n._
|
||||
|
||||
try:
|
||||
import dbus
|
||||
|
|
1515
src/dialogs.py
1515
src/dialogs.py
File diff suppressed because it is too large
Load Diff
31
src/disco.py
31
src/disco.py
|
@ -51,7 +51,6 @@ import inspect
|
|||
import weakref
|
||||
import gobject
|
||||
import gtk
|
||||
import gtk.glade
|
||||
import pango
|
||||
|
||||
import dialogs
|
||||
|
@ -60,12 +59,6 @@ import gtkgui_helpers
|
|||
|
||||
from common import gajim
|
||||
from common import xmpp
|
||||
from common import i18n
|
||||
|
||||
_ = i18n._
|
||||
APP = i18n.APP
|
||||
gtk.glade.bindtextdomain (APP, i18n.DIR)
|
||||
gtk.glade.textdomain (APP)
|
||||
|
||||
# Dictionary mapping category, type pairs to browser class, image pairs.
|
||||
# This is a function, so we can call it after the classes are declared.
|
||||
|
@ -92,7 +85,7 @@ def _gen_agent_type_info():
|
|||
('proxy', 'bytestreams'): (None, 'bytestreams.png'), # Socks5 FT proxy
|
||||
|
||||
# Transports
|
||||
('conference', 'irc'): (False, 'irc.png'),
|
||||
('conference', 'irc'): (ToplevelAgentBrowser, 'irc.png'),
|
||||
('_jid', 'irc'): (False, 'irc.png'),
|
||||
('gateway', 'aim'): (False, 'aim.png'),
|
||||
('_jid', 'aim'): (False, 'aim.png'),
|
||||
|
@ -140,7 +133,8 @@ class CacheDictionary:
|
|||
|
||||
def _expire_timeout(self, key):
|
||||
'''The timeout has expired, remove the object.'''
|
||||
del self.cache[key]
|
||||
if key in self.cache:
|
||||
del self.cache[key]
|
||||
return False
|
||||
|
||||
def _refresh_timeout(self, key):
|
||||
|
@ -278,7 +272,7 @@ class ServicesCache:
|
|||
except KeyError:
|
||||
continue
|
||||
browser = info[0]
|
||||
if browser is not None:
|
||||
if browser:
|
||||
break
|
||||
# Note: possible outcome here is browser=False
|
||||
if browser is None:
|
||||
|
@ -449,7 +443,7 @@ _('Without a connection, you can not browse available services'))
|
|||
|
||||
# Address combobox
|
||||
self.address_comboboxentry = None
|
||||
address_hbox = self.xml.get_widget('address_hbox')
|
||||
address_table = self.xml.get_widget('address_table')
|
||||
if address_entry:
|
||||
self.address_comboboxentry = self.xml.get_widget(
|
||||
'address_comboboxentry')
|
||||
|
@ -461,7 +455,6 @@ _('Without a connection, you can not browse available services'))
|
|||
self.address_comboboxentry.set_text_column(0)
|
||||
self.latest_addresses = gajim.config.get(
|
||||
'latest_disco_addresses').split()
|
||||
jid = gajim.get_hostname_from_account(self.account)
|
||||
if jid in self.latest_addresses:
|
||||
self.latest_addresses.remove(jid)
|
||||
self.latest_addresses.insert(0, jid)
|
||||
|
@ -472,8 +465,8 @@ _('Without a connection, you can not browse available services'))
|
|||
self.address_comboboxentry.child.set_text(jid)
|
||||
else:
|
||||
# Don't show it at all if we didn't ask for it
|
||||
address_hbox.set_no_show_all(True)
|
||||
address_hbox.hide()
|
||||
address_table.set_no_show_all(True)
|
||||
address_table.hide()
|
||||
|
||||
self._initial_state()
|
||||
self.xml.signal_autoconnect(self)
|
||||
|
@ -1205,7 +1198,10 @@ class ToplevelAgentBrowser(AgentBrowser):
|
|||
else:
|
||||
room = ''
|
||||
if not gajim.interface.instances[self.account].has_key('join_gc'):
|
||||
dialogs.JoinGroupchatWindow(self.account, service, room)
|
||||
try:
|
||||
dialogs.JoinGroupchatWindow(self.account, service, room)
|
||||
except RuntimeError:
|
||||
pass
|
||||
else:
|
||||
gajim.interface.instances[self.account]['join_gc'].window.present()
|
||||
self.window.destroy(chain = True)
|
||||
|
@ -1535,7 +1531,10 @@ class MucBrowser(AgentBrowser):
|
|||
else:
|
||||
room = model[iter][1].decode('utf-8')
|
||||
if not gajim.interface.instances[self.account].has_key('join_gc'):
|
||||
dialogs.JoinGroupchatWindow(self.account, service, room)
|
||||
try:
|
||||
dialogs.JoinGroupchatWindow(self.account, service, room)
|
||||
except RuntimeError:
|
||||
pass
|
||||
else:
|
||||
gajim.interface.instances[self.account]['join_gc'].window.present()
|
||||
self.window.destroy(chain = True)
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
##
|
||||
|
||||
import gtk
|
||||
import gtk.glade
|
||||
import gobject
|
||||
import pango
|
||||
import os
|
||||
|
@ -30,12 +29,6 @@ import dialogs
|
|||
|
||||
from common import gajim
|
||||
from common import helpers
|
||||
from common import i18n
|
||||
|
||||
_ = i18n._
|
||||
APP = i18n.APP
|
||||
gtk.glade.bindtextdomain (APP, i18n.DIR)
|
||||
gtk.glade.textdomain (APP)
|
||||
|
||||
C_IMAGE = 0
|
||||
C_LABELS = 1
|
||||
|
@ -153,9 +146,8 @@ class FileTransfersWindow:
|
|||
''' show a dialog saying that file (file_props) has been transferred'''
|
||||
self.window.present()
|
||||
self.window.window.focus()
|
||||
|
||||
def on_open(widget, file_props):
|
||||
self.dialog.destroy()
|
||||
dialog.destroy()
|
||||
if not file_props.has_key('file-name'):
|
||||
return
|
||||
(path, file) = os.path.split(file_props['file-name'])
|
||||
|
@ -192,17 +184,17 @@ class FileTransfersWindow:
|
|||
sectext += recipient
|
||||
if file_props['type'] == 'r':
|
||||
sectext += '\n\t' +_('Saved in: %s') % file_path
|
||||
self.dialog = dialogs.HigDialog(None, gtk.MESSAGE_INFO, gtk.BUTTONS_NONE,
|
||||
dialog = dialogs.HigDialog(None, gtk.MESSAGE_INFO, gtk.BUTTONS_NONE,
|
||||
_('File transfer completed'), sectext)
|
||||
if file_props['type'] == 'r':
|
||||
button = gtk.Button(_('_Open Containing Folder'))
|
||||
button.connect('clicked', on_open, file_props)
|
||||
self.dialog.action_area.pack_start(button)
|
||||
ok_button = self.dialog.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
|
||||
dialog.action_area.pack_start(button)
|
||||
ok_button = dialog.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
|
||||
def on_ok(widget):
|
||||
self.dialog.destroy()
|
||||
dialog.destroy()
|
||||
ok_button.connect('clicked', on_ok)
|
||||
self.dialog.show_all()
|
||||
dialog.show_all()
|
||||
|
||||
def show_request_error(self, file_props):
|
||||
''' show error dialog to the recipient saying that transfer
|
||||
|
@ -221,7 +213,7 @@ class FileTransfersWindow:
|
|||
_('Connection with peer cannot be established.'))
|
||||
self.tree.get_selection().unselect_all()
|
||||
|
||||
def show_stopped(self, jid, file_props):
|
||||
def show_stopped(self, jid, file_props, error_msg = ''):
|
||||
self.window.present()
|
||||
self.window.window.focus()
|
||||
if file_props['type'] == 'r':
|
||||
|
@ -229,7 +221,9 @@ _('Connection with peer cannot be established.'))
|
|||
else:
|
||||
file_name = file_props['name']
|
||||
sectext = '\t' + _('Filename: %s') % file_name
|
||||
sectext += '\n\t' + _('Sender: %s') % jid
|
||||
sectext += '\n\t' + _('Recipient: %s') % jid
|
||||
if error_msg:
|
||||
sectext += '\n\t' + _('Error message: %s') % error_msg
|
||||
dialogs.ErrorDialog(_('File transfer stopped by the contact of the other side'), \
|
||||
sectext)
|
||||
self.tree.get_selection().unselect_all()
|
||||
|
@ -237,7 +231,7 @@ _('Connection with peer cannot be established.'))
|
|||
def show_file_send_request(self, account, contact):
|
||||
def on_ok(widget):
|
||||
file_dir = None
|
||||
files_path_list = self.dialog.get_filenames()
|
||||
files_path_list = dialog.get_filenames()
|
||||
files_path_list = gtkgui_helpers.decode_filechooser_file_paths(
|
||||
files_path_list)
|
||||
for file_path in files_path_list:
|
||||
|
@ -245,16 +239,16 @@ _('Connection with peer cannot be established.'))
|
|||
file_dir = os.path.dirname(file_path)
|
||||
if file_dir:
|
||||
gajim.config.set('last_send_dir', file_dir)
|
||||
self.dialog.destroy()
|
||||
dialog.destroy()
|
||||
|
||||
self.dialog = dialogs.FileChooserDialog(_('Choose File to Send...'),
|
||||
dialog = dialogs.FileChooserDialog(_('Choose File to Send...'),
|
||||
gtk.FILE_CHOOSER_ACTION_OPEN, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL),
|
||||
gtk.RESPONSE_OK,
|
||||
True, # select multiple true as we can select many files to send
|
||||
gajim.config.get('last_send_dir'),
|
||||
)
|
||||
|
||||
btn = self.dialog.add_button(_('_Send'), gtk.RESPONSE_OK)
|
||||
btn = dialog.add_button(_('_Send'), gtk.RESPONSE_OK)
|
||||
btn.set_use_stock(True) # FIXME: add send icon to this button (JUMP_TO)
|
||||
btn.connect('clicked', on_ok)
|
||||
|
||||
|
@ -313,6 +307,12 @@ _('Connection with peer cannot be established.'))
|
|||
file_path = gtkgui_helpers.decode_filechooser_file_paths(
|
||||
(file_path,))[0]
|
||||
if os.path.exists(file_path):
|
||||
# check if we have write permissions
|
||||
if not os.access(file_path, os.W_OK):
|
||||
file_name = os.path.basename(file_path)
|
||||
dialogs.ErrorDialog(_('Cannot overwrite existing file "%s"' % file_name),
|
||||
_('A file with this name already exists and you do not have permission to overwrite it.'))
|
||||
return
|
||||
stat = os.stat(file_path)
|
||||
dl_size = stat.st_size
|
||||
file_size = file_props['size']
|
||||
|
@ -327,6 +327,11 @@ _('Connection with peer cannot be established.'))
|
|||
return
|
||||
elif response == 100:
|
||||
file_props['offset'] = dl_size
|
||||
else:
|
||||
dirname = os.path.dirname(file_path)
|
||||
if not os.access(dirname, os.W_OK):
|
||||
dialogs.ErrorDialog(_('Directory "%s" is not writable') % dirname, _('You do not have permission to create files in this directory.'))
|
||||
return
|
||||
dialog2.destroy()
|
||||
self._start_receive(file_path, account, contact, file_props)
|
||||
|
||||
|
@ -442,12 +447,11 @@ _('Connection with peer cannot be established.'))
|
|||
jid = gajim.get_jid_without_resource(other)
|
||||
else: # It's a Contact instance
|
||||
jid = other.jid
|
||||
if gajim.awaiting_events[account].has_key(jid):
|
||||
for event in gajim.awaiting_events[account][jid]:
|
||||
if event[0] in ('file-error', 'file-completed',
|
||||
'file-request-error', 'file-send-error', 'file-stopped') and \
|
||||
event[1]['sid'] == file_props['sid']:
|
||||
gajim.interface.remove_event(account, jid, event)
|
||||
for ev_type in ('file-error', 'file-completed', 'file-request-error',
|
||||
'file-send-error', 'file-stopped'):
|
||||
for event in gajim.events.get_events(account, jid, [ev_type]):
|
||||
if event.parameters[1]['sid'] == file_props['sid']:
|
||||
gajim.events.remove_events(account, jid, event)
|
||||
del(self.files_props[sid[0]][sid[1:]])
|
||||
del(file_props)
|
||||
|
||||
|
@ -832,9 +836,9 @@ _('Connection with peer cannot be established.'))
|
|||
self.set_buttons_sensitive(path, True)
|
||||
|
||||
event_button = gtkgui_helpers.get_possible_button_event(event)
|
||||
self.file_transfers_menu.show_all()
|
||||
self.file_transfers_menu.popup(None, self.tree, None,
|
||||
event_button, event.time)
|
||||
self.file_transfers_menu.show_all()
|
||||
|
||||
def on_transfers_list_key_press_event(self, widget, event):
|
||||
'''when a key is pressed in the treeviews'''
|
||||
|
|
|
@ -38,8 +38,6 @@ signal.signal(signal.SIGINT, signal.SIG_DFL) # ^C exits the application
|
|||
from common import exceptions
|
||||
from common import i18n
|
||||
|
||||
_ = i18n._
|
||||
i18n.init()
|
||||
try:
|
||||
PREFERRED_ENCODING = locale.getpreferredencoding()
|
||||
except:
|
||||
|
@ -68,7 +66,7 @@ BASENAME = 'gajim-remote'
|
|||
|
||||
|
||||
class GajimRemote:
|
||||
|
||||
|
||||
def __init__(self):
|
||||
self.argv_len = len(sys.argv)
|
||||
# define commands dict. Prototype :
|
||||
|
@ -81,7 +79,7 @@ class GajimRemote:
|
|||
#
|
||||
self.commands = {
|
||||
'help':[
|
||||
_('shows a help on specific command'),
|
||||
_('Shows a help on specific command'),
|
||||
[
|
||||
#User gets help for the command, specified by this parameter
|
||||
(_('command'),
|
||||
|
@ -101,7 +99,7 @@ class GajimRemote:
|
|||
[
|
||||
(_('account'), _('show only contacts of the given account'), False)
|
||||
]
|
||||
|
||||
|
||||
],
|
||||
'list_accounts': [
|
||||
_('Prints a list of registered accounts'),
|
||||
|
@ -110,6 +108,7 @@ class GajimRemote:
|
|||
'change_status': [
|
||||
_('Changes the status of account or accounts'),
|
||||
[
|
||||
#offline, online, chat, away, xa, dnd, invisible should not be translated
|
||||
(_('status'), _('one of: offline, online, chat, away, xa, dnd, invisible '), True),
|
||||
(_('message'), _('status message'), False),
|
||||
(_('account'), _('change status of account "account". '
|
||||
|
@ -127,7 +126,7 @@ class GajimRemote:
|
|||
]
|
||||
],
|
||||
'send_message':[
|
||||
_('Sends new message to a contact in the roster. Both OpenPGP key '
|
||||
_('Sends new chat message to a contact in the roster. Both OpenPGP key '
|
||||
'and account are optional. If you want to set only \'account\', '
|
||||
'without \'OpenPGP key\', just set \'OpenPGP key\' to \'\'.'),
|
||||
[
|
||||
|
@ -138,6 +137,20 @@ class GajimRemote:
|
|||
(_('account'), _('if specified, the message will be sent '
|
||||
'using this account'), False),
|
||||
]
|
||||
],
|
||||
'send_single_message':[
|
||||
_('Sends new single message to a contact in the roster. Both OpenPGP key '
|
||||
'and account are optional. If you want to set only \'account\', '
|
||||
'without \'OpenPGP key\', just set \'OpenPGP key\' to \'\'.'),
|
||||
[
|
||||
('jid', _('JID of the contact that will receive the message'), True),
|
||||
(_('subject'), _('message subject'), True),
|
||||
(_('message'), _('message contents'), True),
|
||||
(_('pgp key'), _('if specified, the message will be encrypted '
|
||||
'using this public key'), False),
|
||||
(_('account'), _('if specified, the message will be sent '
|
||||
'using this account'), False),
|
||||
]
|
||||
],
|
||||
'contact_info': [
|
||||
_('Gets detailed info on a contact'),
|
||||
|
@ -188,7 +201,7 @@ class GajimRemote:
|
|||
('jid', _('JID of the contact'), True),
|
||||
(_('account'), _('if specified, contact is taken from the '
|
||||
'contact list of this account'), False)
|
||||
|
||||
|
||||
]
|
||||
],
|
||||
'add_contact': [
|
||||
|
@ -198,14 +211,14 @@ class GajimRemote:
|
|||
(_('account'), _('Adds new contact to this account'), False)
|
||||
]
|
||||
],
|
||||
|
||||
|
||||
'get_status': [
|
||||
_('Returns current status (the global one unless account is specified)'),
|
||||
[
|
||||
(_('account'), _(''), False)
|
||||
]
|
||||
],
|
||||
|
||||
|
||||
'get_status_message': [
|
||||
_('Returns current status message(the global one unless account is specified)'),
|
||||
[
|
||||
|
@ -218,11 +231,20 @@ class GajimRemote:
|
|||
[ ]
|
||||
],
|
||||
'start_chat': [
|
||||
_('Open \'Start Chat\' dialog'),
|
||||
_('Opens \'Start Chat\' dialog'),
|
||||
[
|
||||
(_('account'), _('Starts chat, using this account'), True)
|
||||
]
|
||||
],
|
||||
'send_xml': [
|
||||
_('Sends custom XML'),
|
||||
[
|
||||
('xml', _('XML to send'), True),
|
||||
('account', _('Account in which the xml will be sent; '
|
||||
'if not specified, xml will be sent to all accounts'),
|
||||
False)
|
||||
]
|
||||
],
|
||||
}
|
||||
if self.argv_len < 2 or \
|
||||
sys.argv[1] not in self.commands.keys(): # no args or bad args
|
||||
|
@ -234,14 +256,14 @@ class GajimRemote:
|
|||
else:
|
||||
print self.compose_help().encode(PREFERRED_ENCODING)
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
self.init_connection()
|
||||
self.check_arguments()
|
||||
|
||||
|
||||
if self.command == 'contact_info':
|
||||
if self.argv_len < 3:
|
||||
send_error(_('Missing argument "contact_jid"'))
|
||||
|
||||
|
||||
try:
|
||||
res = self.call_remote_method()
|
||||
except exceptions.ServiceNotAvailable:
|
||||
|
@ -249,14 +271,14 @@ class GajimRemote:
|
|||
sys.exit(1)
|
||||
else:
|
||||
self.print_result(res)
|
||||
|
||||
|
||||
def print_result(self, res):
|
||||
''' Print retrieved result to the output '''
|
||||
if res is not None:
|
||||
if self.command in ('open_chat', 'send_message', 'start_chat'):
|
||||
if self.command == 'send_message':
|
||||
if self.command in ('open_chat', 'send_message', 'send_single_message', 'start_chat'):
|
||||
if self.command in ('send_message', 'send_single_message'):
|
||||
self.argv_len -= 2
|
||||
|
||||
|
||||
if res is False:
|
||||
if self.argv_len < 4:
|
||||
send_error(_('\'%s\' is not in your roster.\n'
|
||||
|
@ -289,7 +311,7 @@ class GajimRemote:
|
|||
print self.print_info(0, res, True)
|
||||
elif res:
|
||||
print unicode(res).encode(PREFERRED_ENCODING)
|
||||
|
||||
|
||||
def init_connection(self):
|
||||
''' create the onnection to the session dbus,
|
||||
or exit if it is not possible '''
|
||||
|
@ -297,7 +319,7 @@ class GajimRemote:
|
|||
self.sbus = dbus.SessionBus()
|
||||
except:
|
||||
raise exceptions.SessionBusNotPresent
|
||||
|
||||
|
||||
if _version[1] >= 30:
|
||||
obj = self.sbus.get_object(SERVICE, OBJ_PATH)
|
||||
interface = dbus.Interface(obj, INTERFACE)
|
||||
|
@ -306,10 +328,10 @@ class GajimRemote:
|
|||
interface = self.service.get_object(OBJ_PATH, INTERFACE)
|
||||
else:
|
||||
send_error(_('Unknown D-Bus version: %s') % _version[1])
|
||||
|
||||
|
||||
# get the function asked
|
||||
self.method = interface.__getattr__(self.command)
|
||||
|
||||
|
||||
def make_arguments_row(self, args):
|
||||
''' return arguments list. Mandatory arguments are enclosed with:
|
||||
'<', '>', optional arguments - with '[', ']' '''
|
||||
|
@ -326,7 +348,7 @@ class GajimRemote:
|
|||
else:
|
||||
str += ']'
|
||||
return str
|
||||
|
||||
|
||||
def help_on_command(self, command):
|
||||
''' return help message for a given command '''
|
||||
if command in self.commands:
|
||||
|
@ -340,7 +362,7 @@ class GajimRemote:
|
|||
str += ' ' + argument[0] + ' - ' + argument[1] + '\n'
|
||||
return str
|
||||
send_error(_('%s not found') % command)
|
||||
|
||||
|
||||
def compose_help(self):
|
||||
''' print usage, and list available commands '''
|
||||
str = _('Usage: %s command [arguments]\nCommand is one of:\n' ) % BASENAME
|
||||
|
@ -361,7 +383,7 @@ class GajimRemote:
|
|||
str += ']'
|
||||
str += '\n'
|
||||
return str
|
||||
|
||||
|
||||
def print_info(self, level, prop_dict, encode_return = False):
|
||||
''' return formated string from data structure '''
|
||||
if prop_dict is None or not isinstance(prop_dict, (dict, list, tuple)):
|
||||
|
@ -410,7 +432,7 @@ class GajimRemote:
|
|||
except:
|
||||
pass
|
||||
return ret_str
|
||||
|
||||
|
||||
def check_arguments(self):
|
||||
''' Make check if all necessary arguments are given '''
|
||||
argv_len = self.argv_len - 2
|
||||
|
@ -420,7 +442,7 @@ class GajimRemote:
|
|||
send_error(_('Argument "%s" is not specified. \n'
|
||||
'Type "%s help %s" for more info') %
|
||||
(args[argv_len][0], BASENAME, self.command))
|
||||
|
||||
|
||||
def call_remote_method(self):
|
||||
''' calls self.method with arguments from sys.argv[2:] '''
|
||||
args = sys.argv[2:]
|
||||
|
|
418
src/gajim.py
418
src/gajim.py
|
@ -33,16 +33,22 @@ import sys
|
|||
import os
|
||||
import urllib
|
||||
|
||||
from common import i18n
|
||||
|
||||
import message_control
|
||||
|
||||
from chat_control import ChatControlBase
|
||||
|
||||
from common import exceptions
|
||||
from common import i18n
|
||||
from common.zeroconf import connection_zeroconf
|
||||
|
||||
i18n.init()
|
||||
_ = i18n._
|
||||
if os.name == 'posix': # dl module is Unix Only
|
||||
try: # rename the process name to gajim
|
||||
import dl
|
||||
libc = dl.open('/lib/libc.so.6')
|
||||
libc.call('prctl', 15, 'gajim\0', 0, 0, 0)
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
import gtk
|
||||
|
@ -73,6 +79,15 @@ except exceptions.PysqliteNotAvailable, e:
|
|||
pritext = _('Gajim needs PySQLite2 to run')
|
||||
sectext = str(e)
|
||||
|
||||
if os.name == 'nt':
|
||||
try:
|
||||
import winsound # windows-only built-in module for playing wav
|
||||
import win32api
|
||||
import win32con
|
||||
except:
|
||||
pritext = _('Gajim needs pywin32 to run')
|
||||
sectext = _('Please make sure that Pywin32 is installed on your system. You can get it at %s') % 'http://sourceforge.net/project/showfiles.php?group_id=78018'
|
||||
|
||||
if pritext:
|
||||
dlg = gtk.MessageDialog(None,
|
||||
gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL,
|
||||
|
@ -129,40 +144,60 @@ for o, a in opts:
|
|||
elif o in ('-p', '--profile'): # gajim --profile name
|
||||
profile = a
|
||||
|
||||
pid_filename = os.path.expanduser('~/.gajim/gajim')
|
||||
config_filename = os.path.expanduser('~/.gajim/config')
|
||||
if os.name == 'nt':
|
||||
try:
|
||||
# Documents and Settings\[User Name]\Application Data\Gajim\logs
|
||||
config_filename = os.environ['appdata'] + '/Gajim/config'
|
||||
pid_filename = os.environ['appdata'] + '/Gajim/gajim'
|
||||
except KeyError:
|
||||
# win9x so ./config
|
||||
config_filename = 'config'
|
||||
pid_filename = 'gajim'
|
||||
|
||||
if profile:
|
||||
config_filename += '.%s' % profile
|
||||
pid_filename += '.%s' % profile
|
||||
|
||||
pid_filename += '.pid'
|
||||
import dialogs
|
||||
if os.path.exists(pid_filename):
|
||||
path_to_file = os.path.join(gajim.DATA_DIR, 'pixmaps/gajim.png')
|
||||
pix = gtk.gdk.pixbuf_new_from_file(path_to_file)
|
||||
gtk.window_set_default_icon(pix) # set the icon to all newly opened wind
|
||||
pritext = _('Gajim is already running')
|
||||
sectext = _('Another instance of Gajim seems to be running\nRun anyway?')
|
||||
dialog = dialogs.YesNoDialog(pritext, sectext)
|
||||
if dialog.get_response() != gtk.RESPONSE_YES:
|
||||
sys.exit(3)
|
||||
if os.path.exists(pid_filename):
|
||||
os.remove(pid_filename)
|
||||
dialog.destroy()
|
||||
|
||||
# Create .gajim dir
|
||||
pid_dir = os.path.dirname(pid_filename)
|
||||
if not os.path.exists(pid_dir):
|
||||
check_paths.create_path(pid_dir)
|
||||
# Create pid file
|
||||
f = open(pid_filename, 'a')
|
||||
f.close()
|
||||
|
||||
def on_exit():
|
||||
# delete pid file on normal exit
|
||||
if os.path.exists(pid_filename):
|
||||
os.remove(pid_filename)
|
||||
|
||||
import atexit
|
||||
atexit.register(on_exit)
|
||||
|
||||
parser = optparser.OptionsParser(config_filename)
|
||||
|
||||
import roster_window
|
||||
import systray
|
||||
import dialogs
|
||||
import vcard
|
||||
import profile_window
|
||||
import config
|
||||
|
||||
class MigrateCommand(nslookup.IdleCommand):
|
||||
def __init__(self, on_result):
|
||||
nslookup.IdleCommand.__init__(self, on_result)
|
||||
self.commandtimeout = 10
|
||||
|
||||
def _compose_command_args(self):
|
||||
return ['python', 'migrate_logs_to_dot9_db.py', 'dont_wait']
|
||||
|
||||
def _return_result(self):
|
||||
print self.result
|
||||
if self.result_handler:
|
||||
self.result_handler(self.result)
|
||||
self.result_handler = None
|
||||
|
||||
class GlibIdleQueue(idlequeue.IdleQueue):
|
||||
'''
|
||||
Extends IdleQueue to use glib io_add_wath, instead of select/poll
|
||||
|
@ -239,7 +274,7 @@ class Interface:
|
|||
on_response_no = (response, account, data[3], 'no'))
|
||||
|
||||
def handle_event_error_answer(self, account, array):
|
||||
#('ERROR_ANSWER', account, (id, jid_from. errmsg, errcode))
|
||||
#('ERROR_ANSWER', account, (id, jid_from, errmsg, errcode))
|
||||
id, jid_from, errmsg, errcode = array
|
||||
if unicode(errcode) in ('403', '406') and id:
|
||||
# show the error dialog
|
||||
|
@ -251,7 +286,7 @@ class Interface:
|
|||
file_props = ft.files_props['s'][sid]
|
||||
file_props['error'] = -4
|
||||
self.handle_event_file_request_error(account,
|
||||
(jid_from, file_props))
|
||||
(jid_from, file_props, errmsg))
|
||||
conn = gajim.connections[account]
|
||||
conn.disconnect_transfer(file_props)
|
||||
return
|
||||
|
@ -275,6 +310,14 @@ class Interface:
|
|||
gajim.con_types[account] = con_type
|
||||
self.roster.draw_account(account)
|
||||
|
||||
def handle_event_connection_lost(self, account, array):
|
||||
# ('CONNECTION_LOST', account, [title, text])
|
||||
path = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events',
|
||||
'connection_lost.png')
|
||||
path = gtkgui_helpers.get_path_to_generic_or_avatar(path)
|
||||
notify.popup(_('Connection Failed'), account, account,
|
||||
'connection_failed', path, array[0], array[1])
|
||||
|
||||
def unblock_signed_in_notifications(self, account):
|
||||
gajim.block_signed_in_notifications[account] = False
|
||||
|
||||
|
@ -314,9 +357,9 @@ class Interface:
|
|||
|
||||
def edit_own_details(self, account):
|
||||
jid = gajim.get_jid_from_account(account)
|
||||
if not self.instances[account]['infos'].has_key(jid):
|
||||
self.instances[account]['infos'][jid] = \
|
||||
vcard.VcardWindow(jid, account, True)
|
||||
if not self.instances[account].has_key('profile'):
|
||||
self.instances[account]['profile'] = \
|
||||
profile_window.ProfileWindow(account)
|
||||
gajim.connections[account].request_vcard(jid)
|
||||
|
||||
def handle_event_notify(self, account, array):
|
||||
|
@ -346,7 +389,7 @@ class Interface:
|
|||
|
||||
# Update contact
|
||||
jid_list = gajim.contacts.get_jid_list(account)
|
||||
if ji in jid_list:
|
||||
if ji in jid_list or jid == gajim.get_jid_from_account(account):
|
||||
lcontact = gajim.contacts.get_contacts_from_jid(account, ji)
|
||||
contact1 = None
|
||||
resources = []
|
||||
|
@ -363,7 +406,20 @@ class Interface:
|
|||
return
|
||||
else:
|
||||
contact1 = gajim.contacts.get_first_contact_from_jid(account, ji)
|
||||
if contact1.show in statuss:
|
||||
if not contact1:
|
||||
# presence of another resource of our jid
|
||||
if resource == gajim.connections[account].server_resource:
|
||||
return
|
||||
contact1 = gajim.contacts.create_contact(jid = ji,
|
||||
name = gajim.nicks[account], groups = [],
|
||||
show = array[1], status = status_message, sub = 'both',
|
||||
ask = 'none', priority = priority, keyID = keyID,
|
||||
resource = resource)
|
||||
old_show = 0
|
||||
gajim.contacts.add_contact(account, contact1)
|
||||
lcontact.append(contact1)
|
||||
self.roster.add_self_contact(account)
|
||||
elif contact1.show in statuss:
|
||||
old_show = statuss.index(contact1.show)
|
||||
if (resources != [''] and (len(lcontact) != 1 or
|
||||
lcontact[0].show != 'offline')) and jid.find('@') > 0:
|
||||
|
@ -403,9 +459,21 @@ class Interface:
|
|||
if ji in jid_list:
|
||||
# Update existing iter
|
||||
self.roster.draw_contact(ji, account)
|
||||
elif jid == gajim.get_jid_from_account(account):
|
||||
# It's another of our resources. We don't need to see that!
|
||||
return
|
||||
# transport just signed in/out, don't show popup notifications
|
||||
# for 30s
|
||||
account_ji = account + '/' + ji
|
||||
gajim.block_signed_in_notifications[account_ji] = True
|
||||
gobject.timeout_add(30000, self.unblock_signed_in_notifications,
|
||||
account_ji)
|
||||
locations = (self.instances, self.instances[account])
|
||||
for location in locations:
|
||||
if location.has_key('add_contact'):
|
||||
if old_show == 0 and new_show > 1:
|
||||
location['add_contact'].transport_signed_in(jid)
|
||||
break
|
||||
elif old_show > 1 and new_show == 0:
|
||||
location['add_contact'].transport_signed_out(jid)
|
||||
break
|
||||
elif ji in jid_list:
|
||||
# It isn't an agent
|
||||
# reset chatstate if needed:
|
||||
|
@ -416,20 +484,21 @@ class Interface:
|
|||
gajim.connections[account].remove_transfers_for_contact(contact1)
|
||||
self.roster.chg_contact_status(contact1, array[1], status_message,
|
||||
account)
|
||||
# play sound
|
||||
# Notifications
|
||||
if old_show < 2 and new_show > 1:
|
||||
notify.notify('contact_connected', jid, account, status_message)
|
||||
if self.remote_ctrl:
|
||||
self.remote_ctrl.raise_signal('ContactPresence',
|
||||
(account, array))
|
||||
|
||||
|
||||
elif old_show > 1 and new_show < 2:
|
||||
notify.notify('contact_disconnected', jid, account, status_message)
|
||||
if self.remote_ctrl:
|
||||
self.remote_ctrl.raise_signal('ContactAbsence', (account, array))
|
||||
# FIXME: stop non active file transfers
|
||||
elif new_show > 1: # Status change (not connected/disconnected or error (<1))
|
||||
notify.notify('status_change', jid, account, [new_show, status_message])
|
||||
notify.notify('status_change', jid, account, [new_show,
|
||||
status_message])
|
||||
else:
|
||||
# FIXME: Msn transport (CMSN1.2.1 and PyMSN0.10) doesn't follow the JEP
|
||||
# remove in 2007
|
||||
|
@ -440,7 +509,7 @@ class Interface:
|
|||
|
||||
def handle_event_msg(self, account, array):
|
||||
# 'MSG' (account, (jid, msg, time, encrypted, msg_type, subject,
|
||||
# chatstate))
|
||||
# chatstate, msg_id, composing_jep, user_nick)) user_nick is JEP-0172
|
||||
|
||||
full_jid_with_resource = array[0]
|
||||
jid = gajim.get_jid_without_resource(full_jid_with_resource)
|
||||
|
@ -448,6 +517,7 @@ class Interface:
|
|||
|
||||
message = array[1]
|
||||
msg_type = array[4]
|
||||
subject = array[5]
|
||||
chatstate = array[6]
|
||||
msg_id = array[7]
|
||||
composing_jep = array[8]
|
||||
|
@ -513,10 +583,13 @@ class Interface:
|
|||
not gajim.contacts.get_contact(account, jid) and not pm:
|
||||
return
|
||||
|
||||
advanced_notif_num = notify.get_advanced_notification('message_received',
|
||||
account, contact)
|
||||
|
||||
# Is it a first or next message received ?
|
||||
first = False
|
||||
if not chat_control and not gajim.awaiting_events[account].has_key(
|
||||
jid_of_control):
|
||||
if not chat_control and not gajim.events.get_events(account,
|
||||
jid_of_control, ['chat']):
|
||||
# It's a first message and not a Private Message
|
||||
first = True
|
||||
|
||||
|
@ -527,10 +600,14 @@ class Interface:
|
|||
else:
|
||||
# array: (jid, msg, time, encrypted, msg_type, subject)
|
||||
self.roster.on_message(jid, message, array[2], account, array[3],
|
||||
msg_type, array[5], resource, msg_id)
|
||||
msg_type, subject, resource, msg_id, array[9], advanced_notif_num)
|
||||
nickname = gajim.get_name_from_jid(account, jid)
|
||||
# Check and do wanted notifications
|
||||
notify.notify('new_message', jid, account, [msg_type, first, nickname, message])
|
||||
msg = message
|
||||
if subject:
|
||||
msg = _('Subject: %s') % subject + '\n' + msg
|
||||
notify.notify('new_message', jid, account, [msg_type, first, nickname,
|
||||
msg], advanced_notif_num)
|
||||
|
||||
if self.remote_ctrl:
|
||||
self.remote_ctrl.raise_signal('NewMessage', (account, array))
|
||||
|
@ -583,8 +660,8 @@ class Interface:
|
|||
helpers.play_sound('message_sent')
|
||||
|
||||
def handle_event_subscribe(self, account, array):
|
||||
#('SUBSCRIBE', account, (jid, text))
|
||||
dialogs.SubscriptionRequestWindow(array[0], array[1], account)
|
||||
#('SUBSCRIBE', account, (jid, text, user_nick)) user_nick is JEP-0172
|
||||
dialogs.SubscriptionRequestWindow(array[0], array[1], account, array[2])
|
||||
if self.remote_ctrl:
|
||||
self.remote_ctrl.raise_signal('Subscribe', (account, array))
|
||||
|
||||
|
@ -664,8 +741,8 @@ class Interface:
|
|||
config.ServiceRegistrationWindow(array[0], array[1], account,
|
||||
array[2])
|
||||
else:
|
||||
dialogs.ErrorDialog(_('Contact with "%s" cannot be established'\
|
||||
% array[0]), _('Check your connection or try again later.'))
|
||||
dialogs.ErrorDialog(_('Contact with "%s" cannot be established')\
|
||||
% array[0], _('Check your connection or try again later.'))
|
||||
|
||||
def handle_event_agent_info_items(self, account, array):
|
||||
#('AGENT_INFO_ITEMS', account, (agent, node, items))
|
||||
|
@ -705,8 +782,8 @@ class Interface:
|
|||
nick = array['NICKNAME']
|
||||
if nick:
|
||||
gajim.nicks[account] = nick
|
||||
if self.instances[account]['infos'].has_key(array['jid']):
|
||||
win = self.instances[account]['infos'][array['jid']]
|
||||
if self.instances[account].has_key('profile'):
|
||||
win = self.instances[account]['profile']
|
||||
win.set_values(array)
|
||||
if account in self.show_vcard_when_connect:
|
||||
self.show_vcard_when_connect.remove(account)
|
||||
|
@ -763,7 +840,7 @@ class Interface:
|
|||
c = gajim.contacts.get_contact(account, array[0], array[1])
|
||||
# c is a list when no resource is given. it probably means that contact
|
||||
# is offline, so only on Contact instance
|
||||
if isinstance(c, list):
|
||||
if isinstance(c, list) and len(c):
|
||||
c = c[0]
|
||||
if c: # c can be none if it's a gc contact
|
||||
c.last_status_time = time.localtime(time.time() - array[2])
|
||||
|
@ -811,14 +888,13 @@ class Interface:
|
|||
uf_show = helpers.get_uf_show(show)
|
||||
ctrl.print_conversation(_('%s is now %s (%s)') % (nick, uf_show, status),
|
||||
'status')
|
||||
ctrl.draw_banner()
|
||||
ctrl.parent_win.redraw_tab(ctrl)
|
||||
if self.remote_ctrl:
|
||||
self.remote_ctrl.raise_signal('GCPresence', (account, array))
|
||||
|
||||
|
||||
def handle_event_gc_msg(self, account, array):
|
||||
# ('GC_MSG', account, (jid, msg, time))
|
||||
# ('GC_MSG', account, (jid, msg, time, has_timestamp))
|
||||
jids = array[0].split('/', 1)
|
||||
room_jid = jids[0]
|
||||
gc_control = self.msg_win_mgr.get_control(room_jid, account)
|
||||
|
@ -830,7 +906,7 @@ class Interface:
|
|||
else:
|
||||
# message from someone
|
||||
nick = jids[1]
|
||||
gc_control.on_message(nick, array[1], array[2])
|
||||
gc_control.on_message(nick, array[1], array[2], array[3])
|
||||
if self.remote_ctrl:
|
||||
self.remote_ctrl.raise_signal('GCMessage', (account, array))
|
||||
|
||||
|
@ -851,10 +927,18 @@ class Interface:
|
|||
|
||||
def handle_event_gc_config(self, account, array):
|
||||
#('GC_CONFIG', account, (jid, config)) config is a dict
|
||||
jid = array[0].split('/')[0]
|
||||
if not self.instances[account]['gc_config'].has_key(jid):
|
||||
self.instances[account]['gc_config'][jid] = \
|
||||
config.GroupchatConfigWindow(account, jid, array[1])
|
||||
room_jid = array[0].split('/')[0]
|
||||
if room_jid in gajim.automatic_rooms[account]:
|
||||
# use default configuration
|
||||
gajim.connections[account].send_gc_config(room_jid, array[1])
|
||||
# invite contacts
|
||||
if gajim.automatic_rooms[account][room_jid].has_key('invities'):
|
||||
for jid in gajim.automatic_rooms[account][room_jid]['invities']:
|
||||
gajim.connections[account].send_invite(room_jid, jid)
|
||||
del gajim.automatic_rooms[account][room_jid]
|
||||
elif not self.instances[account]['gc_config'].has_key(room_jid):
|
||||
self.instances[account]['gc_config'][room_jid] = \
|
||||
config.GroupchatConfigWindow(account, room_jid, array[1])
|
||||
|
||||
def handle_event_gc_affiliation(self, account, array):
|
||||
#('GC_AFFILIATION', account, (room_jid, affiliation, list)) list is list
|
||||
|
@ -875,8 +959,7 @@ class Interface:
|
|||
self.add_event(account, jid, 'gc-invitation', (room_jid, array[2],
|
||||
array[3]))
|
||||
|
||||
if gajim.config.get('notify_on_new_message') and \
|
||||
helpers.allow_showing_notification(account):
|
||||
if helpers.allow_showing_notification(account, 'notify_on_new_message'):
|
||||
path = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events',
|
||||
'gc_invitation.png')
|
||||
path = gtkgui_helpers.get_path_to_generic_or_avatar(path)
|
||||
|
@ -908,7 +991,7 @@ class Interface:
|
|||
c = contacts[0]
|
||||
self.roster.remove_contact(c, account)
|
||||
gajim.contacts.remove_jid(account, jid)
|
||||
if gajim.awaiting_events[account].has_key(c.jid):
|
||||
if gajim.events.get_events(account, c.jid):
|
||||
keyID = ''
|
||||
attached_keys = gajim.config.get_per('accounts', account,
|
||||
'attached_gpg_keys').split()
|
||||
|
@ -989,12 +1072,20 @@ class Interface:
|
|||
def handle_event_gmail_notify(self, account, array):
|
||||
jid = array[0]
|
||||
gmail_new_messages = int(array[1])
|
||||
gmail_messages_list = array[2]
|
||||
if gajim.config.get('notify_on_new_gmail_email'):
|
||||
img = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events',
|
||||
'single_msg_recv.png') #FIXME: find a better image
|
||||
title = _('New E-mail on %(gmail_mail_address)s') % \
|
||||
{'gmail_mail_address': jid}
|
||||
text = i18n.ngettext('You have %d new E-mail message', 'You have %d new E-mail messages', gmail_new_messages, gmail_new_messages, gmail_new_messages)
|
||||
|
||||
if gajim.config.get('notify_on_new_gmail_email_extra'):
|
||||
for gmessage in gmail_messages_list:
|
||||
# each message has a 'From', 'Subject' and 'Snippet' field
|
||||
text += _('\nFrom: %(from_address)s') % \
|
||||
{'from_address': gmessage['From']}
|
||||
|
||||
path = gtkgui_helpers.get_path_to_generic_or_avatar(img)
|
||||
notify.popup(_('New E-mail'), jid, account, 'gmail',
|
||||
path_to_image = path, title = title, text = text)
|
||||
|
@ -1035,71 +1126,59 @@ class Interface:
|
|||
path_to_bw_file = path_to_file + '_notif_size_bw.png'
|
||||
bwbuf.save(path_to_bw_file, 'png')
|
||||
|
||||
def add_event(self, account, jid, typ, args):
|
||||
'''add an event to the awaiting_events var'''
|
||||
# We add it to the awaiting_events queue
|
||||
def add_event(self, account, jid, type_, args):
|
||||
'''add an event to the gajim.events var'''
|
||||
# We add it to the gajim.events queue
|
||||
# Do we have a queue?
|
||||
jid = gajim.get_jid_without_resource(jid)
|
||||
qs = gajim.awaiting_events[account]
|
||||
no_queue = False
|
||||
if not qs.has_key(jid):
|
||||
no_queue = True
|
||||
qs[jid] = []
|
||||
qs[jid].append((typ, args))
|
||||
self.roster.nb_unread += 1
|
||||
no_queue = len(gajim.events.get_events(account, jid)) == 0
|
||||
event_type = None
|
||||
# type_ can be gc-invitation file-send-error file-error file-request-error
|
||||
# file-request file-completed file-stopped
|
||||
# event_type can be in advancedNotificationWindow.events_list
|
||||
event_types = {'file-request': 'ft_request',
|
||||
'file-completed': 'ft_finished'}
|
||||
if type_ in event_types:
|
||||
event_type = event_types[type_]
|
||||
show_in_roster = notify.get_show_in_roster(event_type, account, jid)
|
||||
show_in_systray = notify.get_show_in_systray(event_type, account, jid)
|
||||
event = gajim.events.create_event(type_, args,
|
||||
show_in_roster = show_in_roster,
|
||||
show_in_systray = show_in_systray)
|
||||
gajim.events.add_event(account, jid, event)
|
||||
|
||||
self.roster.show_title()
|
||||
if no_queue: # We didn't have a queue: we change icons
|
||||
self.roster.draw_contact(jid, account)
|
||||
if self.systray_enabled:
|
||||
self.systray.add_jid(jid, account, typ)
|
||||
|
||||
def redraw_roster_systray(self, account, jid, typ = None):
|
||||
self.roster.nb_unread -= 1
|
||||
self.roster.show_title()
|
||||
self.roster.draw_contact(jid, account)
|
||||
if self.systray_enabled:
|
||||
self.systray.remove_jid(jid, account, typ)
|
||||
def remove_first_event(self, account, jid, type_ = None):
|
||||
event = gajim.events.get_first_event(account, jid, type_)
|
||||
self.remove_event(account, jid, event)
|
||||
|
||||
def remove_first_event(self, account, jid, typ = None):
|
||||
qs = gajim.awaiting_events[account]
|
||||
event = gajim.get_first_event(account, jid, typ)
|
||||
qs[jid].remove(event)
|
||||
# Is it the last event?
|
||||
if not len(qs[jid]):
|
||||
del qs[jid]
|
||||
def remove_event(self, account, jid, event):
|
||||
if gajim.events.remove_events(account, jid, event):
|
||||
# No such event found
|
||||
return
|
||||
# no other event?
|
||||
if not len(gajim.events.get_events(account, jid)):
|
||||
if not gajim.config.get('showoffline'):
|
||||
contact = gajim.contacts.get_contact_with_highest_priority(account,
|
||||
jid)
|
||||
if contact:
|
||||
self.roster.really_remove_contact(contact, account)
|
||||
self.redraw_roster_systray(account, jid, typ)
|
||||
|
||||
def remove_event(self, account, jid, event):
|
||||
qs = gajim.awaiting_events[account]
|
||||
if not event in qs[jid]:
|
||||
return
|
||||
qs[jid].remove(event)
|
||||
# Is it the last event?
|
||||
if not len(qs[jid]):
|
||||
del qs[jid]
|
||||
if not gajim.config.get('showoffline'):
|
||||
contact = gajim.contacts.get_contact_with_highest_priority(account,
|
||||
jid)
|
||||
if contact:
|
||||
self.roster.really_remove_contact(contact, account)
|
||||
self.redraw_roster_systray(account, jid, event[0])
|
||||
self.roster.show_title()
|
||||
self.roster.draw_contact(jid, account)
|
||||
|
||||
def handle_event_file_request_error(self, account, array):
|
||||
jid = array[0]
|
||||
file_props = array[1]
|
||||
# ('FILE_REQUEST_ERROR', account, (jid, file_props, error_msg))
|
||||
jid, file_props, errmsg = array
|
||||
ft = self.instances['file_transfers']
|
||||
ft.set_status(file_props['type'], file_props['sid'], 'stop')
|
||||
errno = file_props['error']
|
||||
|
||||
if helpers.allow_popup_window(account):
|
||||
if errno in (-4, -5):
|
||||
ft.show_stopped(jid, file_props)
|
||||
ft.show_stopped(jid, file_props, errmsg)
|
||||
else:
|
||||
ft.show_request_error(file_props)
|
||||
return
|
||||
|
@ -1114,7 +1193,7 @@ class Interface:
|
|||
if helpers.allow_showing_notification(account):
|
||||
# check if we should be notified
|
||||
img = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events', 'ft_error.png')
|
||||
|
||||
|
||||
path = gtkgui_helpers.get_path_to_generic_or_avatar(img)
|
||||
event_type = _('File Transfer Error')
|
||||
notify.popup(event_type, jid, account, msg_type, path,
|
||||
|
@ -1137,7 +1216,8 @@ class Interface:
|
|||
if helpers.allow_showing_notification(account):
|
||||
img = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events',
|
||||
'ft_request.png')
|
||||
txt = _('%s wants to send you a file.') % gajim.get_name_from_jid(account, jid)
|
||||
txt = _('%s wants to send you a file.') % gajim.get_name_from_jid(
|
||||
account, jid)
|
||||
path = gtkgui_helpers.get_path_to_generic_or_avatar(img)
|
||||
event_type = _('File Transfer Request')
|
||||
notify.popup(event_type, jid, account, 'file-request',
|
||||
|
@ -1146,7 +1226,7 @@ class Interface:
|
|||
def handle_event_file_progress(self, account, file_props):
|
||||
self.instances['file_transfers'].set_progress(file_props['type'],
|
||||
file_props['sid'], file_props['received-len'])
|
||||
|
||||
|
||||
def handle_event_file_rcv_completed(self, account, file_props):
|
||||
ft = self.instances['file_transfers']
|
||||
if file_props['error'] == 0:
|
||||
|
@ -1172,13 +1252,14 @@ class Interface:
|
|||
|
||||
msg_type = ''
|
||||
event_type = ''
|
||||
if file_props['error'] == 0 and gajim.config.get('notify_on_file_complete'):
|
||||
if file_props['error'] == 0 and gajim.config.get(
|
||||
'notify_on_file_complete'):
|
||||
msg_type = 'file-completed'
|
||||
event_type = _('File Transfer Completed')
|
||||
elif file_props['error'] == -1:
|
||||
msg_type = 'file-stopped'
|
||||
event_type = _('File Transfer Stopped')
|
||||
|
||||
|
||||
if event_type == '':
|
||||
# FIXME: ugly workaround (this can happen Gajim sent, Gaim recvs)
|
||||
# this should never happen but it does. see process_result() in socks5.py
|
||||
|
@ -1188,7 +1269,7 @@ class Interface:
|
|||
|
||||
if msg_type:
|
||||
self.add_event(account, jid, msg_type, file_props)
|
||||
|
||||
|
||||
if file_props is not None:
|
||||
if file_props['type'] == 'r':
|
||||
# get the name of the sender, as it is in the roster
|
||||
|
@ -1260,6 +1341,8 @@ class Interface:
|
|||
|
||||
def handle_event_signed_in(self, account, empty):
|
||||
'''SIGNED_IN event is emitted when we sign in, so handle it'''
|
||||
# block signed in notifications for 30 seconds
|
||||
gajim.block_signed_in_notifications[account] = True
|
||||
self.roster.actions_menu_needs_rebuild = True
|
||||
if gajim.interface.sleeper.getState() != common.sleepy.STATE_UNKNOWN and \
|
||||
gajim.connections[account].connected in (2, 3):
|
||||
|
@ -1289,6 +1372,31 @@ class Interface:
|
|||
def handle_event_metacontacts(self, account, tags_list):
|
||||
gajim.contacts.define_metacontacts(account, tags_list)
|
||||
|
||||
def handle_event_privacy_lists_received(self, account, data):
|
||||
# ('PRIVACY_LISTS_RECEIVED', account, list)
|
||||
if not self.instances.has_key(account):
|
||||
return
|
||||
if self.instances[account].has_key('privacy_lists'):
|
||||
self.instances[account]['privacy_lists'].privacy_lists_received(data)
|
||||
|
||||
def handle_event_privacy_list_received(self, account, data):
|
||||
# ('PRIVACY_LISTS_RECEIVED', account, (name, rules))
|
||||
if not self.instances.has_key(account):
|
||||
return
|
||||
name = data[0]
|
||||
rules = data[1]
|
||||
if self.instances[account].has_key('privacy_list_%s' % name):
|
||||
self.instances[account]['privacy_list_%s' % name].\
|
||||
privacy_list_received(rules)
|
||||
|
||||
def handle_event_privacy_lists_active_default(self, account, data):
|
||||
if not data:
|
||||
return
|
||||
# Send to all privacy_list_* windows as we can't know which one asked
|
||||
for win in self.instances[account]:
|
||||
if win.startswith('privacy_list_'):
|
||||
self.instances[account][win].check_active_default(data)
|
||||
|
||||
def read_sleepy(self):
|
||||
'''Check idle status and change that status if needed'''
|
||||
if not self.sleeper.poll():
|
||||
|
@ -1342,12 +1450,12 @@ class Interface:
|
|||
return False
|
||||
|
||||
def show_systray(self):
|
||||
self.systray.show_icon()
|
||||
self.systray_enabled = True
|
||||
self.systray.show_icon()
|
||||
|
||||
def hide_systray(self):
|
||||
self.systray.hide_icon()
|
||||
self.systray_enabled = False
|
||||
self.systray.hide_icon()
|
||||
|
||||
def image_is_ok(self, image):
|
||||
if not os.path.exists(image):
|
||||
|
@ -1577,6 +1685,7 @@ class Interface:
|
|||
'ROSTER_INFO': self.handle_event_roster_info,
|
||||
'BOOKMARKS': self.handle_event_bookmarks,
|
||||
'CON_TYPE': self.handle_event_con_type,
|
||||
'CONNECTION_LOST': self.handle_event_connection_lost,
|
||||
'FILE_REQUEST': self.handle_event_file_request,
|
||||
'GMAIL_NOTIFY': self.handle_event_gmail_notify,
|
||||
'FILE_REQUEST_ERROR': self.handle_event_file_request_error,
|
||||
|
@ -1589,6 +1698,10 @@ class Interface:
|
|||
'ASK_NEW_NICK': self.handle_event_ask_new_nick,
|
||||
'SIGNED_IN': self.handle_event_signed_in,
|
||||
'METACONTACTS': self.handle_event_metacontacts,
|
||||
'PRIVACY_LISTS_RECEIVED': self.handle_event_privacy_lists_received,
|
||||
'PRIVACY_LIST_RECEIVED': self.handle_event_privacy_list_received,
|
||||
'PRIVACY_LISTS_ACTIVE_DEFAULT': \
|
||||
self.handle_event_privacy_lists_active_default,
|
||||
}
|
||||
gajim.handlers = self.handlers
|
||||
|
||||
|
@ -1608,14 +1721,14 @@ class Interface:
|
|||
err_str)
|
||||
sys.exit()
|
||||
|
||||
def handle_event(self, account, jid, typ):
|
||||
def handle_event(self, account, jid, type_):
|
||||
w = None
|
||||
fjid = jid
|
||||
resource = gajim.get_resource_from_jid(jid)
|
||||
jid = gajim.get_jid_without_resource(jid)
|
||||
if typ == message_control.TYPE_GC:
|
||||
if type_ in ('printed_gc_msg', 'gc_msg'):
|
||||
w = self.msg_win_mgr.get_window(jid, account)
|
||||
elif typ == message_control.TYPE_CHAT:
|
||||
elif type_ in ('printed_chat', 'chat'):
|
||||
if self.msg_win_mgr.has_window(fjid, account):
|
||||
w = self.msg_win_mgr.get_window(fjid, account)
|
||||
else:
|
||||
|
@ -1625,30 +1738,30 @@ class Interface:
|
|||
self.roster.new_chat(contact, account, resource = resource)
|
||||
w = self.msg_win_mgr.get_window(fjid, account)
|
||||
gajim.last_message_time[account][jid] = 0 # long time ago
|
||||
elif typ == message_control.TYPE_PM:
|
||||
elif type_ in ('printed_pm', 'pm'):
|
||||
if self.msg_win_mgr.has_window(fjid, account):
|
||||
w = self.msg_win_mgr.get_window(fjid, account)
|
||||
else:
|
||||
room_jid = jid
|
||||
nick = resource
|
||||
gc_contact = gajim.contacts.get_gc_contact(account, room_jid,
|
||||
nick)
|
||||
nick)
|
||||
if gc_contact:
|
||||
show = gc_contact.show
|
||||
else:
|
||||
show = 'offline'
|
||||
gc_contact = gajim.contacts.create_gc_contact(room_jid = room_jid,
|
||||
name = nick, show = show)
|
||||
gc_contact = gajim.contacts.create_gc_contact(
|
||||
room_jid = room_jid, name = nick, show = show)
|
||||
c = gajim.contacts.contact_from_gc_contact(gc_contact)
|
||||
self.roster.new_chat(c, account, private_chat = True)
|
||||
w = self.msg_win_mgr.get_window(fjid, account)
|
||||
elif typ in ('normal', 'file-request', 'file-request-error',
|
||||
'file-send-error', 'file-error', 'file-stopped', 'file-completed'):
|
||||
elif type_ in ('normal', 'file-request', 'file-request-error',
|
||||
'file-send-error', 'file-error', 'file-stopped', 'file-completed'):
|
||||
# Get the first single message event
|
||||
ev = gajim.get_first_event(account, jid, typ)
|
||||
event = gajim.events.get_first_event(account, jid, type_)
|
||||
# Open the window
|
||||
self.roster.open_event(account, jid, ev)
|
||||
elif typ == 'gmail':
|
||||
self.roster.open_event(account, jid, event)
|
||||
elif type_ == 'gmail':
|
||||
if gajim.config.get_per('accounts', account, 'savepass'):
|
||||
url = ('http://www.google.com/accounts/ServiceLoginAuth?service=mail&Email=%s&Passwd=%s&continue=https://mail.google.com/mail') %\
|
||||
(urllib.quote(gajim.config.get_per('accounts', account, 'name')),
|
||||
|
@ -1656,12 +1769,12 @@ class Interface:
|
|||
else:
|
||||
url = ('http://mail.google.com/')
|
||||
helpers.launch_browser_mailer('url', url)
|
||||
elif typ == 'gc-invitation':
|
||||
ev = gajim.get_first_event(account, jid, typ)
|
||||
data = ev[1]
|
||||
elif type_ == 'gc-invitation':
|
||||
event = gajim.events.get_first_event(account, jid, type_)
|
||||
data = event.parameters
|
||||
dialogs.InvitationReceivedDialog(account, data[0], jid, data[2],
|
||||
data[1])
|
||||
self.remove_first_event(account, jid, typ)
|
||||
gajim.events.remove_events(account, jid, event)
|
||||
if w:
|
||||
w.set_active_tab(fjid, account)
|
||||
w.window.present()
|
||||
|
@ -1753,14 +1866,13 @@ class Interface:
|
|||
self.instances = {'logs': {}}
|
||||
|
||||
for a in gajim.connections:
|
||||
self.instances[a] = {'infos': {}, 'disco': {}, 'chats': {},
|
||||
'gc': {}, 'gc_config': {}}
|
||||
self.instances[a] = {'infos': {}, 'disco': {}, 'gc_config': {}}
|
||||
gajim.contacts.add_account(a)
|
||||
gajim.groups[a] = {}
|
||||
gajim.gc_connected[a] = {}
|
||||
gajim.automatic_rooms[a] = {}
|
||||
gajim.newly_added[a] = []
|
||||
gajim.to_be_removed[a] = []
|
||||
gajim.awaiting_events[a] = {}
|
||||
gajim.nicks[a] = gajim.config.get_per('accounts', a, 'name')
|
||||
gajim.block_signed_in_notifications[a] = True
|
||||
gajim.sleeper_state[a] = 0
|
||||
|
@ -1814,12 +1926,34 @@ class Interface:
|
|||
# get instances for windows/dialogs that will show_all()/hide()
|
||||
self.instances['file_transfers'] = dialogs.FileTransfersWindow()
|
||||
|
||||
# get transports type from DB
|
||||
gajim.transport_type = gajim.logger.get_transports_type()
|
||||
|
||||
# test is dictionnary is present for speller
|
||||
if gajim.config.get('use_speller'):
|
||||
lang = gajim.config.get('speller_language')
|
||||
if not lang:
|
||||
lang = gajim.LANG
|
||||
tv = gtk.TextView()
|
||||
try:
|
||||
import gtkspell
|
||||
spell = gtkspell.Spell(tv, lang)
|
||||
except:
|
||||
dialogs.ErrorDialog(
|
||||
_('Dictionary for lang %s not available') % lang,
|
||||
_('You have to install %s dictionary to use spellchecking, or '
|
||||
'choose another language by setting the speller_language option.'
|
||||
) % lang)
|
||||
gajim.config.set('use_speller', False)
|
||||
gobject.timeout_add(100, self.autoconnect)
|
||||
gobject.timeout_add(200, self.process_connections)
|
||||
gobject.timeout_add(500, self.read_sleepy)
|
||||
|
||||
if __name__ == '__main__':
|
||||
signal.signal(signal.SIGINT, signal.SIG_DFL) # ^C exits the application
|
||||
def sigint_cb(num, stack):
|
||||
sys.exit(5)
|
||||
# ^C exits the application normally to delete pid file
|
||||
signal.signal(signal.SIGINT, sigint_cb)
|
||||
|
||||
if os.name != 'nt':
|
||||
# Session Management support
|
||||
|
@ -1847,33 +1981,7 @@ if __name__ == '__main__':
|
|||
cli.set_restart_command(len(argv), argv)
|
||||
|
||||
gtkgui_helpers.possibly_set_gajim_as_xmpp_handler()
|
||||
|
||||
|
||||
# Migrate old logs if we have such olds logs
|
||||
from common import logger
|
||||
from migrate_logs_to_dot9_db import PATH_TO_LOGS_BASE_DIR
|
||||
LOG_DB_PATH = logger.LOG_DB_PATH
|
||||
if not os.path.exists(LOG_DB_PATH) and os.path.isdir(PATH_TO_LOGS_BASE_DIR):
|
||||
import Queue
|
||||
q = Queue.Queue(100)
|
||||
dialog = dialogs.ProgressDialog(_('Migrating Logs...'),
|
||||
_('Please wait while logs are being migrated...'), q)
|
||||
if os.name == 'nt' and gtk.pygtk_version > (2, 8, 0):
|
||||
idlequeue = idlequeue.SelectIdleQueue()
|
||||
else:
|
||||
idlequeue = GlibIdleQueue()
|
||||
def on_result(*arg):
|
||||
dialog.dialog.destroy()
|
||||
dialog.dialog = None
|
||||
gobject.source_remove(dialog.update_progressbar_timeout_id)
|
||||
gajim.logger.init_vars()
|
||||
check_paths.check_and_possibly_create_paths()
|
||||
Interface()
|
||||
m = MigrateCommand(on_result)
|
||||
m.set_idlequeue(idlequeue)
|
||||
m.start()
|
||||
else:
|
||||
check_paths.check_and_possibly_create_paths()
|
||||
Interface()
|
||||
|
||||
check_paths.check_and_possibly_create_paths()
|
||||
Interface()
|
||||
gtk.main()
|
||||
|
||||
|
|
|
@ -25,23 +25,18 @@
|
|||
##
|
||||
|
||||
import gtk
|
||||
import gtk.glade
|
||||
import pango
|
||||
import dialogs
|
||||
import gtkgui_helpers
|
||||
|
||||
from common import gajim
|
||||
from common import i18n
|
||||
_ = i18n._
|
||||
APP = i18n.APP
|
||||
gtk.glade.bindtextdomain (APP, i18n.DIR)
|
||||
gtk.glade.textdomain (APP)
|
||||
|
||||
class GajimThemesWindow:
|
||||
|
||||
def __init__(self):
|
||||
self.xml = gtkgui_helpers.get_glade('gajim_themes_window.glade')
|
||||
self.window = self.xml.get_widget('gajim_themes_window')
|
||||
self.window.set_transient_for(gajim.interface.roster.window)
|
||||
|
||||
self.options = ['account', 'group', 'contact', 'banner']
|
||||
self.options_combobox = self.xml.get_widget('options_combobox')
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
import os
|
||||
import time
|
||||
import gtk
|
||||
import gtk.glade
|
||||
import pango
|
||||
import gobject
|
||||
import gtkgui_helpers
|
||||
|
@ -40,13 +39,6 @@ from common import helpers
|
|||
from chat_control import ChatControl
|
||||
from chat_control import ChatControlBase
|
||||
from conversation_textview import ConversationTextview
|
||||
from common import i18n
|
||||
|
||||
_ = i18n._
|
||||
Q_ = i18n.Q_
|
||||
APP = i18n.APP
|
||||
gtk.glade.bindtextdomain(APP, i18n.DIR)
|
||||
gtk.glade.textdomain(APP)
|
||||
|
||||
#(status_image, type, nick, shown_nick)
|
||||
(
|
||||
|
@ -127,6 +119,13 @@ class PrivateChatControl(ChatControl):
|
|||
return
|
||||
|
||||
ChatControl.send_message(self, message)
|
||||
|
||||
def update_ui(self):
|
||||
if self.contact.show == 'offline':
|
||||
self.got_disconnected()
|
||||
else:
|
||||
self.got_connected()
|
||||
ChatControl.update_ui(self)
|
||||
|
||||
|
||||
class GroupchatControl(ChatControlBase):
|
||||
|
@ -183,7 +182,7 @@ class GroupchatControl(ChatControlBase):
|
|||
# alphanum sorted
|
||||
self.muc_cmds = ['ban', 'chat', 'query', 'clear', 'close', 'compact',
|
||||
'help', 'invite', 'join', 'kick', 'leave', 'me', 'msg', 'nick', 'part',
|
||||
'say', 'topic']
|
||||
'names', 'say', 'topic']
|
||||
# muc attention flag (when we are mentioned in a muc)
|
||||
# if True, the room has mentioned us
|
||||
self.attention_flag = False
|
||||
|
@ -197,10 +196,6 @@ class GroupchatControl(ChatControlBase):
|
|||
|
||||
self.tooltip = tooltips.GCTooltip()
|
||||
|
||||
self.allow_focus_out_line = True
|
||||
# holds the iter's offset which points to the end of --- line
|
||||
self.focus_out_end_iter_offset = None
|
||||
|
||||
# connect the menuitems to their respective functions
|
||||
xm = gtkgui_helpers.get_glade('gc_control_popup_menu.glade')
|
||||
|
||||
|
@ -292,12 +287,9 @@ class GroupchatControl(ChatControlBase):
|
|||
column.set_visible(False)
|
||||
self.list_treeview.set_expander_column(column)
|
||||
|
||||
id = self.msg_textview.connect('populate_popup',
|
||||
self.on_msg_textview_populate_popup)
|
||||
self.handlers[id] = self.msg_textview
|
||||
# set an empty subject to show the room_jid
|
||||
self.set_subject('')
|
||||
self.got_disconnected() #init some variables
|
||||
self.got_disconnected() # init some variables
|
||||
|
||||
self.update_ui()
|
||||
self.conv_textview.tv.grab_focus()
|
||||
|
@ -306,20 +298,18 @@ class GroupchatControl(ChatControlBase):
|
|||
def on_msg_textview_populate_popup(self, textview, menu):
|
||||
'''we override the default context menu and we prepend Clear
|
||||
and the ability to insert a nick'''
|
||||
ChatControlBase.on_msg_textview_populate_popup(self, textview, menu)
|
||||
item = gtk.SeparatorMenuItem()
|
||||
menu.prepend(item)
|
||||
item = gtk.ImageMenuItem(gtk.STOCK_CLEAR)
|
||||
menu.prepend(item)
|
||||
id = item.connect('activate', self.msg_textview.clear)
|
||||
self.handlers[id] = item
|
||||
|
||||
item = gtk.MenuItem(_('Insert Nickname'))
|
||||
menu.prepend(item)
|
||||
submenu = gtk.Menu()
|
||||
item.set_submenu(submenu)
|
||||
|
||||
for nick in gajim.contacts.get_nick_list(self.account, self.room_jid):
|
||||
item = gtk.MenuItem(nick)
|
||||
for nick in sorted(gajim.contacts.get_nick_list(self.account,
|
||||
self.room_jid)):
|
||||
item = gtk.MenuItem(nick, use_underline = False)
|
||||
submenu.append(item)
|
||||
id = item.connect('activate', self.append_nick_in_msg_textview, nick)
|
||||
self.handlers[id] = item
|
||||
|
@ -333,7 +323,7 @@ class GroupchatControl(ChatControlBase):
|
|||
def _on_window_focus_in_event(self, widget, event):
|
||||
'''When window gets focus'''
|
||||
if self.parent_win.get_active_jid() == self.room_jid:
|
||||
self.allow_focus_out_line = True
|
||||
self.conv_textview.allow_focus_out_line = True
|
||||
|
||||
def on_treeview_size_allocate(self, widget, allocation):
|
||||
'''The MUC treeview has resized. Move the hpaned in all tabs to match'''
|
||||
|
@ -440,21 +430,21 @@ class GroupchatControl(ChatControlBase):
|
|||
childs[3].set_sensitive(False)
|
||||
return menu
|
||||
|
||||
def on_message(self, nick, msg, tim):
|
||||
def on_message(self, nick, msg, tim, has_timestamp = False):
|
||||
if not nick:
|
||||
# message from server
|
||||
self.print_conversation(msg, tim = tim)
|
||||
else:
|
||||
# message from someone
|
||||
self.print_conversation(msg, nick, tim)
|
||||
if has_timestamp:
|
||||
self.print_old_conversation(msg, nick, tim)
|
||||
else:
|
||||
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
|
||||
no_queue = len(gajim.events.get_events(self.account, fjid)) == 0
|
||||
|
||||
# We print if window is opened
|
||||
pm_control = gajim.interface.msg_win_mgr.get_control(fjid, self.account)
|
||||
|
@ -462,9 +452,9 @@ class GroupchatControl(ChatControlBase):
|
|||
pm_control.print_conversation(msg, tim = tim)
|
||||
return
|
||||
|
||||
if no_queue:
|
||||
qs[fjid] = []
|
||||
qs[fjid].append(('chat', (msg, '', 'incoming', tim, False, '')))
|
||||
event = gajim.events.create_event('pm', (msg, '', 'incoming', tim,
|
||||
False, '', None))
|
||||
gajim.events.add_event(self.account, fjid, event)
|
||||
|
||||
autopopup = gajim.config.get('autopopup')
|
||||
autopopupaway = gajim.config.get('autopopupaway')
|
||||
|
@ -479,8 +469,6 @@ class GroupchatControl(ChatControlBase):
|
|||
self.room_jid, icon_name = 'message')
|
||||
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:
|
||||
self._start_private_message(nick)
|
||||
|
@ -511,6 +499,24 @@ class GroupchatControl(ChatControlBase):
|
|||
fin = True
|
||||
return None
|
||||
|
||||
gc_count_nicknames_colors = 0
|
||||
gc_custom_colors = {}
|
||||
|
||||
def print_old_conversation(self, text, contact, tim = None):
|
||||
if isinstance(text, str):
|
||||
text = unicode(text, 'utf-8')
|
||||
if contact == self.nick: # it's us
|
||||
kind = 'outgoing'
|
||||
else:
|
||||
kind = 'incoming'
|
||||
if gajim.config.get('restored_messages_small'):
|
||||
small_attr = ['small']
|
||||
else:
|
||||
small_attr = []
|
||||
ChatControlBase.print_conversation_line(self, text, kind, contact, tim,
|
||||
small_attr, small_attr + ['restored_message'],
|
||||
small_attr + ['restored_message'])
|
||||
|
||||
def print_conversation(self, text, contact = '', tim = None):
|
||||
'''Print a line in the conversation:
|
||||
if contact is set: it's a message from someone or an info message (contact
|
||||
|
@ -536,6 +542,19 @@ class GroupchatControl(ChatControlBase):
|
|||
if kind == 'incoming': # it's a message NOT from us
|
||||
# highlighting and sounds
|
||||
(highlight, sound) = self.highlighting_for_message(text, tim)
|
||||
if self.gc_custom_colors.has_key(contact):
|
||||
other_tags_for_name.append('gc_nickname_color_' + \
|
||||
str(self.gc_custom_colors[contact]))
|
||||
else:
|
||||
self.gc_count_nicknames_colors += 1
|
||||
number_of_colors = len(gajim.config.get('gc_nicknames_colors').\
|
||||
split(':'))
|
||||
if self.gc_count_nicknames_colors == number_of_colors:
|
||||
self.gc_count_nicknames_colors = 0
|
||||
self.gc_custom_colors[contact] = \
|
||||
self.gc_count_nicknames_colors
|
||||
other_tags_for_name.append('gc_nickname_color_' + \
|
||||
str(self.gc_count_nicknames_colors))
|
||||
if highlight:
|
||||
# muc-specific chatstate
|
||||
self.parent_win.redraw_tab(self, 'attention')
|
||||
|
@ -545,12 +564,23 @@ class GroupchatControl(ChatControlBase):
|
|||
helpers.play_sound('muc_message_received')
|
||||
elif sound == 'highlight':
|
||||
helpers.play_sound('muc_message_highlight')
|
||||
if text.startswith('/me ') or text.startswith('/me\n'):
|
||||
other_tags_for_text.append('gc_nickname_color_' + \
|
||||
str(self.gc_custom_colors[contact]))
|
||||
|
||||
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 get_nb_unread(self):
|
||||
nb = len(gajim.events.get_events(self.account, self.room_jid,
|
||||
['printed_gc_msg']))
|
||||
for nick in gajim.contacts.get_nick_list(self.account, self.room_jid):
|
||||
nb += len(gajim.events.get_events(self.account, self.room_jid + '/' + \
|
||||
nick, ['pm']))
|
||||
return nb
|
||||
|
||||
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.'''
|
||||
|
@ -587,64 +617,7 @@ class GroupchatControl(ChatControlBase):
|
|||
# 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.tv.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)
|
||||
self.conv_textview.show_focus_out_line()
|
||||
|
||||
def needs_visual_notification(self, text):
|
||||
'''checks text to see whether any of the words in (muc_highlight_words
|
||||
|
@ -740,7 +713,7 @@ class GroupchatControl(ChatControlBase):
|
|||
model = self.list_treeview.get_model()
|
||||
gc_contact = gajim.contacts.get_gc_contact(self.account, self.room_jid, nick)
|
||||
state_images = gajim.interface.roster.jabber_state_images['16']
|
||||
if gajim.awaiting_events[self.account].has_key(self.room_jid + '/' + nick):
|
||||
if len(gajim.events.get_events(self.account, self.room_jid + '/' + nick)):
|
||||
image = state_images['message']
|
||||
else:
|
||||
image = state_images[gc_contact.show]
|
||||
|
@ -823,9 +796,29 @@ class GroupchatControl(ChatControlBase):
|
|||
# after that, but that doesn't hurt
|
||||
self.add_contact_to_roster(new_nick, show, role, affiliation,
|
||||
status, jid)
|
||||
# keep nickname color
|
||||
if nick in self.gc_custom_colors:
|
||||
self.gc_custom_colors[new_nick] = self.gc_custom_colors[nick]
|
||||
# rename vcard / avatar
|
||||
puny_jid = helpers.sanitize_filename(self.room_jid)
|
||||
puny_nick = helpers.sanitize_filename(nick)
|
||||
puny_new_nick = helpers.sanitize_filename(new_nick)
|
||||
old_path = os.path.join(gajim.VCARD_PATH, puny_jid, puny_nick)
|
||||
new_path = os.path.join(gajim.VCARD_PATH, puny_jid, puny_new_nick)
|
||||
files = {old_path: new_path}
|
||||
path = os.path.join(gajim.AVATAR_PATH, puny_jid)
|
||||
# possible extensions
|
||||
for ext in ('.png', '.jpeg', '_notif_size_bw.png',
|
||||
'_notif_size_colored.png'):
|
||||
files[os.path.join(path, puny_nick + ext)] = \
|
||||
os.path.join(path, puny_new_nick + ext)
|
||||
for old_file in files:
|
||||
if os.path.exists(old_file):
|
||||
os.rename(old_file, files[old_file])
|
||||
self.print_conversation(s, 'info')
|
||||
|
||||
if not gajim.awaiting_events[self.account].has_key(self.room_jid + '/' + nick):
|
||||
if len(gajim.events.get_events(self.account,
|
||||
self.room_jid + '/' + nick)) == 0:
|
||||
self.remove_contact(nick)
|
||||
else:
|
||||
c = gajim.contacts.get_gc_contact(self.account, self.room_jid, nick)
|
||||
|
@ -870,15 +863,18 @@ class GroupchatControl(ChatControlBase):
|
|||
break
|
||||
if print_status is None:
|
||||
print_status = gajim.config.get('print_status_in_muc')
|
||||
nick_jid = nick
|
||||
if jid:
|
||||
nick_jid += ' (%s)' % jid
|
||||
if show == 'offline' and print_status in ('all', 'in_and_out'):
|
||||
st = _('%s has left') % nick
|
||||
st = _('%s has left') % nick_jid
|
||||
if reason:
|
||||
st += ' [%s]' % reason
|
||||
else:
|
||||
if newly_created and print_status in ('all', 'in_and_out'):
|
||||
st = _('%s has joined the room') % nick
|
||||
st = _('%s has joined the room') % nick_jid
|
||||
elif print_status == 'all':
|
||||
st = _('%s is now %s') % (nick, helpers.get_uf_show(show))
|
||||
st = _('%s is now %s') % (nick_jid, helpers.get_uf_show(show))
|
||||
if st:
|
||||
if status:
|
||||
st += ' (' + status + ')'
|
||||
|
@ -974,7 +970,7 @@ class GroupchatControl(ChatControlBase):
|
|||
|
||||
if command == 'nick':
|
||||
# example: /nick foo
|
||||
if len(message_array):
|
||||
if len(message_array) and message_array[0] != self.nick:
|
||||
nick = message_array[0]
|
||||
gajim.connections[self.account].change_gc_nick(self.room_jid, nick)
|
||||
self.clear(self.msg_textview)
|
||||
|
@ -985,14 +981,19 @@ class GroupchatControl(ChatControlBase):
|
|||
# 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)
|
||||
nick0 = message_array.pop(0)
|
||||
if nick0[-1] == ' ':
|
||||
nick1 = nick0[:-1]
|
||||
else:
|
||||
self.print_conversation(_('Nickname not found: %s') % nick,
|
||||
'info')
|
||||
nick1 = nick0
|
||||
nicks = gajim.contacts.get_nick_list(self.account, self.room_jid)
|
||||
for nick in [nick0, nick1]:
|
||||
if nick in nicks:
|
||||
self.on_send_pm(nick = nick)
|
||||
self.clear(self.msg_textview)
|
||||
return True
|
||||
self.print_conversation(_('Nickname not found: %s') % \
|
||||
nick0, 'info')
|
||||
else:
|
||||
self.get_command_help(command)
|
||||
return True
|
||||
|
@ -1002,7 +1003,8 @@ class GroupchatControl(ChatControlBase):
|
|||
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)
|
||||
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)
|
||||
|
@ -1122,6 +1124,21 @@ class GroupchatControl(ChatControlBase):
|
|||
else:
|
||||
self.get_command_help(command)
|
||||
return True
|
||||
elif command == 'names':
|
||||
# print the list of participants
|
||||
nicklist=''
|
||||
i=0
|
||||
for contact in self.iter_contact_rows():
|
||||
nicklist += '[ %-12.12s ] ' % (contact[C_NICK].decode('utf-8'))
|
||||
i=i+1
|
||||
if i == 3:
|
||||
i=0
|
||||
self.print_conversation(nicklist, 'info')
|
||||
nicklist=''
|
||||
if nicklist:
|
||||
self.print_conversation(nicklist, 'info')
|
||||
self.clear(self.msg_textview)
|
||||
return True
|
||||
elif command == 'help':
|
||||
if len(message_array):
|
||||
subcommand = message_array.pop(0)
|
||||
|
@ -1205,6 +1222,10 @@ class GroupchatControl(ChatControlBase):
|
|||
s = _('Usage: /%s <nickname>, changes your nickname in current room.')\
|
||||
% command
|
||||
self.print_conversation(s, 'info')
|
||||
elif command == 'names':
|
||||
s = _('Usage: /%s , display the names of room occupants.')\
|
||||
% command
|
||||
self.print_conversation(s, 'info')
|
||||
elif command == 'topic':
|
||||
self.print_conversation(_('Usage: /%s [topic], displays or updates the'
|
||||
' current room topic.') % command, 'info')
|
||||
|
@ -1287,9 +1308,8 @@ class GroupchatControl(ChatControlBase):
|
|||
nb = 0
|
||||
for nick in gajim.contacts.get_nick_list(self.account, self.room_jid):
|
||||
fjid = self.room_jid + '/' + nick
|
||||
if gajim.awaiting_events[self.account].has_key(fjid):
|
||||
# gc can only have messages as event
|
||||
nb += len(gajim.awaiting_events[self.account][fjid])
|
||||
nb += len(gajim.events.get_events(self.account, fjid))
|
||||
# gc can only have messages as event
|
||||
return nb
|
||||
|
||||
def _on_change_subject_menuitem_activate(self, widget):
|
||||
|
@ -1559,8 +1579,8 @@ class GroupchatControl(ChatControlBase):
|
|||
|
||||
# show the popup now!
|
||||
menu = xml.get_widget('gc_occupants_menu')
|
||||
menu.popup(None, None, None, event.button, event.time)
|
||||
menu.show_all()
|
||||
menu.popup(None, None, None, event.button, event.time)
|
||||
|
||||
def _start_private_message(self, nick):
|
||||
gc_c = gajim.contacts.get_gc_contact(self.account, self.room_jid, nick)
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
##
|
||||
|
||||
import sys
|
||||
import os
|
||||
import traceback
|
||||
import threading
|
||||
|
||||
|
@ -36,9 +37,7 @@ import dialogs
|
|||
|
||||
from cStringIO import StringIO
|
||||
from common import helpers
|
||||
from common import i18n
|
||||
|
||||
_ = i18n._
|
||||
_exception_in_progress = threading.Lock()
|
||||
|
||||
def _info(type, value, tb):
|
||||
|
@ -102,7 +101,8 @@ def _info(type, value, tb):
|
|||
|
||||
_exception_in_progress.release()
|
||||
|
||||
if not sys.stderr.isatty(): # gdb/kdm etc if we use startx this is not True
|
||||
# gdb/kdm etc if we use startx this is not True
|
||||
if os.name == 'nt' or not sys.stderr.isatty():
|
||||
#FIXME: maybe always show dialog?
|
||||
_excepthook_save = sys.excepthook
|
||||
sys.excepthook = _info
|
||||
|
|
|
@ -19,12 +19,14 @@
|
|||
|
||||
import xml.sax.saxutils
|
||||
import gtk
|
||||
import gtk.glade
|
||||
import gobject
|
||||
import pango
|
||||
import os
|
||||
import sys
|
||||
|
||||
import vcard
|
||||
import dialogs
|
||||
|
||||
|
||||
HAS_PYWIN32 = True
|
||||
|
@ -37,11 +39,12 @@ if os.name == 'nt':
|
|||
HAS_PYWIN32 = False
|
||||
|
||||
from common import i18n
|
||||
i18n.init()
|
||||
_ = i18n._
|
||||
from common import gajim
|
||||
from common import helpers
|
||||
|
||||
gtk.glade.bindtextdomain(i18n.APP, i18n.DIR)
|
||||
gtk.glade.textdomain(i18n.APP)
|
||||
|
||||
screen_w = gtk.gdk.screen_width()
|
||||
screen_h = gtk.gdk.screen_height()
|
||||
|
||||
|
@ -123,8 +126,8 @@ def get_default_font():
|
|||
# in try because daemon may not be there
|
||||
client = gconf.client_get_default()
|
||||
|
||||
return helpers.ensure_unicode_string(
|
||||
client.get_string('/desktop/gnome/interface/font_name'))
|
||||
return client.get_string('/desktop/gnome/interface/font_name'
|
||||
).decode('utf-8')
|
||||
except:
|
||||
pass
|
||||
|
||||
|
@ -144,8 +147,7 @@ def get_default_font():
|
|||
for line in file(xfce_config_file):
|
||||
if line.find('name="Gtk/FontName"') != -1:
|
||||
start = line.find('value="') + 7
|
||||
return helpers.ensure_unicode_string(
|
||||
line[start:line.find('"', start)])
|
||||
return line[start:line.find('"', start)].decode('utf-8')
|
||||
except:
|
||||
#we talk about file
|
||||
print >> sys.stderr, _('Error: cannot open %s for reading') % xfce_config_file
|
||||
|
@ -160,7 +162,7 @@ def get_default_font():
|
|||
font_name = values[0]
|
||||
font_size = values[1]
|
||||
font_string = '%s %s' % (font_name, font_size) # Verdana 9
|
||||
return helpers.ensure_unicode_string(font_string)
|
||||
return font_string.decode('utf-8')
|
||||
except:
|
||||
#we talk about file
|
||||
print >> sys.stderr, _('Error: cannot open %s for reading') % kde_config_file
|
||||
|
@ -403,7 +405,7 @@ def possibly_move_window_in_current_desktop(window):
|
|||
if current_virtual_desktop_no != window_virtual_desktop:
|
||||
# we are in another VD that the window was
|
||||
# so show it in current VD
|
||||
window.show()
|
||||
window.present()
|
||||
|
||||
def file_is_locked(path_to_file):
|
||||
'''returns True if file is locked (WINDOWS ONLY)'''
|
||||
|
@ -455,7 +457,7 @@ def _get_fade_color(treeview, selected, focused):
|
|||
|
||||
def get_scaled_pixbuf(pixbuf, kind):
|
||||
'''returns scaled pixbuf, keeping ratio etc or None
|
||||
kind is either "chat" or "roster" or "notification" or "tooltip"'''
|
||||
kind is either "chat", "roster", "notification", "tooltip", "vcard"'''
|
||||
|
||||
# resize to a width / height for the avatar not to have distortion
|
||||
# (keep aspect ratio)
|
||||
|
@ -666,3 +668,70 @@ def get_possible_button_event(event):
|
|||
|
||||
def destroy_widget(widget):
|
||||
widget.destroy()
|
||||
|
||||
def on_avatar_save_as_menuitem_activate(widget, jid, account,
|
||||
default_name = ''):
|
||||
def on_ok(widget):
|
||||
def on_ok2(widget, file_path, pixbuf):
|
||||
pixbuf.save(file_path, 'jpeg')
|
||||
dialog2.destroy()
|
||||
dialog.destroy()
|
||||
|
||||
file_path = dialog.get_filename()
|
||||
file_path = decode_filechooser_file_paths((file_path,))[0]
|
||||
if os.path.exists(file_path):
|
||||
dialog2 = dialogs.FTOverwriteConfirmationDialog(
|
||||
_('This file already exists'), _('What do you want to do?'),
|
||||
False)
|
||||
dialog2.set_transient_for(dialog)
|
||||
dialog2.set_destroy_with_parent(True)
|
||||
response = dialog2.get_response()
|
||||
if response < 0:
|
||||
return
|
||||
|
||||
# Get pixbuf
|
||||
pixbuf = None
|
||||
is_fake = False
|
||||
if account and gajim.contacts.is_pm_from_jid(account, jid):
|
||||
is_fake = True
|
||||
pixbuf = get_avatar_pixbuf_from_cache(jid, is_fake)
|
||||
ext = file_path.split('.')[-1]
|
||||
type_ = ''
|
||||
if not ext:
|
||||
# Silently save as Jpeg image
|
||||
file_path += '.jpeg'
|
||||
type_ = 'jpeg'
|
||||
elif ext == 'jpg':
|
||||
type_ = 'jpeg'
|
||||
else:
|
||||
type_ = ext
|
||||
|
||||
# Save image
|
||||
try:
|
||||
pixbuf.save(file_path, type_)
|
||||
except:
|
||||
#XXX Check for permissions
|
||||
os.remove(file_path)
|
||||
new_file_path = '.'.join(file_path.split('.')[:-1]) + '.jpeg'
|
||||
dialog2 = dialogs.ConfirmationDialog(_('Extension not supported'),
|
||||
_('Image cannot be saved in %(type)s format. Save as %(new_filename)s?') % {'type': type_, 'new_filename': new_file_path},
|
||||
on_response_ok = (on_ok2, new_file_path, pixbuf))
|
||||
else:
|
||||
dialog.destroy()
|
||||
|
||||
def on_cancel(widget):
|
||||
dialog.destroy()
|
||||
|
||||
dialog = dialogs.FileChooserDialog(
|
||||
title_text = _('Save Image as...'),
|
||||
action = gtk.FILE_CHOOSER_ACTION_SAVE,
|
||||
buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
|
||||
gtk.STOCK_SAVE, gtk.RESPONSE_OK),
|
||||
default_response = gtk.RESPONSE_OK,
|
||||
current_folder = gajim.config.get('last_save_dir'),
|
||||
on_response_ok = on_ok,
|
||||
on_response_cancel = on_cancel)
|
||||
|
||||
dialog.set_current_name(default_name)
|
||||
dialog.connect('delete-event', lambda widget, event:
|
||||
on_cancel(widget))
|
||||
|
|
|
@ -24,21 +24,17 @@ import sys
|
|||
import os
|
||||
import signal
|
||||
import gtk
|
||||
import gtk.glade
|
||||
import time
|
||||
import locale
|
||||
|
||||
from common import i18n
|
||||
import exceptions
|
||||
import dialogs
|
||||
import gtkgui_helpers
|
||||
from common.logger import LOG_DB_PATH, constants
|
||||
|
||||
from common import gajim
|
||||
from common import i18n
|
||||
from common import helpers
|
||||
_ = i18n._
|
||||
gtk.glade.bindtextdomain(i18n.APP, i18n.DIR)
|
||||
gtk.glade.textdomain(i18n.APP)
|
||||
|
||||
# time, message, subject
|
||||
(
|
||||
|
@ -468,8 +464,8 @@ class HistoryManager:
|
|||
except ValueError:
|
||||
pass
|
||||
|
||||
file_.write(_('%(who)s on %(time)s said: %(message)s\n' % {'who': who,
|
||||
'time': time_, 'message': message}))
|
||||
file_.write(_('%(who)s on %(time)s said: %(message)s\n') % {'who': who,
|
||||
'time': time_, 'message': message})
|
||||
|
||||
def _delete_jid_logs(self, liststore, list_of_paths):
|
||||
paths_len = len(list_of_paths)
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
##
|
||||
|
||||
import gtk
|
||||
import gtk.glade
|
||||
import gobject
|
||||
import time
|
||||
import calendar
|
||||
|
@ -34,17 +33,11 @@ import conversation_textview
|
|||
|
||||
from common import gajim
|
||||
from common import helpers
|
||||
from common import i18n
|
||||
|
||||
from common.logger import Constants
|
||||
|
||||
constants = Constants()
|
||||
|
||||
_ = i18n._
|
||||
APP = i18n.APP
|
||||
gtk.glade.bindtextdomain(APP, i18n.DIR)
|
||||
gtk.glade.textdomain(APP)
|
||||
|
||||
# contact_name, date, message, time
|
||||
(
|
||||
C_CONTACT_NAME,
|
||||
|
@ -120,7 +113,7 @@ class HistoryWindow:
|
|||
|
||||
# select and show logs for last date we have logs with contact
|
||||
# and if we don't have logs at all, default to today
|
||||
result = gajim.logger.get_last_date_that_has_logs(self.jid)
|
||||
result = gajim.logger.get_last_date_that_has_logs(self.jid, self.account)
|
||||
if result is None:
|
||||
date = time.localtime()
|
||||
else:
|
||||
|
@ -157,7 +150,7 @@ class HistoryWindow:
|
|||
asks for days in this month if they have logs it bolds them (marks them)'''
|
||||
weekday, days_in_this_month = calendar.monthrange(year, month)
|
||||
log_days = gajim.logger.get_days_with_logs(self.jid, year,
|
||||
month, days_in_this_month)
|
||||
month, days_in_this_month, self.account)
|
||||
for day in log_days:
|
||||
widget.mark_day(day)
|
||||
yield True
|
||||
|
@ -197,7 +190,8 @@ class HistoryWindow:
|
|||
'''adds all the lines for given date in textbuffer'''
|
||||
self.history_buffer.set_text('') # clear the buffer first
|
||||
self.last_time_printout = 0
|
||||
lines = gajim.logger.get_conversation_for_date(self.jid, year, month, day)
|
||||
|
||||
lines = gajim.logger.get_conversation_for_date(self.jid, year, month, day, self.account)
|
||||
# lines holds list with tupples that have:
|
||||
# contact_name, time, kind, show, message
|
||||
for line in lines:
|
||||
|
@ -325,7 +319,8 @@ class HistoryWindow:
|
|||
if text == '':
|
||||
return
|
||||
# contact_name, time, kind, show, message, subject
|
||||
results = gajim.logger.get_search_results_for_query(self.jid, text)
|
||||
results = gajim.logger.get_search_results_for_query(
|
||||
self.jid, text, self.account)
|
||||
#FIXME:
|
||||
# add "subject: | message: " in message column if kind is single
|
||||
# also do we need show at all? (we do not search on subject)
|
||||
|
|
|
@ -11,8 +11,6 @@
|
|||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
import gtk
|
||||
import gtk.glade
|
||||
import gtkgui_helpers
|
||||
|
||||
from common import gajim
|
||||
|
@ -22,12 +20,6 @@ TYPE_CHAT = 'chat'
|
|||
TYPE_GC = 'gc'
|
||||
TYPE_PM = 'pm'
|
||||
|
||||
####################
|
||||
# FIXME: Can't this stuff happen once?
|
||||
from common import i18n
|
||||
_ = i18n._
|
||||
APP = i18n.APP
|
||||
|
||||
####################
|
||||
|
||||
class MessageControl:
|
||||
|
@ -47,7 +39,6 @@ class MessageControl:
|
|||
self.account = account
|
||||
self.hide_chat_buttons_always = False
|
||||
self.hide_chat_buttons_current = False
|
||||
self.nb_unread = 0
|
||||
self.resource = resource
|
||||
|
||||
gajim.last_message_time[self.account][self.get_full_jid()] = 0
|
||||
|
@ -125,16 +116,15 @@ class MessageControl:
|
|||
pass
|
||||
|
||||
def get_specific_unread(self):
|
||||
n = 0
|
||||
if gajim.awaiting_events[self.account].has_key(self.contact.jid):
|
||||
n = len(gajim.awaiting_events[self.account][self.contact.jid])
|
||||
return n
|
||||
return len(gajim.events.get_events(self.account, self.contact.jid))
|
||||
|
||||
def send_message(self, message, keyID = '', type = 'chat',
|
||||
chatstate = None, msg_id = None, composing_jep = None, resource = None):
|
||||
chatstate = None, msg_id = None, composing_jep = None, resource = None,
|
||||
user_nick = None):
|
||||
'''Send the given message to the active tab'''
|
||||
jid = self.contact.jid
|
||||
# Send and update history
|
||||
gajim.connections[self.account].send_message(jid, message, keyID,
|
||||
type = type, chatstate = chatstate, msg_id = msg_id,
|
||||
composing_jep = composing_jep, resource = self.resource)
|
||||
type = type, chatstate = chatstate, msg_id = msg_id,
|
||||
composing_jep = composing_jep, resource = self.resource,
|
||||
user_nick = user_nick)
|
||||
|
|
|
@ -44,12 +44,14 @@ class MessageTextView(gtk.TextView):
|
|||
self.set_accepts_tab(True)
|
||||
self.set_editable(True)
|
||||
self.set_cursor_visible(True)
|
||||
self.set_wrap_mode(gtk.WRAP_WORD)
|
||||
self.set_wrap_mode(gtk.WRAP_WORD_CHAR)
|
||||
self.set_left_margin(2)
|
||||
self.set_right_margin(2)
|
||||
self.set_pixels_above_lines(2)
|
||||
self.set_pixels_below_lines(2)
|
||||
|
||||
self.lang = None # Lang used for spell checking
|
||||
|
||||
def destroy(self):
|
||||
import gc
|
||||
gobject.idle_add(lambda:gc.collect())
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
##
|
||||
|
||||
import gtk
|
||||
import gtk.glade
|
||||
import gobject
|
||||
|
||||
import common
|
||||
import gtkgui_helpers
|
||||
|
@ -31,12 +31,6 @@ from chat_control import ChatControlBase
|
|||
|
||||
from common import gajim
|
||||
|
||||
####################
|
||||
# FIXME: Can't this stuff happen once?
|
||||
from common import i18n
|
||||
_ = i18n._
|
||||
APP = i18n.APP
|
||||
|
||||
####################
|
||||
|
||||
class MessageWindow:
|
||||
|
@ -156,8 +150,17 @@ class MessageWindow:
|
|||
fjid = control.get_full_jid()
|
||||
self._controls[control.account][fjid] = control
|
||||
|
||||
if self.get_num_controls() > 1:
|
||||
if self.get_num_controls() == 2:
|
||||
# is first conversation_textview scrolled down ?
|
||||
scrolled = False
|
||||
first_widget = self.notebook.get_nth_page(0)
|
||||
ctrl = self._widget_to_control(first_widget)
|
||||
conv_textview = ctrl.conv_textview
|
||||
if conv_textview.at_the_end():
|
||||
scrolled = True
|
||||
self.notebook.set_show_tabs(True)
|
||||
if scrolled:
|
||||
gobject.idle_add(conv_textview.scroll_to_end_iter)
|
||||
self.alignment.set_property('top-padding', 2)
|
||||
|
||||
# Add notebook page and connect up to the tab's close button
|
||||
|
@ -221,7 +224,7 @@ class MessageWindow:
|
|||
gajim.config.get('notify_on_all_muc_messages') and not \
|
||||
ctrl.attention_flag:
|
||||
continue
|
||||
unread += ctrl.nb_unread
|
||||
unread += ctrl.get_nb_unread()
|
||||
|
||||
unread_str = ''
|
||||
if unread > 1:
|
||||
|
@ -277,9 +280,8 @@ class MessageWindow:
|
|||
ctrl.shutdown()
|
||||
|
||||
# Update external state
|
||||
if gajim.interface.systray_enabled:
|
||||
gajim.interface.systray.remove_jid(ctrl.get_full_jid(), ctrl.account,
|
||||
ctrl.type_id)
|
||||
gajim.events.remove_events(ctrl.account, ctrl.get_full_jid,
|
||||
types = ['printed_msg', 'chat', 'gc_msg'])
|
||||
del gajim.last_message_time[ctrl.account][ctrl.get_full_jid()]
|
||||
|
||||
self.disconnect_tab_dnd(ctrl.widget)
|
||||
|
@ -416,6 +418,8 @@ class MessageWindow:
|
|||
ind = self.notebook.get_current_page()
|
||||
current = ind
|
||||
found = False
|
||||
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:
|
||||
if forward == True: # look for the first unread tab on the right
|
||||
|
@ -426,15 +430,22 @@ class MessageWindow:
|
|||
ind = ind - 1
|
||||
if ind < 0:
|
||||
ind = self.notebook.get_n_pages() - 1
|
||||
if ind == current:
|
||||
break # a complete cycle without finding an unread tab
|
||||
ctrl = self.get_control(ind, None)
|
||||
if ctrl.nb_unread > 0:
|
||||
if ctrl.get_nb_unread() > 0:
|
||||
found = True
|
||||
break # found
|
||||
elif gajim.config.get('ctrl_tab_go_to_next_composing') : # Search for a composing contact
|
||||
contact = ctrl.contact
|
||||
if first_composing_ind == -1 and contact.chatstate == 'composing':
|
||||
# If no composing contact found yet, check if this one is composing
|
||||
first_composing_ind = ind
|
||||
if ind == current:
|
||||
break # a complete cycle without finding an unread tab
|
||||
if found:
|
||||
self.notebook.set_current_page(ind)
|
||||
else: # not found
|
||||
elif first_composing_ind != -1:
|
||||
self.notebook.set_current_page(first_composing_ind)
|
||||
else: # not found and nobody composing
|
||||
if forward: # CTRL + TAB
|
||||
if current < (self.notebook.get_n_pages() - 1):
|
||||
self.notebook.next_page()
|
||||
|
@ -641,13 +652,13 @@ class MessageWindowMgr:
|
|||
if not gajim.config.get('saveposition'):
|
||||
return
|
||||
|
||||
if self.mode in (self.ONE_MSG_WINDOW_NEVER, self.ONE_MSG_WINDOW_ALWAYS):
|
||||
if self.mode == self.ONE_MSG_WINDOW_ALWAYS:
|
||||
size = (gajim.config.get('msgwin-width'),
|
||||
gajim.config.get('msgwin-height'))
|
||||
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'))
|
||||
elif self.mode == self.ONE_MSG_WINDOW_PERTYPE:
|
||||
elif self.mode in (self.ONE_MSG_WINDOW_NEVER, self.ONE_MSG_WINDOW_PERTYPE):
|
||||
if type == message_control.TYPE_PM:
|
||||
type = message_control.TYPE_CHAT
|
||||
opt_width = type + '-msgwin-width'
|
||||
|
@ -705,6 +716,7 @@ class MessageWindowMgr:
|
|||
win_type = type
|
||||
win_role = type
|
||||
elif self.mode == self.ONE_MSG_WINDOW_NEVER:
|
||||
win_type = type
|
||||
win_role = contact.jid
|
||||
|
||||
win = None
|
||||
|
@ -792,6 +804,10 @@ class MessageWindowMgr:
|
|||
pos_y_key = type + '-msgwin-y-position'
|
||||
size_width_key = type + '-msgwin-width'
|
||||
size_height_key = type + '-msgwin-height'
|
||||
elif self.mode == self.ONE_MSG_WINDOW_NEVER:
|
||||
type = msg_win.type
|
||||
size_width_key = type + '-msgwin-width'
|
||||
size_height_key = type + '-msgwin-height'
|
||||
|
||||
if acct:
|
||||
gajim.config.set_per('accounts', acct, size_width_key, width)
|
||||
|
|
|
@ -1,271 +0,0 @@
|
|||
#!/bin/sh
|
||||
''':'
|
||||
exec python -OOt "$0" ${1+"$@"}
|
||||
' '''
|
||||
## Contributors for this file:
|
||||
## - Yann Le Boulanger <asterix@lagaule.org>
|
||||
## - Nikos Kouremenos <kourem@gmail.com>
|
||||
##
|
||||
## Copyright (C) 2003-2004 Yann Le Boulanger <asterix@lagaule.org>
|
||||
## Vincent Hanquez <tab@snarc.org>
|
||||
## Copyright (C) 2005 Yann Le Boulanger <asterix@lagaule.org>
|
||||
## Vincent Hanquez <tab@snarc.org>
|
||||
## Nikos Kouremenos <nkour@jabber.org>
|
||||
## Dimitur Kirov <dkirov@gmail.com>
|
||||
## Travis Shirk <travis@pobox.com>
|
||||
## Norman Rasmussen <norman@rasmussen.co.za>
|
||||
##
|
||||
## This program is free software; you can redistribute it and/or modify
|
||||
## it under the terms of the GNU General Public License as published
|
||||
## by the Free Software Foundation; version 2 only.
|
||||
##
|
||||
## This program is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
|
||||
import os
|
||||
import sre
|
||||
import sys
|
||||
import time
|
||||
import signal
|
||||
from common import logger
|
||||
from common import i18n
|
||||
|
||||
_ = i18n._
|
||||
try:
|
||||
PREFERRED_ENCODING = sys.getpreferredencoding()
|
||||
except:
|
||||
PREFERRED_ENCODING = 'utf-8'
|
||||
from common.helpers import from_one_line, decode_string
|
||||
|
||||
signal.signal(signal.SIGINT, signal.SIG_DFL) # ^C exits the application
|
||||
|
||||
from pysqlite2 import dbapi2 as sqlite
|
||||
|
||||
if os.name == 'nt':
|
||||
try:
|
||||
PATH_TO_LOGS_BASE_DIR = os.path.join(os.environ['appdata'], 'Gajim', 'Logs')
|
||||
PATH_TO_DB = os.path.join(os.environ['appdata'], 'Gajim', 'logs.db') # database is called logs.db
|
||||
except KeyError:
|
||||
# win9x
|
||||
PATH_TO_LOGS_BASE_DIR = '../src/Logs'
|
||||
PATH_TO_DB = '../src/logs.db'
|
||||
else:
|
||||
PATH_TO_LOGS_BASE_DIR = os.path.expanduser('~/.gajim/logs')
|
||||
PATH_TO_DB = os.path.expanduser('~/.gajim/logs.db') # database is called logs.db
|
||||
|
||||
|
||||
|
||||
class Migration:
|
||||
def __init__(self):
|
||||
self.constants = logger.Constants()
|
||||
self.DONE = False
|
||||
self.PROCESSING = False
|
||||
|
||||
if os.path.exists(PATH_TO_DB):
|
||||
print '%s already exists. Exiting..' % PATH_TO_DB
|
||||
sys.exit()
|
||||
|
||||
self.jids_already_in = [] # jid we already put in DB
|
||||
|
||||
def get_jid(self, dirname, filename):
|
||||
# jids.jid text column will be JID if TC-related, room_jid if GC-related,
|
||||
# ROOM_JID/nick if pm-related. Here I get names from filenames
|
||||
if dirname.endswith('logs') or dirname.endswith('Logs'):
|
||||
# we have file (not dir) in logs base dir, so it's TC
|
||||
jid = filename # file is JID
|
||||
else:
|
||||
# we are in a room folder (so it can be either pm or message in room)
|
||||
if filename == os.path.basename(dirname): # room/room
|
||||
jid = dirname # filename is ROOM_JID
|
||||
else: #room/nick it's pm
|
||||
jid = dirname + '/' + filename
|
||||
|
||||
if jid.startswith('/'):
|
||||
p = len(PATH_TO_LOGS_BASE_DIR)
|
||||
jid = jid[p+1:]
|
||||
jid = jid.lower()
|
||||
return jid
|
||||
|
||||
def decode_jid(self, string):
|
||||
'''try to decode (to make it Unicode instance) given jid'''
|
||||
string = decode_string(string)
|
||||
if isinstance(string, str):
|
||||
return None # decode failed
|
||||
return string
|
||||
|
||||
def visit(self, arg, dirname, filenames):
|
||||
s = _('Visiting %s') % dirname
|
||||
if self.queue:
|
||||
self.queue.put(s)
|
||||
else:
|
||||
print s
|
||||
for filename in filenames:
|
||||
# Don't take this file into account, this is dup info
|
||||
# notifications are also in contact log file
|
||||
if filename in ('notify.log', 'README'):
|
||||
continue
|
||||
path_to_text_file = os.path.join(dirname, filename)
|
||||
if os.path.isdir(path_to_text_file):
|
||||
continue
|
||||
|
||||
jid = self.get_jid(dirname, filename)
|
||||
|
||||
jid = self.decode_jid(jid)
|
||||
if not jid:
|
||||
continue
|
||||
|
||||
if filename == os.path.basename(dirname): # gajim@conf/gajim@conf then gajim@conf is type room
|
||||
jid_type = self.constants.JID_ROOM_TYPE
|
||||
#Type of log
|
||||
typ = 'room'
|
||||
else:
|
||||
jid_type = self.constants.JID_NORMAL_TYPE
|
||||
#Type of log
|
||||
typ = _('normal')
|
||||
s = _('Processing %s of type %s') % (jid, typ)
|
||||
if self.queue:
|
||||
self.queue.put(s.encode(PREFERRED_ENCODING))
|
||||
else:
|
||||
print s.encode(PREFERRED_ENCODING)
|
||||
|
||||
JID_ID = None
|
||||
f = open(path_to_text_file, 'r')
|
||||
lines = f.readlines()
|
||||
for line in lines:
|
||||
line = from_one_line(line)
|
||||
splitted_line = line.split(':')
|
||||
if len(splitted_line) > 2:
|
||||
# type in logs is one of
|
||||
# 'gc', 'gcstatus', 'recv', 'sent' and if nothing of those
|
||||
# it is status
|
||||
# new db has:
|
||||
# status, gcstatus, gc_msg, (we only recv those 3),
|
||||
# single_msg_recv, chat_msg_recv, chat_msg_sent, single_msg_sent
|
||||
# to meet all our needs
|
||||
# here I convert
|
||||
# gc ==> gc_msg, gcstatus ==> gcstatus, recv ==> chat_msg_recv
|
||||
# sent ==> chat_msg_sent, status ==> status
|
||||
typ = splitted_line[1] # line[1] has type of logged message
|
||||
message_data = splitted_line[2:] # line[2:] has message data
|
||||
# line[0] is date,
|
||||
# some lines can be fucked up, just drop them
|
||||
try:
|
||||
tim = int(float(splitted_line[0]))
|
||||
except:
|
||||
continue
|
||||
|
||||
contact_name = None
|
||||
show = None
|
||||
if typ == 'gc':
|
||||
contact_name = message_data[0]
|
||||
message = ':'.join(message_data[1:])
|
||||
kind = self.constants.KIND_GC_MSG
|
||||
elif typ == 'gcstatus':
|
||||
contact_name = message_data[0]
|
||||
show = message_data[1]
|
||||
message = ':'.join(message_data[2:]) # status msg
|
||||
kind = self.constants.KIND_GCSTATUS
|
||||
elif typ == 'recv':
|
||||
message = ':'.join(message_data[0:])
|
||||
kind = self.constants.KIND_CHAT_MSG_RECV
|
||||
elif typ == 'sent':
|
||||
message = ':'.join(message_data[0:])
|
||||
kind = self.constants.KIND_CHAT_MSG_SENT
|
||||
else: # status
|
||||
kind = self.constants.KIND_STATUS
|
||||
show = message_data[0]
|
||||
message = ':'.join(message_data[1:]) # status msg
|
||||
|
||||
message = message[:-1] # remove last \n
|
||||
if not message:
|
||||
continue
|
||||
|
||||
# jid is already in the DB, don't create a new row, just get his jid_id
|
||||
if not JID_ID:
|
||||
if jid in self.jids_already_in:
|
||||
self.cur.execute('SELECT jid_id FROM jids WHERE jid = "%s"' % jid)
|
||||
JID_ID = self.cur.fetchone()[0]
|
||||
else:
|
||||
self.jids_already_in.append(jid)
|
||||
self.cur.execute('INSERT INTO jids (jid, type) VALUES (?, ?)',
|
||||
(jid, jid_type))
|
||||
self.con.commit()
|
||||
JID_ID = self.cur.lastrowid
|
||||
|
||||
sql = 'INSERT INTO logs (jid_id, contact_name, time, kind, show, message) '\
|
||||
'VALUES (?, ?, ?, ?, ?, ?)'
|
||||
|
||||
values = (JID_ID, contact_name, tim, kind, show, message)
|
||||
self.cur.execute(sql, values)
|
||||
self.con.commit()
|
||||
|
||||
def migrate(self, queue = None):
|
||||
self.queue = queue
|
||||
self.con = sqlite.connect(PATH_TO_DB)
|
||||
os.chmod(PATH_TO_DB, 0600) # rw only for us
|
||||
self.cur = self.con.cursor()
|
||||
# create the tables
|
||||
# kind can be
|
||||
# status, gcstatus, gc_msg, (we only recv for those 3),
|
||||
# single_msg_recv, chat_msg_recv, chat_msg_sent, single_msg_sent
|
||||
# to meet all our needs
|
||||
# logs.jid_id --> jids.jid_id but Sqlite doesn't do FK etc so it's done in python code
|
||||
self.cur.executescript(
|
||||
'''
|
||||
CREATE TABLE jids(
|
||||
jid_id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
|
||||
jid TEXT UNIQUE,
|
||||
type INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE unread_messages(
|
||||
message_id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
|
||||
jid_id INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE logs(
|
||||
log_line_id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
|
||||
jid_id INTEGER,
|
||||
contact_name TEXT,
|
||||
time INTEGER,
|
||||
kind INTEGER,
|
||||
show INTEGER,
|
||||
message TEXT,
|
||||
subject TEXT
|
||||
);
|
||||
'''
|
||||
)
|
||||
|
||||
self.con.commit()
|
||||
|
||||
self.PROCESSING = True
|
||||
os.path.walk(PATH_TO_LOGS_BASE_DIR, self.visit, None)
|
||||
s = '''
|
||||
|
||||
We do not use plain-text files anymore, because they do not meet our needs.
|
||||
Those files here are logs for Gajim up until 0.8.2
|
||||
We now use an sqlite database called logs.db found in %s
|
||||
You can now safely remove your %s folder
|
||||
Thank you''' % (os.path.dirname(PATH_TO_LOGS_BASE_DIR), PATH_TO_LOGS_BASE_DIR)
|
||||
f = open(os.path.join(PATH_TO_LOGS_BASE_DIR, 'README'), 'w')
|
||||
f.write(s)
|
||||
f.close()
|
||||
if queue:
|
||||
queue.put(s)
|
||||
self.DONE = True
|
||||
|
||||
if __name__ == '__main__':
|
||||
# magic argumen 'dont_wait' tells us that script is run from Gajim
|
||||
if len(sys.argv) < 2 or sys.argv[1] != 'dont_wait':
|
||||
print 'IMPORTNANT: PLEASE READ http://trac.gajim.org/wiki/MigrateLogToDot9DB'
|
||||
print 'Migration will start in 40 seconds unless you press Ctrl+C'
|
||||
time.sleep(40) # give the user time to act
|
||||
print
|
||||
print 'Starting Logs Migration'
|
||||
print '======================='
|
||||
print 'Please do NOT run Gajim until this script is over'
|
||||
m = Migration()
|
||||
m.migrate()
|
155
src/notify.py
155
src/notify.py
|
@ -23,10 +23,7 @@ import dialogs
|
|||
import gtkgui_helpers
|
||||
|
||||
from common import gajim
|
||||
from common import i18n
|
||||
from common import helpers
|
||||
i18n.init()
|
||||
_ = i18n._
|
||||
|
||||
import dbus_support
|
||||
if dbus_support.supported:
|
||||
|
@ -35,29 +32,112 @@ if dbus_support.supported:
|
|||
import dbus.glib
|
||||
import dbus.service
|
||||
|
||||
def notify(event, jid, account, parameters):
|
||||
def get_show_in_roster(event, account, contact):
|
||||
'''Return True if this event must be shown in roster, else False'''
|
||||
num = get_advanced_notification(event, account, contact)
|
||||
if num != None:
|
||||
if gajim.config.get_per('notifications', str(num), 'roster') == 'yes':
|
||||
return True
|
||||
if gajim.config.get_per('notifications', str(num), 'roster') == 'no':
|
||||
return False
|
||||
if event == 'message_received':
|
||||
chat_control = helpers.get_chat_control(account, contact)
|
||||
if not chat_control:
|
||||
return True
|
||||
elif event == 'ft_request':
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_show_in_systray(event, account, contact):
|
||||
'''Return True if this event must be shown in roster, else False'''
|
||||
num = get_advanced_notification(event, account, contact)
|
||||
if num != None:
|
||||
if gajim.config.get_per('notifications', str(num), 'systray') == 'yes':
|
||||
return True
|
||||
if gajim.config.get_per('notifications', str(num), 'systray') == 'no':
|
||||
return False
|
||||
if event in ('message_received', 'ft_request', 'gc_msg_highlight',
|
||||
'ft_request'):
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_advanced_notification(event, account, contact):
|
||||
'''Returns the number of the first advanced notification or None'''
|
||||
num = 0
|
||||
notif = gajim.config.get_per('notifications', str(num))
|
||||
while notif:
|
||||
recipient_ok = False
|
||||
status_ok = False
|
||||
tab_opened_ok = False
|
||||
# test event
|
||||
if gajim.config.get_per('notifications', str(num), 'event') == event:
|
||||
# test recipient
|
||||
recipient_type = gajim.config.get_per('notifications', str(num),
|
||||
'recipient_type')
|
||||
recipients = gajim.config.get_per('notifications', str(num),
|
||||
'recipients').split()
|
||||
if recipient_type == 'all':
|
||||
recipient_ok = True
|
||||
elif recipient_type == 'contact' and contact.jid in recipients:
|
||||
recipient_ok = True
|
||||
elif recipient_type == 'group':
|
||||
for group in contact.groups:
|
||||
if group in contact.groups:
|
||||
recipient_ok = True
|
||||
break
|
||||
if recipient_ok:
|
||||
# test status
|
||||
our_status = gajim.SHOW_LIST[gajim.connections[account].connected]
|
||||
status = gajim.config.get_per('notifications', str(num), 'status')
|
||||
if status == 'all' or our_status in status.split():
|
||||
status_ok = True
|
||||
if status_ok:
|
||||
# test window_opened
|
||||
tab_opened = gajim.config.get_per('notifications', str(num),
|
||||
'tab_opened')
|
||||
if tab_opened == 'both':
|
||||
tab_opened_ok = True
|
||||
else:
|
||||
chat_control = helper.get_chat_control(account, contact)
|
||||
if (chat_control and tab_opened == 'yes') or (not chat_control and \
|
||||
tab_opened == 'no'):
|
||||
tab_opened_ok = True
|
||||
if tab_opened_ok:
|
||||
return num
|
||||
|
||||
num += 1
|
||||
notif = gajim.config.get_per('notifications', str(num))
|
||||
|
||||
def notify(event, jid, account, parameters, advanced_notif_num = None):
|
||||
'''Check what type of notifications we want, depending on basic configuration
|
||||
of notifications and advanced one and do these notifications'''
|
||||
# First, find what notifications we want
|
||||
do_popup = False
|
||||
do_sound = False
|
||||
do_cmd = False
|
||||
if (event == 'status_change'):
|
||||
new_show = parameters[0]
|
||||
status_message = parameters[1]
|
||||
# Default : No popup for status change
|
||||
elif (event == 'contact_connected'):
|
||||
status_message = parameters
|
||||
if gajim.config.get('notify_on_signin') and \
|
||||
not gajim.block_signed_in_notifications[account]\
|
||||
and helpers.allow_showing_notification(account):
|
||||
j = gajim.get_jid_without_resource(jid)
|
||||
server = gajim.get_server_from_jid(j)
|
||||
account_server = account + '/' + server
|
||||
block_transport = False
|
||||
if account_server in gajim.block_signed_in_notifications and \
|
||||
gajim.block_signed_in_notifications[account_server]:
|
||||
block_transport = True
|
||||
if helpers.allow_showing_notification(account, 'notify_on_signin') and \
|
||||
not gajim.block_signed_in_notifications[account] and not block_transport:
|
||||
do_popup = True
|
||||
if gajim.config.get_per('soundevents', 'contact_connected',
|
||||
'enabled') and not gajim.block_signed_in_notifications[account]:
|
||||
'enabled') and not gajim.block_signed_in_notifications[account] and \
|
||||
not block_transport:
|
||||
do_sound = True
|
||||
elif (event == 'contact_disconnected'):
|
||||
status_message = parameters
|
||||
if gajim.config.get('notify_on_signout') \
|
||||
and helpers.allow_showing_notification(account):
|
||||
if helpers.allow_showing_notification(account, 'notify_on_signout'):
|
||||
do_popup = True
|
||||
if gajim.config.get_per('soundevents', 'contact_disconnected',
|
||||
'enabled'):
|
||||
|
@ -67,17 +147,21 @@ def notify(event, jid, account, parameters):
|
|||
first = parameters[1]
|
||||
nickname = parameters[2]
|
||||
message = parameters[3]
|
||||
if gajim.config.get('notify_on_new_message') and \
|
||||
helpers.allow_showing_notification(account) and first:
|
||||
if helpers.allow_showing_notification(account, 'notify_on_new_message',
|
||||
advanced_notif_num, first):
|
||||
do_popup = True
|
||||
if first and gajim.config.get_per('soundevents', 'first_message_received',
|
||||
'enabled'):
|
||||
if first and helpers.allow_sound_notification('first_message_received',
|
||||
advanced_notif_num):
|
||||
do_sound = True
|
||||
elif not first and gajim.config.get_per('soundevents', 'next_message_received',
|
||||
'enabled'):
|
||||
elif not first and helpers.allow_sound_notification(
|
||||
'next_message_received', advanced_notif_num):
|
||||
do_sound = True
|
||||
else:
|
||||
print '*Event not implemeted yet*'
|
||||
|
||||
if advanced_notif_num != None and gajim.config.get_per('notifications',
|
||||
str(advanced_notif_num), 'run_command'):
|
||||
do_cmd = True
|
||||
|
||||
# Do the wanted notifications
|
||||
if (do_popup):
|
||||
|
@ -138,8 +222,7 @@ def notify(event, jid, account, parameters):
|
|||
text = message
|
||||
elif message_type == 'pm': # private message
|
||||
event_type = _('New Private Message')
|
||||
room_name, t = gajim.get_room_name_and_server_from_room_jid(
|
||||
jid)
|
||||
room_name, t = gajim.get_room_name_and_server_from_room_jid(jid)
|
||||
img = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events',
|
||||
'priv_msg_recv.png')
|
||||
title = _('New Private Message from room %s') % room_name
|
||||
|
@ -151,20 +234,40 @@ def notify(event, jid, account, parameters):
|
|||
'chat_msg_recv.png')
|
||||
title = _('New Message from %(nickname)s') % \
|
||||
{'nickname': nickname}
|
||||
text = message
|
||||
text = message
|
||||
path = gtkgui_helpers.get_path_to_generic_or_avatar(img)
|
||||
popup(event_type, jid, account, message_type,
|
||||
path_to_image = path, title = title, text = text)
|
||||
|
||||
if (do_sound):
|
||||
snd_file = None
|
||||
snd_event = None # If not snd_file, play the event
|
||||
if (event == 'new_message'):
|
||||
if first:
|
||||
helpers.play_sound('first_message_received')
|
||||
if advanced_notif_num != None and gajim.config.get_per('notifications',
|
||||
str(advanced_notif_num), 'sound') == 'yes':
|
||||
snd_file = gajim.config.get_per('notifications',
|
||||
str(advanced_notif_num), 'sound_file')
|
||||
elif advanced_notif_num != None and gajim.config.get_per(
|
||||
'notifications', str(advanced_notif_num), 'sound') == 'no':
|
||||
pass # do not set snd_event
|
||||
elif first:
|
||||
snd_event = 'first_message_received'
|
||||
else:
|
||||
helpers.play_sound('next_message_received')
|
||||
elif (event == 'contact_connected' or event == 'contact_disconnected'):
|
||||
helpers.play_sound(event)
|
||||
|
||||
snd_event = 'next_message_received'
|
||||
elif event in ('contact_connected', 'contact_disconnected'):
|
||||
snd_event = event
|
||||
if snd_file:
|
||||
helpers.play_sound_file(snd_file)
|
||||
if snd_event:
|
||||
helpers.play_sound(snd_event)
|
||||
|
||||
if do_cmd:
|
||||
command = gajim.config.get_per('notifications', str(advanced_notif_num),
|
||||
'command')
|
||||
try:
|
||||
helpers.exec_command(command)
|
||||
except:
|
||||
pass
|
||||
|
||||
def popup(event_type, jid, account, msg_type = '', path_to_image = None,
|
||||
title = None, text = None):
|
||||
|
@ -279,6 +382,8 @@ class DesktopNotification:
|
|||
ntype = 'im.invitation'
|
||||
elif event_type == _('Contact Changed Status'):
|
||||
ntype = 'presence.status'
|
||||
elif event_type == _('Connection Failed'):
|
||||
ntype = 'connection.failed'
|
||||
else:
|
||||
# default failsafe values
|
||||
self.path_to_image = os.path.abspath(
|
||||
|
|
|
@ -0,0 +1,283 @@
|
|||
## profile_window.py
|
||||
##
|
||||
## Copyright (C) 2003-2006 Yann Le Boulanger <asterix@lagaule.org>
|
||||
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem@gmail.com>
|
||||
##
|
||||
## This program is free software; you can redistribute it and/or modify
|
||||
## it under the terms of the GNU General Public License as published
|
||||
## by the Free Software Foundation; version 2 only.
|
||||
##
|
||||
## This program is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
|
||||
import gtk
|
||||
import gobject
|
||||
import base64
|
||||
import mimetypes
|
||||
import os
|
||||
import time
|
||||
import locale
|
||||
|
||||
import gtkgui_helpers
|
||||
import dialogs
|
||||
|
||||
from common import helpers
|
||||
from common import gajim
|
||||
from common.i18n import Q_
|
||||
|
||||
def get_avatar_pixbuf_encoded_mime(photo):
|
||||
'''return the pixbuf of the image
|
||||
photo is a dictionary containing PHOTO information'''
|
||||
if not isinstance(photo, dict):
|
||||
return None, None, None
|
||||
img_decoded = None
|
||||
avatar_encoded = None
|
||||
avatar_mime_type = None
|
||||
if photo.has_key('BINVAL'):
|
||||
img_encoded = photo['BINVAL']
|
||||
avatar_encoded = img_encoded
|
||||
try:
|
||||
img_decoded = base64.decodestring(img_encoded)
|
||||
except:
|
||||
pass
|
||||
if img_decoded:
|
||||
if photo.has_key('TYPE'):
|
||||
avatar_mime_type = photo['TYPE']
|
||||
pixbuf = gtkgui_helpers.get_pixbuf_from_data(img_decoded)
|
||||
else:
|
||||
pixbuf, avatar_mime_type = gtkgui_helpers.get_pixbuf_from_data(
|
||||
img_decoded, want_type=True)
|
||||
else:
|
||||
pixbuf = None
|
||||
return pixbuf, avatar_encoded, avatar_mime_type
|
||||
|
||||
class ProfileWindow:
|
||||
'''Class for our information window'''
|
||||
|
||||
def __init__(self, account):
|
||||
self.xml = gtkgui_helpers.get_glade('profile_window.glade')
|
||||
self.window = self.xml.get_widget('profile_window')
|
||||
|
||||
self.account = account
|
||||
self.jid = gajim.get_jid_from_account(account)
|
||||
|
||||
self.avatar_mime_type = None
|
||||
self.avatar_encoded = None
|
||||
|
||||
self.xml.signal_autoconnect(self)
|
||||
self.window.show_all()
|
||||
|
||||
def on_profile_window_destroy(self, widget):
|
||||
del gajim.interface.instances[self.account]['profile']
|
||||
|
||||
def on_profile_window_key_press_event(self, widget, event):
|
||||
if event.keyval == gtk.keysyms.Escape:
|
||||
self.window.destroy()
|
||||
|
||||
def on_clear_button_clicked(self, widget):
|
||||
# empty the image
|
||||
self.xml.get_widget('PHOTO_image').set_from_icon_name('stock_person',
|
||||
gtk.ICON_SIZE_DIALOG)
|
||||
self.avatar_encoded = None
|
||||
self.avatar_mime_type = None
|
||||
|
||||
def on_set_avatar_button_clicked(self, widget):
|
||||
f = None
|
||||
def on_ok(widget, path_to_file):
|
||||
filesize = os.path.getsize(path_to_file) # in bytes
|
||||
#FIXME: use messages for invalid file for 0.11
|
||||
invalid_file = False
|
||||
msg = ''
|
||||
if os.path.isfile(path_to_file):
|
||||
stat = os.stat(path_to_file)
|
||||
if stat[6] == 0:
|
||||
invalid_file = True
|
||||
else:
|
||||
invalid_file = True
|
||||
if not invalid_file and filesize > 16384: # 16 kb
|
||||
try:
|
||||
pixbuf = gtk.gdk.pixbuf_new_from_file(path_to_file)
|
||||
# get the image at 'notification size'
|
||||
# and use that user did not specify in ACE crazy size
|
||||
scaled_pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf,
|
||||
'tooltip')
|
||||
except gobject.GError, msg: # unknown format
|
||||
# msg should be string, not object instance
|
||||
msg = str(msg)
|
||||
invalid_file = True
|
||||
if invalid_file:
|
||||
if True: # keep identation
|
||||
dialogs.ErrorDialog(_('Could not load image'), msg)
|
||||
return
|
||||
if filesize > 16384:
|
||||
if scaled_pixbuf:
|
||||
path_to_file = os.path.join(gajim.TMP,
|
||||
'avatar_scaled.png')
|
||||
scaled_pixbuf.save(path_to_file, 'png')
|
||||
self.dialog.destroy()
|
||||
|
||||
fd = open(path_to_file, 'rb')
|
||||
data = fd.read()
|
||||
pixbuf = gtkgui_helpers.get_pixbuf_from_data(data)
|
||||
# rescale it
|
||||
pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf, 'vcard')
|
||||
image = self.xml.get_widget('PHOTO_image')
|
||||
image.set_from_pixbuf(pixbuf)
|
||||
self.avatar_encoded = base64.encodestring(data)
|
||||
# returns None if unknown type
|
||||
self.avatar_mime_type = mimetypes.guess_type(path_to_file)[0]
|
||||
|
||||
self.dialog = dialogs.ImageChooserDialog(on_response_ok = on_ok)
|
||||
|
||||
def on_PHOTO_button_press_event(self, widget, event):
|
||||
'''If right-clicked, show popup'''
|
||||
if event.button == 3 and self.avatar_encoded: # right click
|
||||
menu = gtk.Menu()
|
||||
nick = gajim.config.get_per('accounts', self.account, 'name')
|
||||
menuitem = gtk.ImageMenuItem(gtk.STOCK_SAVE_AS)
|
||||
menuitem.connect('activate',
|
||||
gtkgui_helpers.on_avatar_save_as_menuitem_activate,
|
||||
self.jid, None, nick + '.jpeg')
|
||||
menu.append(menuitem)
|
||||
# show clear
|
||||
menuitem = gtk.ImageMenuItem(gtk.STOCK_CLEAR)
|
||||
menuitem.connect('activate', self.on_clear_button_clicked)
|
||||
menu.append(menuitem)
|
||||
menu.connect('selection-done', lambda w:w.destroy())
|
||||
# show the menu
|
||||
menu.show_all()
|
||||
menu.popup(None, None, None, event.button, event.time)
|
||||
elif event.button == 1: # left click
|
||||
self.on_set_avatar_button_clicked(widget)
|
||||
|
||||
def set_value(self, entry_name, value):
|
||||
try:
|
||||
self.xml.get_widget(entry_name).set_text(value)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def set_values(self, vcard):
|
||||
if not 'PHOTO' in vcard:
|
||||
# set default image
|
||||
image = self.xml.get_widget('PHOTO_image')
|
||||
image.set_from_icon_name('stock_person', gtk.ICON_SIZE_DIALOG)
|
||||
for i in vcard.keys():
|
||||
if i == 'PHOTO':
|
||||
pixbuf, self.avatar_encoded, self.avatar_mime_type = \
|
||||
get_avatar_pixbuf_encoded_mime(vcard[i])
|
||||
image = self.xml.get_widget('PHOTO_image')
|
||||
if not pixbuf:
|
||||
image.set_from_icon_name('stock_person', gtk.ICON_SIZE_DIALOG)
|
||||
continue
|
||||
pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf, 'vcard')
|
||||
image.set_from_pixbuf(pixbuf)
|
||||
continue
|
||||
if i == 'ADR' or i == 'TEL' or i == 'EMAIL':
|
||||
for entry in vcard[i]:
|
||||
add_on = '_HOME'
|
||||
if 'WORK' in entry:
|
||||
add_on = '_WORK'
|
||||
for j in entry.keys():
|
||||
self.set_value(i + add_on + '_' + j + '_entry', entry[j])
|
||||
if isinstance(vcard[i], dict):
|
||||
for j in vcard[i].keys():
|
||||
self.set_value(i + '_' + j + '_entry', vcard[i][j])
|
||||
else:
|
||||
if i == 'DESC':
|
||||
self.xml.get_widget('DESC_textview').get_buffer().set_text(
|
||||
vcard[i], 0)
|
||||
else:
|
||||
self.set_value(i + '_entry', vcard[i])
|
||||
|
||||
def add_to_vcard(self, vcard, entry, txt):
|
||||
'''Add an information to the vCard dictionary'''
|
||||
entries = entry.split('_')
|
||||
loc = vcard
|
||||
if len(entries) == 3: # We need to use lists
|
||||
if not loc.has_key(entries[0]):
|
||||
loc[entries[0]] = []
|
||||
found = False
|
||||
for e in loc[entries[0]]:
|
||||
if entries[1] in e:
|
||||
found = True
|
||||
break
|
||||
if found:
|
||||
e[entries[2]] = txt
|
||||
else:
|
||||
loc[entries[0]].append({entries[1]: '', entries[2]: txt})
|
||||
return vcard
|
||||
while len(entries) > 1:
|
||||
if not loc.has_key(entries[0]):
|
||||
loc[entries[0]] = {}
|
||||
loc = loc[entries[0]]
|
||||
del entries[0]
|
||||
loc[entries[0]] = txt
|
||||
return vcard
|
||||
|
||||
def make_vcard(self):
|
||||
'''make the vCard dictionary'''
|
||||
entries = ['FN', 'NICKNAME', 'BDAY', 'EMAIL_HOME_USERID', 'URL',
|
||||
'TEL_HOME_NUMBER', 'N_FAMILY', 'N_GIVEN', 'N_MIDDLE', 'N_PREFIX',
|
||||
'N_SUFFIX', 'ADR_HOME_STREET', 'ADR_HOME_EXTADR', 'ADR_HOME_LOCALITY',
|
||||
'ADR_HOME_REGION', 'ADR_HOME_PCODE', 'ADR_HOME_CTRY', 'ORG_ORGNAME',
|
||||
'ORG_ORGUNIT', 'TITLE', 'ROLE', 'TEL_WORK_NUMBER', 'EMAIL_WORK_USERID',
|
||||
'ADR_WORK_STREET', 'ADR_WORK_EXTADR', 'ADR_WORK_LOCALITY',
|
||||
'ADR_WORK_REGION', 'ADR_WORK_PCODE', 'ADR_WORK_CTRY']
|
||||
vcard = {}
|
||||
for e in entries:
|
||||
txt = self.xml.get_widget(e + '_entry').get_text().decode('utf-8')
|
||||
if txt != '':
|
||||
vcard = self.add_to_vcard(vcard, e, txt)
|
||||
|
||||
# DESC textview
|
||||
buff = self.xml.get_widget('DESC_textview').get_buffer()
|
||||
start_iter = buff.get_start_iter()
|
||||
end_iter = buff.get_end_iter()
|
||||
txt = buff.get_text(start_iter, end_iter, 0)
|
||||
if txt != '':
|
||||
vcard['DESC'] = txt.decode('utf-8')
|
||||
|
||||
# Avatar
|
||||
if self.avatar_encoded:
|
||||
vcard['PHOTO'] = {'BINVAL': self.avatar_encoded}
|
||||
if self.avatar_mime_type:
|
||||
vcard['PHOTO']['TYPE'] = self.avatar_mime_type
|
||||
return vcard
|
||||
|
||||
def on_publish_button_clicked(self, widget):
|
||||
if gajim.connections[self.account].connected < 2:
|
||||
dialogs.ErrorDialog(_('You are not connected to the server'),
|
||||
_('Without a connection you can not publish your contact '
|
||||
'information.'))
|
||||
return
|
||||
vcard = self.make_vcard()
|
||||
nick = ''
|
||||
if vcard.has_key('NICKNAME'):
|
||||
nick = vcard['NICKNAME']
|
||||
if nick == '':
|
||||
nick = gajim.config.get_per('accounts', self.account, 'name')
|
||||
gajim.nicks[self.account] = nick
|
||||
gajim.connections[self.account].send_vcard(vcard)
|
||||
|
||||
def on_retrieve_button_clicked(self, widget):
|
||||
entries = ['FN', 'NICKNAME', 'BDAY', 'EMAIL_HOME_USERID', 'URL',
|
||||
'TEL_HOME_NUMBER', 'N_FAMILY', 'N_GIVEN', 'N_MIDDLE', 'N_PREFIX',
|
||||
'N_SUFFIX', 'ADR_HOME_STREET', 'ADR_HOME_EXTADR', 'ADR_HOME_LOCALITY',
|
||||
'ADR_HOME_REGION', 'ADR_HOME_PCODE', 'ADR_HOME_CTRY', 'ORG_ORGNAME',
|
||||
'ORG_ORGUNIT', 'TITLE', 'ROLE', 'ADR_WORK_STREET', 'ADR_WORK_EXTADR',
|
||||
'ADR_WORK_LOCALITY', 'ADR_WORK_REGION', 'ADR_WORK_PCODE',
|
||||
'ADR_WORK_CTRY']
|
||||
if gajim.connections[self.account].connected > 1:
|
||||
# clear all entries
|
||||
for e in entries:
|
||||
self.xml.get_widget(e + '_entry').set_text('')
|
||||
self.xml.get_widget('DESC_textview').get_buffer().set_text('')
|
||||
self.xml.get_widget('PHOTO_image').set_from_icon_name('stock_person',
|
||||
gtk.ICON_SIZE_DIALOG)
|
||||
gajim.connections[self.account].request_vcard(self.jid)
|
||||
else:
|
||||
dialogs.ErrorDialog(_('You are not connected to the server'),
|
||||
_('Without a connection, you can not get your contact information.'))
|
|
@ -31,9 +31,7 @@ import os
|
|||
from common import gajim
|
||||
from common import helpers
|
||||
from time import time
|
||||
from common import i18n
|
||||
from dialogs import AddNewContactWindow, NewChatDialog
|
||||
_ = i18n._
|
||||
|
||||
import dbus_support
|
||||
if dbus_support.supported:
|
||||
|
@ -56,7 +54,7 @@ ident = lambda e: e
|
|||
if dbus_support.version[1] >= 43:
|
||||
# in most cases it is a utf-8 string
|
||||
DBUS_STRING = dbus.String
|
||||
|
||||
|
||||
# general type (for use in dicts,
|
||||
# where all values should have the same type)
|
||||
DBUS_VARIANT = dbus.Variant
|
||||
|
@ -69,7 +67,7 @@ if dbus_support.version[1] >= 43:
|
|||
DBUS_DICT_SS = lambda : dbus.Dictionary({}, signature="ss")
|
||||
# empty type
|
||||
DBUS_NONE = lambda : dbus.Variant(0)
|
||||
|
||||
|
||||
else: # 33, 35, 36
|
||||
DBUS_DICT_SV = lambda : {}
|
||||
DBUS_DICT_SS = lambda : {}
|
||||
|
@ -124,7 +122,7 @@ class Remote:
|
|||
def __init__(self):
|
||||
self.signal_object = None
|
||||
session_bus = dbus_support.session_bus.SessionBus()
|
||||
|
||||
|
||||
if dbus_support.version[1] >= 41:
|
||||
service = dbus.service.BusName(SERVICE, bus=session_bus)
|
||||
self.signal_object = SignalObject(service)
|
||||
|
@ -141,7 +139,7 @@ class Remote:
|
|||
class SignalObject(DbusPrototype):
|
||||
''' Local object definition for /org/gajim/dbus/RemoteObject. This doc must
|
||||
not be visible, because the clients can access only the remote object. '''
|
||||
|
||||
|
||||
def __init__(self, service):
|
||||
self.first_show = True
|
||||
self.vcard_account = None
|
||||
|
@ -161,6 +159,7 @@ class SignalObject(DbusPrototype):
|
|||
self.change_status,
|
||||
self.open_chat,
|
||||
self.send_message,
|
||||
self.send_single_message,
|
||||
self.contact_info,
|
||||
self.send_file,
|
||||
self.prefs_list,
|
||||
|
@ -172,6 +171,7 @@ class SignalObject(DbusPrototype):
|
|||
self.get_status,
|
||||
self.get_status_message,
|
||||
self.start_chat,
|
||||
self.send_xml,
|
||||
])
|
||||
|
||||
def raise_signal(self, signal, arg):
|
||||
|
@ -181,7 +181,7 @@ class SignalObject(DbusPrototype):
|
|||
i = message.get_iter(True)
|
||||
i.append(arg)
|
||||
self._connection.send(message)
|
||||
|
||||
|
||||
def get_status(self, *args):
|
||||
'''get_status(account = None)
|
||||
returns status (show to be exact) which is the global one
|
||||
|
@ -194,7 +194,7 @@ class SignalObject(DbusPrototype):
|
|||
# return show for the given account
|
||||
index = gajim.connections[account].connected
|
||||
return DBUS_STRING(STATUS_LIST[index])
|
||||
|
||||
|
||||
def get_status_message(self, *args):
|
||||
'''get_status(account = None)
|
||||
returns status which is the global one
|
||||
|
@ -207,7 +207,7 @@ class SignalObject(DbusPrototype):
|
|||
# return show for the given account
|
||||
status = gajim.connections[account].status
|
||||
return DBUS_STRING(status)
|
||||
|
||||
|
||||
|
||||
def get_account_and_contact(self, account, jid):
|
||||
''' get the account (if not given) and contact instance from jid'''
|
||||
|
@ -233,7 +233,7 @@ class SignalObject(DbusPrototype):
|
|||
break
|
||||
if not contact:
|
||||
contact = jid
|
||||
|
||||
|
||||
return connected_account, contact
|
||||
|
||||
def send_file(self, *args):
|
||||
|
@ -241,34 +241,50 @@ class SignalObject(DbusPrototype):
|
|||
send file, located at 'file_path' to 'jid', using account
|
||||
(optional) 'account' '''
|
||||
file_path, jid, account = self._get_real_arguments(args, 3)
|
||||
|
||||
jid = self._get_real_jid(jid, account)
|
||||
connected_account, contact = self.get_account_and_contact(account, jid)
|
||||
|
||||
if connected_account:
|
||||
if file_path[:7] == 'file://':
|
||||
file_path=file_path[7:]
|
||||
if os.path.isfile(file_path): # is it file?
|
||||
gajim.interface.instances['file_transfers'].send_file(
|
||||
connected_account, contact, file_path)
|
||||
return True
|
||||
return False
|
||||
|
||||
def send_message(self, *args):
|
||||
''' send_message(jid, message, keyID=None, account=None)
|
||||
send 'message' to 'jid', using account (optional) 'account'.
|
||||
if keyID is specified, encrypt the message with the pgp key '''
|
||||
jid, message, keyID, account = self._get_real_arguments(args, 4)
|
||||
def _send_message(self, jid, message, keyID, account, type = 'chat', subject = None):
|
||||
''' can be called from send_chat_message (default when send_message)
|
||||
or send_single_message'''
|
||||
if not jid or not message:
|
||||
return None # or raise error
|
||||
if not keyID:
|
||||
keyID = ''
|
||||
|
||||
|
||||
connected_account, contact = self.get_account_and_contact(account, jid)
|
||||
|
||||
|
||||
if connected_account:
|
||||
connection = gajim.connections[connected_account]
|
||||
res = connection.send_message(jid, message, keyID)
|
||||
res = connection.send_message(jid, message, keyID, type, subject)
|
||||
return True
|
||||
return False
|
||||
|
||||
def send_chat_message(self, *args):
|
||||
''' send_message(jid, message, keyID=None, account=None)
|
||||
send chat 'message' to 'jid', using account (optional) 'account'.
|
||||
if keyID is specified, encrypt the message with the pgp key '''
|
||||
jid, message, keyID, account = self._get_real_arguments(args, 4)
|
||||
jid = self._get_real_jid(jid, account)
|
||||
return self._send_message(jid, message, keyID, account)
|
||||
|
||||
def send_single_message(self, *args):
|
||||
''' send_single_message(jid, subject, message, keyID=None, account=None)
|
||||
send single 'message' to 'jid', using account (optional) 'account'.
|
||||
if keyID is specified, encrypt the message with the pgp key '''
|
||||
jid, subject, message, keyID, account = self._get_real_arguments(args, 5)
|
||||
jid = self._get_real_jid(jid, account)
|
||||
return self._send_message(jid, message, keyID, account, type, subject)
|
||||
|
||||
def open_chat(self, *args):
|
||||
''' start_chat(jid, account=None) -> shows the tabbed window for new
|
||||
message to 'jid', using account(optional) 'account' '''
|
||||
|
@ -276,9 +292,8 @@ class SignalObject(DbusPrototype):
|
|||
if not jid:
|
||||
# FIXME: raise exception for missing argument (dbus0.35+)
|
||||
return None
|
||||
if jid.startswith('xmpp:'):
|
||||
jid = jid[5:] # len('xmpp:') = 5
|
||||
|
||||
jid = self._get_real_jid(jid, account)
|
||||
|
||||
if account:
|
||||
accounts = [account]
|
||||
else:
|
||||
|
@ -303,11 +318,11 @@ class SignalObject(DbusPrototype):
|
|||
connected_account = acct
|
||||
elif first_connected_acct is None:
|
||||
first_connected_acct = acct
|
||||
|
||||
|
||||
# if jid is not a conntact, open-chat with first connected account
|
||||
if connected_account is None and first_connected_acct:
|
||||
connected_account = first_connected_acct
|
||||
|
||||
|
||||
if connected_account:
|
||||
gajim.interface.roster.new_chat_from_jid(connected_account, jid)
|
||||
# preserve the 'steal focus preservation'
|
||||
|
@ -316,7 +331,7 @@ class SignalObject(DbusPrototype):
|
|||
win.window.focus()
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def change_status(self, *args, **keywords):
|
||||
''' change_status(status, message, account). account is optional -
|
||||
if not specified status is changed for all accounts. '''
|
||||
|
@ -331,16 +346,17 @@ class SignalObject(DbusPrototype):
|
|||
else:
|
||||
# account not specified, so change the status of all accounts
|
||||
for acc in gajim.contacts.get_accounts():
|
||||
if not gajim.config.get_per('accounts', acc, 'sync_with_global_status'):
|
||||
continue
|
||||
gobject.idle_add(gajim.interface.roster.send_status, acc,
|
||||
status, message)
|
||||
return None
|
||||
|
||||
|
||||
def show_next_unread(self, *args):
|
||||
''' Show the window(s) with next waiting messages in tabbed/group chats. '''
|
||||
#FIXME: when systray is disabled this method does nothing.
|
||||
if len(gajim.interface.systray.jids) != 0:
|
||||
if gajim.events.get_nb_events():
|
||||
gajim.interface.systray.handle_first_event()
|
||||
|
||||
|
||||
def contact_info(self, *args):
|
||||
''' get vcard info for a contact. Return cached value of the vcard.
|
||||
'''
|
||||
|
@ -350,14 +366,15 @@ class SignalObject(DbusPrototype):
|
|||
if not jid:
|
||||
# FIXME: raise exception for missing argument (0.3+)
|
||||
return None
|
||||
|
||||
jid = self._get_real_jid(jid, account)
|
||||
|
||||
cached_vcard = gajim.connections.values()[0].get_cached_vcard(jid)
|
||||
if cached_vcard:
|
||||
return get_dbus_struct(cached_vcard)
|
||||
|
||||
|
||||
# return empty dict
|
||||
return DBUS_DICT_SV()
|
||||
|
||||
|
||||
def list_accounts(self, *args):
|
||||
''' list register accounts '''
|
||||
result = gajim.contacts.get_accounts()
|
||||
|
@ -367,7 +384,7 @@ class SignalObject(DbusPrototype):
|
|||
result_array.append(DBUS_STRING(account))
|
||||
return result_array
|
||||
return None
|
||||
|
||||
|
||||
def account_info(self, *args):
|
||||
''' show info on account: resource, jid, nick, prio, message '''
|
||||
[for_account] = self._get_real_arguments(args, 1)
|
||||
|
@ -386,7 +403,7 @@ class SignalObject(DbusPrototype):
|
|||
result['resource'] = DBUS_STRING(unicode(gajim.config.get_per('accounts',
|
||||
account.name, 'resource')))
|
||||
return result
|
||||
|
||||
|
||||
def list_contacts(self, *args):
|
||||
''' list all contacts in the roster. If the first argument is specified,
|
||||
then return the contacts for the specified account '''
|
||||
|
@ -410,7 +427,7 @@ class SignalObject(DbusPrototype):
|
|||
if result == []:
|
||||
return None
|
||||
return result
|
||||
|
||||
|
||||
def toggle_roster_appearance(self, *args):
|
||||
''' shows/hides the roster window '''
|
||||
win = gajim.interface.roster.window
|
||||
|
@ -437,14 +454,14 @@ class SignalObject(DbusPrototype):
|
|||
prefs_dict[DBUS_STRING(key)] = DBUS_STRING(value[1])
|
||||
gajim.config.foreach(get_prefs)
|
||||
return prefs_dict
|
||||
|
||||
|
||||
def prefs_store(self, *args):
|
||||
try:
|
||||
gajim.interface.save_config()
|
||||
except Exception, e:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def prefs_del(self, *args):
|
||||
[key] = self._get_real_arguments(args, 1)
|
||||
if not key:
|
||||
|
@ -457,7 +474,7 @@ class SignalObject(DbusPrototype):
|
|||
else:
|
||||
gajim.config.del_per(key_path[0], key_path[1], key_path[2])
|
||||
return True
|
||||
|
||||
|
||||
def prefs_put(self, *args):
|
||||
[key] = self._get_real_arguments(args, 1)
|
||||
if not key:
|
||||
|
@ -470,7 +487,7 @@ class SignalObject(DbusPrototype):
|
|||
subname, value = key_path[2].split('=', 1)
|
||||
gajim.config.set_per(key_path[0], key_path[1], subname, value)
|
||||
return True
|
||||
|
||||
|
||||
def add_contact(self, *args):
|
||||
[jid, account] = self._get_real_arguments(args, 2)
|
||||
if account:
|
||||
|
@ -485,11 +502,12 @@ class SignalObject(DbusPrototype):
|
|||
# if account is not given, show account combobox
|
||||
AddNewContactWindow(account = None, jid = jid)
|
||||
return True
|
||||
|
||||
|
||||
def remove_contact(self, *args):
|
||||
[jid, account] = self._get_real_arguments(args, 2)
|
||||
jid = self._get_real_jid(jid, account)
|
||||
accounts = gajim.contacts.get_accounts()
|
||||
|
||||
|
||||
# if there is only one account in roster, take it as default
|
||||
if account:
|
||||
accounts = [account]
|
||||
|
@ -503,7 +521,7 @@ class SignalObject(DbusPrototype):
|
|||
gajim.contacts.remove_jid(account, jid)
|
||||
contact_exists = True
|
||||
return contact_exists
|
||||
|
||||
|
||||
def _is_first(self):
|
||||
if self.first_show:
|
||||
self.first_show = False
|
||||
|
@ -523,7 +541,35 @@ class SignalObject(DbusPrototype):
|
|||
args.extend([None] * (desired_length - len(args)))
|
||||
args = args[:desired_length]
|
||||
return args
|
||||
|
||||
|
||||
def _get_real_jid(self, jid, account = None):
|
||||
'''get the real jid from the given one: removes xmpp: or get jid from nick
|
||||
if account is specified, search only in this account
|
||||
'''
|
||||
if account:
|
||||
accounts = [account]
|
||||
else:
|
||||
accounts = gajim.connections.keys()
|
||||
if jid.startswith('xmpp:'):
|
||||
return jid[5:] # len('xmpp:') = 5
|
||||
nick_in_roster = None # Is jid a nick ?
|
||||
for account in accounts:
|
||||
# Does jid exists in roster of one account ?
|
||||
if gajim.contacts.get_contacts_from_jid(account, jid):
|
||||
return jid
|
||||
if not nick_in_roster:
|
||||
# look in all contact if one has jid as nick
|
||||
for jid_ in gajim.contacts.get_jid_list(account):
|
||||
c = gajim.contacts.get_contacts_from_jid(account, jid_)
|
||||
if c[0].name == jid:
|
||||
nick_in_roster = jid_
|
||||
break
|
||||
if nick_in_roster:
|
||||
# We have not found jid in roster, but we found is as a nick
|
||||
return nick_in_roster
|
||||
# We have not found it as jid nor as nick, probably a not in roster jid
|
||||
return jid
|
||||
|
||||
def _contacts_as_dbus_structure(self, contacts):
|
||||
''' get info from list of Contact objects and create dbus dict '''
|
||||
if not contacts:
|
||||
|
@ -552,7 +598,7 @@ class SignalObject(DbusPrototype):
|
|||
return contact_dict
|
||||
|
||||
def get_unread_msgs_number(self, *args):
|
||||
return str(gajim.interface.roster.nb_unread)
|
||||
return str(gajim.events.get_nb_events)
|
||||
|
||||
def start_chat(self, *args):
|
||||
[account] = self._get_real_arguments(args, 1)
|
||||
|
@ -562,13 +608,21 @@ class SignalObject(DbusPrototype):
|
|||
NewChatDialog(account)
|
||||
return True
|
||||
|
||||
def send_xml(self, *args):
|
||||
xml, account = self._get_real_arguments(args, 2)
|
||||
if account:
|
||||
gajim.connections[account[0]].send_stanza(xml)
|
||||
else:
|
||||
for acc in gajim.contacts.get_accounts():
|
||||
gajim.connections[acc].send_stanza(xml)
|
||||
|
||||
if dbus_support.version[1] >= 30 and dbus_support.version[1] <= 40:
|
||||
method = dbus.method
|
||||
signal = dbus.signal
|
||||
elif dbus_support.version[1] >= 41:
|
||||
method = dbus.service.method
|
||||
signal = dbus.service.signal
|
||||
|
||||
|
||||
# prevent using decorators, because they are not supported
|
||||
# on python < 2.4
|
||||
# FIXME: use decorators when python2.3 (and dbus 0.23) is OOOOOOLD
|
||||
|
@ -579,7 +633,8 @@ class SignalObject(DbusPrototype):
|
|||
change_status = method(INTERFACE)(change_status)
|
||||
open_chat = method(INTERFACE)(open_chat)
|
||||
contact_info = method(INTERFACE)(contact_info)
|
||||
send_message = method(INTERFACE)(send_message)
|
||||
send_message = method(INTERFACE)(send_chat_message)
|
||||
send_single_message = method(INTERFACE)(send_single_message)
|
||||
send_file = method(INTERFACE)(send_file)
|
||||
prefs_list = method(INTERFACE)(prefs_list)
|
||||
prefs_put = method(INTERFACE)(prefs_put)
|
||||
|
@ -592,3 +647,4 @@ class SignalObject(DbusPrototype):
|
|||
account_info = method(INTERFACE)(account_info)
|
||||
get_unread_msgs_number = method(INTERFACE)(get_unread_msgs_number)
|
||||
start_chat = method(INTERFACE)(start_chat)
|
||||
send_xml = method(INTERFACE)(send_xml)
|
||||
|
|
1086
src/roster_window.py
1086
src/roster_window.py
File diff suppressed because it is too large
Load Diff
|
@ -2,7 +2,7 @@
|
|||
##
|
||||
## Copyright (C) 2003-2006 Yann Le Boulanger <asterix@lagaule.org>
|
||||
## Copyright (C) 2003-2004 Vincent Hanquez <tab@snarc.org>
|
||||
## Copyright (C) 2005-2006 Nikos Kouremenos <nkour@jabber.org>
|
||||
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem@gmail.com>
|
||||
## Copyright (C) 2005 Dimitur Kirov <dkirov@gmail.com>
|
||||
## Copyright (C) 2005-2006 Travis Shirk <travis@pobox.com>
|
||||
## Copyright (C) 2005 Norman Rasmussen <norman@rasmussen.co.za>
|
||||
|
@ -18,7 +18,6 @@
|
|||
##
|
||||
|
||||
import gtk
|
||||
import gtk.glade
|
||||
import gobject
|
||||
import os
|
||||
|
||||
|
@ -29,7 +28,6 @@ import gtkgui_helpers
|
|||
|
||||
from common import gajim
|
||||
from common import helpers
|
||||
from common import i18n
|
||||
|
||||
HAS_SYSTRAY_CAPABILITIES = True
|
||||
|
||||
|
@ -42,19 +40,13 @@ except:
|
|||
gajim.log.debug('No trayicon module available')
|
||||
HAS_SYSTRAY_CAPABILITIES = False
|
||||
|
||||
_ = i18n._
|
||||
APP = i18n.APP
|
||||
gtk.glade.bindtextdomain(APP, i18n.DIR)
|
||||
gtk.glade.textdomain(APP)
|
||||
|
||||
|
||||
class Systray:
|
||||
'''Class for icon in the notification area
|
||||
This class is both base class (for systraywin32.py) and normal class
|
||||
for trayicon in GNU/Linux'''
|
||||
|
||||
|
||||
def __init__(self):
|
||||
self.jids = [] # Contain things like [account, jid, type_of_msg]
|
||||
self.single_message_handler_id = None
|
||||
self.new_chat_handler_id = None
|
||||
self.t = None
|
||||
|
@ -66,7 +58,9 @@ class Systray:
|
|||
self.popup_menus = []
|
||||
|
||||
def set_img(self):
|
||||
if len(self.jids) > 0:
|
||||
if not gajim.interface.systray_enabled:
|
||||
return
|
||||
if gajim.events.get_nb_systray_events():
|
||||
state = 'message'
|
||||
else:
|
||||
state = self.status
|
||||
|
@ -76,26 +70,13 @@ class Systray:
|
|||
elif image.get_storage_type() == gtk.IMAGE_PIXBUF:
|
||||
self.img_tray.set_from_pixbuf(image.get_pixbuf())
|
||||
|
||||
def add_jid(self, jid, account, typ):
|
||||
l = [account, jid, typ]
|
||||
# We can keep several single message 'cause we open them one by one
|
||||
if not l in self.jids or typ == 'normal':
|
||||
self.jids.append(l)
|
||||
self.set_img()
|
||||
|
||||
def remove_jid(self, jid, account, typ):
|
||||
l = [account, jid, typ]
|
||||
if l in self.jids:
|
||||
self.jids.remove(l)
|
||||
self.set_img()
|
||||
|
||||
def change_status(self, global_status):
|
||||
''' set tray image to 'global_status' '''
|
||||
# change image and status, only if it is different
|
||||
if global_status is not None and self.status != global_status:
|
||||
self.status = global_status
|
||||
self.set_img()
|
||||
|
||||
|
||||
def start_chat(self, widget, account, jid):
|
||||
contact = gajim.contacts.get_first_contact_from_jid(account, jid)
|
||||
if gajim.interface.msg_win_mgr.has_window(jid, account):
|
||||
|
@ -106,13 +87,13 @@ class Systray:
|
|||
gajim.interface.roster.new_chat(contact, account)
|
||||
gajim.interface.msg_win_mgr.get_window(jid, account).set_active_tab(
|
||||
jid, account)
|
||||
|
||||
|
||||
def on_single_message_menuitem_activate(self, widget, account):
|
||||
dialogs.SingleMessageWindow(account, action = 'send')
|
||||
|
||||
def on_new_chat(self, widget, account):
|
||||
dialogs.NewChatDialog(account)
|
||||
|
||||
|
||||
def make_menu(self, event = None):
|
||||
'''create chat with and new message (sub) menus/menuitems
|
||||
event is None when we're in Windows
|
||||
|
@ -125,7 +106,7 @@ class Systray:
|
|||
single_message_menuitem = self.xml.get_widget('single_message_menuitem')
|
||||
status_menuitem = self.xml.get_widget('status_menu')
|
||||
join_gc_menuitem = self.xml.get_widget('join_gc_menuitem')
|
||||
|
||||
|
||||
if self.single_message_handler_id:
|
||||
single_message_menuitem.handler_disconnect(
|
||||
self.single_message_handler_id)
|
||||
|
@ -137,7 +118,7 @@ class Systray:
|
|||
sub_menu = gtk.Menu()
|
||||
self.popup_menus.append(sub_menu)
|
||||
status_menuitem.set_submenu(sub_menu)
|
||||
|
||||
|
||||
gc_sub_menu = gtk.Menu() # gc is always a submenu
|
||||
join_gc_menuitem.set_submenu(gc_sub_menu)
|
||||
|
||||
|
@ -182,7 +163,7 @@ class Systray:
|
|||
chat_with_menuitem.set_sensitive(iskey)
|
||||
single_message_menuitem.set_sensitive(iskey)
|
||||
join_gc_menuitem.set_sensitive(iskey)
|
||||
|
||||
|
||||
if connected_accounts >= 2: # 2 or more connections? make submenus
|
||||
account_menu_for_chat_with = gtk.Menu()
|
||||
chat_with_menuitem.set_submenu(account_menu_for_chat_with)
|
||||
|
@ -191,7 +172,7 @@ class Systray:
|
|||
account_menu_for_single_message = gtk.Menu()
|
||||
single_message_menuitem.set_submenu(account_menu_for_single_message)
|
||||
self.popup_menus.append(account_menu_for_single_message)
|
||||
|
||||
|
||||
accounts_list = gajim.contacts.get_accounts()
|
||||
accounts_list.sort()
|
||||
for account in accounts_list:
|
||||
|
@ -215,7 +196,7 @@ class Systray:
|
|||
gc_item.add(label)
|
||||
gc_sub_menu.append(gc_item)
|
||||
gajim.interface.roster.add_bookmarks_list(gc_sub_menu, account)
|
||||
|
||||
|
||||
elif connected_accounts == 1: # one account
|
||||
# one account connected, no need to show 'as jid'
|
||||
for account in gajim.connections:
|
||||
|
@ -230,7 +211,7 @@ class Systray:
|
|||
# join gc
|
||||
gajim.interface.roster.add_bookmarks_list(gc_sub_menu, account)
|
||||
break # No other connected account
|
||||
|
||||
|
||||
if event is None:
|
||||
# None means windows (we explicitly popup in systraywin32.py)
|
||||
if self.added_hide_menuitem is False:
|
||||
|
@ -238,14 +219,18 @@ class Systray:
|
|||
item = gtk.MenuItem(_('Hide this menu'))
|
||||
self.systray_context_menu.prepend(item)
|
||||
self.added_hide_menuitem = True
|
||||
|
||||
|
||||
else: # GNU and Unices
|
||||
self.systray_context_menu.popup(None, None, None, event.button, event.time)
|
||||
self.systray_context_menu.popup(None, None, None, event.button,
|
||||
event.time)
|
||||
self.systray_context_menu.show_all()
|
||||
|
||||
def on_show_all_events_menuitem_activate(self, widget):
|
||||
for i in range(len(self.jids)):
|
||||
self.handle_first_event()
|
||||
events = gajim.events.get_systray_events()
|
||||
for account in events:
|
||||
for jid in events[account]:
|
||||
for event in events[account][jid]:
|
||||
gajim.interface.handle_event(account, jid, event.type_)
|
||||
|
||||
def on_show_roster_menuitem_activate(self, widget):
|
||||
win = gajim.interface.roster.window
|
||||
|
@ -262,11 +247,11 @@ class Systray:
|
|||
|
||||
def on_left_click(self):
|
||||
win = gajim.interface.roster.window
|
||||
if len(self.jids) == 0:
|
||||
if len(gajim.events.get_systray_events()) == 0:
|
||||
# no pending events, so toggle visible/hidden for roster window
|
||||
if win.get_property('visible'): # visible in ANY virtual desktop?
|
||||
win.hide() # we hide it from VD that was visible in
|
||||
|
||||
|
||||
# but we could be in another VD right now. eg vd2
|
||||
# and we want not only to hide it in vd1 but also show it in vd2
|
||||
gtkgui_helpers.possibly_move_window_in_current_desktop(win)
|
||||
|
@ -276,10 +261,8 @@ class Systray:
|
|||
self.handle_first_event()
|
||||
|
||||
def handle_first_event(self):
|
||||
account = self.jids[0][0]
|
||||
jid = self.jids[0][1]
|
||||
typ = self.jids[0][2]
|
||||
gajim.interface.handle_event(account, jid, typ)
|
||||
account, jid, event = gajim.events.get_first_systray_event()
|
||||
gajim.interface.handle_event(account, jid, event.type_)
|
||||
|
||||
def on_middle_click(self):
|
||||
'''middle click raises window to have complete focus (fe. get kbd events)
|
||||
|
@ -292,13 +275,13 @@ class Systray:
|
|||
|
||||
def on_clicked(self, widget, event):
|
||||
self.on_tray_leave_notify_event(widget, None)
|
||||
if event.button == 1: # Left click
|
||||
if event.type == gtk.gdk.BUTTON_PRESS and event.button == 1: # Left click
|
||||
self.on_left_click()
|
||||
elif event.button == 2: # middle click
|
||||
self.on_middle_click()
|
||||
elif event.button == 3: # right click
|
||||
self.make_menu(event)
|
||||
|
||||
|
||||
def on_show_menuitem_activate(self, widget, show):
|
||||
# we all add some fake (we cannot select those nor have them as show)
|
||||
# but this helps to align with roster's status_combobox index positions
|
||||
|
@ -327,7 +310,7 @@ class Systray:
|
|||
if self.tooltip.id == position:
|
||||
size = widget.window.get_size()
|
||||
self.tooltip.show_tooltip('', size[1], position[1])
|
||||
|
||||
|
||||
def on_tray_motion_notify_event(self, widget, event):
|
||||
wireq=widget.size_request()
|
||||
position = widget.window.get_origin()
|
||||
|
@ -339,16 +322,23 @@ class Systray:
|
|||
self.tooltip.id = position
|
||||
self.tooltip.timeout = gobject.timeout_add(500,
|
||||
self.show_tooltip, widget)
|
||||
|
||||
|
||||
def on_tray_leave_notify_event(self, widget, event):
|
||||
position = widget.window.get_origin()
|
||||
if self.tooltip.timeout > 0 and \
|
||||
self.tooltip.id == position:
|
||||
self.tooltip.hide_tooltip()
|
||||
|
||||
|
||||
def on_tray_destroyed(self, widget):
|
||||
'''re-add trayicon when systray is destroyed'''
|
||||
self.t = None
|
||||
if gajim.interface.systray_enabled:
|
||||
self.show_icon()
|
||||
|
||||
def show_icon(self):
|
||||
if not self.t:
|
||||
self.t = trayicon.TrayIcon('Gajim')
|
||||
self.t.connect('destroy', self.on_tray_destroyed)
|
||||
eb = gtk.EventBox()
|
||||
# avoid draw seperate bg color in some gtk themes
|
||||
eb.set_visible_window(False)
|
||||
|
@ -363,7 +353,7 @@ class Systray:
|
|||
self.t.add(eb)
|
||||
self.set_img()
|
||||
self.t.show_all()
|
||||
|
||||
|
||||
def hide_icon(self):
|
||||
if self.t:
|
||||
self.t.destroy()
|
||||
|
|
|
@ -42,10 +42,6 @@ WM_TRAYMESSAGE = win32con.WM_USER + 20
|
|||
import gtkgui_helpers
|
||||
from common import gajim
|
||||
from common import i18n
|
||||
_ = i18n._
|
||||
APP = i18n.APP
|
||||
gtk.glade.bindtextdomain(APP, i18n.DIR)
|
||||
gtk.glade.textdomain(APP)
|
||||
|
||||
class SystrayWINAPI:
|
||||
def __init__(self, gtk_window):
|
||||
|
@ -249,36 +245,25 @@ class SystrayWin32(systray.Systray):
|
|||
elif lparam == win32con.WM_LBUTTONUP: # Left click
|
||||
self.on_left_click()
|
||||
|
||||
def add_jid(self, jid, account, typ):
|
||||
systray.Systray.add_jid(self, jid, account, typ)
|
||||
def set_img(self):
|
||||
self.tray_ico_imgs = self.load_icos() #FIXME: do not do this here
|
||||
# see gajim.interface.roster.reload_jabber_state_images() to merge
|
||||
|
||||
nb = gajim.interface.roster.nb_unread
|
||||
for acct in gajim.connections:
|
||||
# in chat / groupchat windows
|
||||
for kind in ('chats', 'gc'):
|
||||
jids = gajim.interface.instances[acct][kind]
|
||||
for jid in jids:
|
||||
if jid != 'tabbed':
|
||||
nb += jids[jid].nb_unread[jid]
|
||||
|
||||
text = i18n.ngettext(
|
||||
'Gajim - %d unread message',
|
||||
'Gajim - %d unread messages',
|
||||
nb, nb, nb)
|
||||
if len(self.jids) > 0:
|
||||
state = 'message'
|
||||
else:
|
||||
state = self.status
|
||||
hicon = self.tray_ico_imgs[state]
|
||||
if hicon is None:
|
||||
return
|
||||
|
||||
self.systray_winapi.notify_icon.set_tooltip(text)
|
||||
self.systray_winapi.remove_notify_icon()
|
||||
self.systray_winapi.add_notify_icon(self.systray_context_menu, hicon,
|
||||
'Gajim')
|
||||
self.systray_winapi.notify_icon.menu = self.systray_context_menu
|
||||
|
||||
def remove_jid(self, jid, account, typ):
|
||||
systray.Systray.remove_jid(self, jid, account, typ)
|
||||
nb = gajim.events.get_nb_systray_events()
|
||||
|
||||
nb = gajim.interface.roster.nb_unread
|
||||
for acct in gajim.connections:
|
||||
# in chat / groupchat windows
|
||||
for kind in ('chats', 'gc'):
|
||||
for jid in gajim.interface.instances[acct][kind]:
|
||||
if jid != 'tabbed':
|
||||
nb += gajim.interface.instances[acct][kind][jid].nb_unread[jid]
|
||||
|
||||
if nb > 0:
|
||||
text = i18n.ngettext(
|
||||
'Gajim - %d unread message',
|
||||
|
@ -288,23 +273,6 @@ class SystrayWin32(systray.Systray):
|
|||
text = 'Gajim'
|
||||
self.systray_winapi.notify_icon.set_tooltip(text)
|
||||
|
||||
def set_img(self):
|
||||
self.tray_ico_imgs = self.load_icos() #FIXME: do not do this here
|
||||
# see gajim.interface.roster.reload_jabber_state_images() to merge
|
||||
|
||||
if len(self.jids) > 0:
|
||||
state = 'message'
|
||||
else:
|
||||
state = self.status
|
||||
hicon = self.tray_ico_imgs[state]
|
||||
if hicon is None:
|
||||
return
|
||||
|
||||
self.systray_winapi.remove_notify_icon()
|
||||
self.systray_winapi.add_notify_icon(self.systray_context_menu, hicon,
|
||||
'Gajim')
|
||||
self.systray_winapi.notify_icon.menu = self.systray_context_menu
|
||||
|
||||
def load_icos(self):
|
||||
'''load .ico files and return them to a dic of SHOW --> img_obj'''
|
||||
iconset = str(gajim.config.get('iconset'))
|
||||
|
|
|
@ -27,9 +27,6 @@ from common import gajim
|
|||
from common import helpers
|
||||
from common import i18n
|
||||
|
||||
_ = i18n._
|
||||
APP = i18n.APP
|
||||
|
||||
class BaseTooltip:
|
||||
''' Base Tooltip class;
|
||||
Usage:
|
||||
|
@ -285,34 +282,14 @@ class NotificationAreaTooltip(BaseTooltip, StatusTable):
|
|||
self.table.set_property('column-spacing', 1)
|
||||
text, single_line = '', ''
|
||||
|
||||
unread_chat = gajim.interface.roster.nb_unread
|
||||
unread_single_chat = 0
|
||||
unread_gc = 0
|
||||
unread_pm = 0
|
||||
unread_chat = gajim.events.get_nb_events(types = ['printed_chat', 'chat'])
|
||||
unread_single_chat = gajim.events.get_nb_events(types = ['normal'])
|
||||
unread_gc = gajim.events.get_nb_events(types = ['printed_gc_msg',
|
||||
'gc_msg'])
|
||||
unread_pm = gajim.events.get_nb_events(types = ['printed_pm', 'pm'])
|
||||
|
||||
accounts = self.get_accounts_info()
|
||||
|
||||
for acct in gajim.connections:
|
||||
# Count unread chat messages
|
||||
chat_t = message_control.TYPE_CHAT
|
||||
for ctrl in gajim.interface.msg_win_mgr.get_controls(chat_t, acct):
|
||||
unread_chat += ctrl.nb_unread
|
||||
|
||||
# Count unread PM messages for which we have a control
|
||||
chat_t = message_control.TYPE_PM
|
||||
for ctrl in gajim.interface.msg_win_mgr.get_controls(chat_t, acct):
|
||||
unread_pm += ctrl.nb_unread
|
||||
|
||||
# we count unread gc/pm messages
|
||||
chat_t = message_control.TYPE_GC
|
||||
for ctrl in gajim.interface.msg_win_mgr.get_controls(chat_t, acct):
|
||||
# These are PMs for which the PrivateChatControl has not yet been
|
||||
# created
|
||||
pm_msgs = ctrl.get_specific_unread()
|
||||
unread_gc += ctrl.nb_unread
|
||||
unread_gc -= pm_msgs
|
||||
unread_pm += pm_msgs
|
||||
|
||||
if unread_chat or unread_single_chat or unread_gc or unread_pm:
|
||||
text = 'Gajim '
|
||||
awaiting_events = unread_chat + unread_single_chat + unread_gc + unread_pm
|
||||
|
@ -391,8 +368,9 @@ class GCTooltip(BaseTooltip):
|
|||
if contact.jid.strip() != '':
|
||||
jid_markup = '<span weight="bold">' + contact.jid + '</span>'
|
||||
else:
|
||||
jid_markup = '<span weight="bold">' + contact.get_shown_name() + \
|
||||
'</span>'
|
||||
jid_markup = '<span weight="bold">' + \
|
||||
gtkgui_helpers.escape_for_pango_markup(contact.get_shown_name()) \
|
||||
+ '</span>'
|
||||
properties.append((jid_markup, None))
|
||||
properties.append((_('Role: '), helpers.get_uf_role(contact.role)))
|
||||
properties.append((_('Affiliation: '), contact.affiliation.capitalize()))
|
||||
|
@ -510,11 +488,12 @@ class RosterTooltip(NotificationAreaTooltip):
|
|||
properties = []
|
||||
jid_markup = '<span weight="bold">' + prim_contact.jid + '</span>'
|
||||
properties.append((jid_markup, None))
|
||||
|
||||
properties.append((_('Name: '), gtkgui_helpers.escape_for_pango_markup(
|
||||
prim_contact.get_shown_name())))
|
||||
prim_contact.get_shown_name())))
|
||||
if prim_contact.sub:
|
||||
properties.append(( _('Subscription: '),
|
||||
gtkgui_helpers.escape_for_pango_markup(prim_contact.sub)))
|
||||
gtkgui_helpers.escape_for_pango_markup(helpers.get_uf_sub(prim_contact.sub))))
|
||||
if prim_contact.keyID:
|
||||
keyID = None
|
||||
if len(prim_contact.keyID) == 8:
|
||||
|
@ -525,17 +504,27 @@ class RosterTooltip(NotificationAreaTooltip):
|
|||
properties.append((_('OpenPGP: '),
|
||||
gtkgui_helpers.escape_for_pango_markup(keyID)))
|
||||
num_resources = 0
|
||||
# put contacts in dict, where key is priority
|
||||
contacts_dict = {}
|
||||
for contact in contacts:
|
||||
if contact.resource:
|
||||
num_resources += 1
|
||||
|
||||
if num_resources== 1 and contact.resource:
|
||||
properties.append((_('Resource: '), gtkgui_helpers.escape_for_pango_markup(
|
||||
contact.resource) + ' (' + unicode(contact.priority) + ')'))
|
||||
if contact.priority in contacts_dict:
|
||||
contacts_dict[contact.priority].append(contact)
|
||||
else:
|
||||
contacts_dict[contact.priority] = [contact]
|
||||
|
||||
if num_resources == 1 and contact.resource:
|
||||
properties.append((_('Resource: '),
|
||||
gtkgui_helpers.escape_for_pango_markup(contact.resource) + ' (' + \
|
||||
unicode(contact.priority) + ')'))
|
||||
if num_resources > 1:
|
||||
properties.append((_('Status: '), ' '))
|
||||
for contact in contacts:
|
||||
if contact.resource:
|
||||
contact_keys = contacts_dict.keys()
|
||||
contact_keys.sort()
|
||||
contact_keys.reverse()
|
||||
for priority in contact_keys:
|
||||
for contact in contacts_dict[priority]:
|
||||
status_line = self.get_status_info(contact.resource,
|
||||
contact.priority, contact.show, contact.status)
|
||||
|
||||
|
|
291
src/vcard.py
291
src/vcard.py
|
@ -14,7 +14,6 @@
|
|||
##
|
||||
|
||||
import gtk
|
||||
import gtk.glade
|
||||
import gobject
|
||||
import base64
|
||||
import mimetypes
|
||||
|
@ -27,12 +26,7 @@ import dialogs
|
|||
|
||||
from common import helpers
|
||||
from common import gajim
|
||||
from common import i18n
|
||||
_ = i18n._
|
||||
Q_ = i18n.Q_
|
||||
APP = i18n.APP
|
||||
gtk.glade.bindtextdomain (APP, i18n.DIR)
|
||||
gtk.glade.textdomain (APP)
|
||||
from common.i18n import Q_
|
||||
|
||||
def get_avatar_pixbuf_encoded_mime(photo):
|
||||
'''return the pixbuf of the image
|
||||
|
@ -42,16 +36,20 @@ def get_avatar_pixbuf_encoded_mime(photo):
|
|||
img_decoded = None
|
||||
avatar_encoded = None
|
||||
avatar_mime_type = None
|
||||
if photo.has_key('BINVAL') and photo.has_key('TYPE'):
|
||||
if photo.has_key('BINVAL'):
|
||||
img_encoded = photo['BINVAL']
|
||||
avatar_encoded = img_encoded
|
||||
avatar_mime_type = photo['TYPE']
|
||||
try:
|
||||
img_decoded = base64.decodestring(img_encoded)
|
||||
except:
|
||||
pass
|
||||
if img_decoded:
|
||||
pixbuf = gtkgui_helpers.get_pixbuf_from_data(img_decoded)
|
||||
if photo.has_key('TYPE'):
|
||||
avatar_mime_type = photo['TYPE']
|
||||
pixbuf = gtkgui_helpers.get_pixbuf_from_data(img_decoded)
|
||||
else:
|
||||
pixbuf, avatar_mime_type = gtkgui_helpers.get_pixbuf_from_data(
|
||||
img_decoded, want_type=True)
|
||||
else:
|
||||
pixbuf = None
|
||||
return pixbuf, avatar_encoded, avatar_mime_type
|
||||
|
@ -59,50 +57,25 @@ def get_avatar_pixbuf_encoded_mime(photo):
|
|||
class VcardWindow:
|
||||
'''Class for contact's information window'''
|
||||
|
||||
def __init__(self, contact, account, vcard = False, is_fake = False):
|
||||
def __init__(self, contact, account, is_fake = False):
|
||||
# the contact variable is the jid if vcard is true
|
||||
self.xml = gtkgui_helpers.get_glade('vcard_information_window.glade')
|
||||
self.window = self.xml.get_widget('vcard_information_window')
|
||||
|
||||
self.publish_button = self.xml.get_widget('publish_button')
|
||||
self.retrieve_button = self.xml.get_widget('retrieve_button')
|
||||
self.nickname_entry = self.xml.get_widget('nickname_entry')
|
||||
if not vcard: # Maybe gc_vcard ?
|
||||
self.nickname_entry.set_property('editable', False)
|
||||
|
||||
self.publish_button.set_no_show_all(True)
|
||||
self.retrieve_button.set_no_show_all(True)
|
||||
self.xml.get_widget('photo_vbuttonbox').set_no_show_all(True)
|
||||
|
||||
self.contact = contact # don't use it if vcard is true
|
||||
self.contact = contact
|
||||
self.account = account
|
||||
self.vcard = vcard
|
||||
self.is_fake = is_fake
|
||||
|
||||
self.avatar_mime_type = None
|
||||
self.avatar_encoded = None
|
||||
|
||||
if vcard: # we view/edit our own vcard
|
||||
self.jid = contact
|
||||
# remove Jabber tab & show publish/retrieve/close/set_avatar buttons
|
||||
# and make entries and textview editable
|
||||
self.change_to_vcard()
|
||||
else: # we see someone else's vcard
|
||||
self.publish_button.hide()
|
||||
self.retrieve_button.hide()
|
||||
self.jid = contact.jid
|
||||
self.fill_jabber_page()
|
||||
|
||||
# if we are editing our own vcard publish button should publish
|
||||
# vcard data we have typed including nickname, it's why we connect only
|
||||
# here (when we see someone else's vcard)
|
||||
self.nickname_entry.connect('focus-out-event',
|
||||
self.on_nickname_entry_focus_out_event)
|
||||
self.fill_jabber_page()
|
||||
|
||||
self.xml.signal_autoconnect(self)
|
||||
self.window.show_all()
|
||||
|
||||
def on_vcard_information_window_destroy(self, widget):
|
||||
del gajim.interface.instances[self.account]['infos'][self.jid]
|
||||
del gajim.interface.instances[self.account]['infos'][self.contact.jid]
|
||||
|
||||
def on_vcard_information_window_key_press_event(self, widget, event):
|
||||
if event.keyval == gtk.keysyms.Escape:
|
||||
|
@ -123,86 +96,20 @@ class VcardWindow:
|
|||
if oldlog != log:
|
||||
gajim.config.set_per('accounts', self.account, 'no_log_for',
|
||||
' '.join(no_log_for))
|
||||
|
||||
def on_nickname_entry_focus_out_event(self, widget, event):
|
||||
'''Save contact information and update
|
||||
the roster item on the Jabber server'''
|
||||
new_name = self.nickname_entry.get_text().decode('utf-8')
|
||||
# update contact.name with new nickname if that is not ''
|
||||
if new_name != self.contact.name and new_name != '':
|
||||
self.contact.name = new_name
|
||||
# update roster model
|
||||
model = gajim.interface.roster.tree.get_model()
|
||||
for iter_ in gajim.interface.roster.get_contact_iter(self.contact.jid,
|
||||
self.account):
|
||||
model[iter_][1] = new_name
|
||||
gajim.connections[self.account].update_contact(self.contact.jid,
|
||||
self.contact.name, self.contact.groups)
|
||||
# update opened chat window
|
||||
ctrl = gajim.interface.msg_win_mgr.get_control(self.contact.jid,
|
||||
self.account)
|
||||
if ctrl:
|
||||
ctrl.update_ui()
|
||||
win = gajim.interface.msg_win_mgr.get_window(self.contact.jid,
|
||||
self.account)
|
||||
win.redraw_tab(ctrl)
|
||||
win.show_title()
|
||||
|
||||
def on_close_button_clicked(self, widget):
|
||||
self.window.destroy()
|
||||
|
||||
def on_clear_button_clicked(self, widget):
|
||||
# empty the image
|
||||
self.xml.get_widget('PHOTO_image').set_from_pixbuf(None)
|
||||
self.avatar_encoded = None
|
||||
|
||||
def on_set_avatar_button_clicked(self, widget):
|
||||
f = None
|
||||
def on_ok(widget, path_to_file):
|
||||
filesize = os.path.getsize(path_to_file) # in bytes
|
||||
#FIXME: use messages for invalid file for 0.11
|
||||
invalid_file = False
|
||||
msg = ''
|
||||
if os.path.isfile(path_to_file):
|
||||
stat = os.stat(path_to_file)
|
||||
if stat[6] == 0:
|
||||
invalid_file = True
|
||||
else:
|
||||
invalid_file = True
|
||||
if not invalid_file and filesize > 16384: # 16 kb
|
||||
try:
|
||||
pixbuf = gtk.gdk.pixbuf_new_from_file(path_to_file)
|
||||
# get the image at 'notification size'
|
||||
# and use that user did not specify in ACE crazy size
|
||||
scaled_pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf,
|
||||
'tooltip')
|
||||
except gobject.GError, msg: # unknown format
|
||||
# msg should be string, not object instance
|
||||
msg = str(msg)
|
||||
invalid_file = True
|
||||
if invalid_file:
|
||||
if True: # keep identation
|
||||
dialogs.ErrorDialog(_('Could not load image'), msg)
|
||||
return
|
||||
if filesize > 16384:
|
||||
if scaled_pixbuf:
|
||||
path_to_file = os.path.join(gajim.TMP,
|
||||
'avatar_scaled.png')
|
||||
scaled_pixbuf.save(path_to_file, 'png')
|
||||
self.dialog.destroy()
|
||||
|
||||
fd = open(path_to_file, 'rb')
|
||||
data = fd.read()
|
||||
pixbuf = gtkgui_helpers.get_pixbuf_from_data(data)
|
||||
# rescale it
|
||||
pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf, 'vcard')
|
||||
image = self.xml.get_widget('PHOTO_image')
|
||||
image.set_from_pixbuf(pixbuf)
|
||||
self.avatar_encoded = base64.encodestring(data)
|
||||
# returns None if unknown type
|
||||
self.avatar_mime_type = mimetypes.guess_type(path_to_file)[0]
|
||||
|
||||
self.dialog = dialogs.ImageChooserDialog(on_response_ok = on_ok)
|
||||
def on_PHOTO_eventbox_button_press_event(self, widget, event):
|
||||
'''If right-clicked, show popup'''
|
||||
if event.button == 3: # right click
|
||||
menu = gtk.Menu()
|
||||
menuitem = gtk.ImageMenuItem(gtk.STOCK_SAVE_AS)
|
||||
menuitem.connect('activate',
|
||||
gtkgui_helpers.on_avatar_save_as_menuitem_activate,
|
||||
self.contact.jid, self.account, self.contact.name + '.jpeg')
|
||||
menu.append(menuitem)
|
||||
menu.connect('selection-done', lambda w:w.destroy())
|
||||
# show the menu
|
||||
menu.show_all()
|
||||
menu.popup(None, None, None, event.button, event.time)
|
||||
|
||||
def set_value(self, entry_name, value):
|
||||
try:
|
||||
|
@ -215,9 +122,11 @@ class VcardWindow:
|
|||
if i == 'PHOTO':
|
||||
pixbuf, self.avatar_encoded, self.avatar_mime_type = \
|
||||
get_avatar_pixbuf_encoded_mime(vcard[i])
|
||||
if not pixbuf:
|
||||
continue
|
||||
image = self.xml.get_widget('PHOTO_image')
|
||||
if not pixbuf:
|
||||
image.set_from_icon_name('stock_person',
|
||||
gtk.ICON_SIZE_DIALOG)
|
||||
continue
|
||||
pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf, 'vcard')
|
||||
image.set_from_pixbuf(pixbuf)
|
||||
continue
|
||||
|
@ -227,21 +136,23 @@ class VcardWindow:
|
|||
if 'WORK' in entry:
|
||||
add_on = '_WORK'
|
||||
for j in entry.keys():
|
||||
self.set_value(i + add_on + '_' + j + '_entry', entry[j])
|
||||
self.set_value(i + add_on + '_' + j + '_label', entry[j])
|
||||
if isinstance(vcard[i], dict):
|
||||
for j in vcard[i].keys():
|
||||
self.set_value(i + '_' + j + '_entry', vcard[i][j])
|
||||
self.set_value(i + '_' + j + '_label', vcard[i][j])
|
||||
else:
|
||||
if i == 'DESC':
|
||||
self.xml.get_widget('DESC_textview').get_buffer().set_text(
|
||||
vcard[i], 0)
|
||||
else:
|
||||
self.set_value(i + '_entry', vcard[i])
|
||||
self.set_value(i + '_label', vcard[i])
|
||||
|
||||
def set_last_status_time(self):
|
||||
self.fill_status_label()
|
||||
|
||||
def set_os_info(self, resource, client_info, os_info):
|
||||
if self.xml.get_widget('information_notebook').get_n_pages() < 4:
|
||||
return
|
||||
i = 0
|
||||
client = ''
|
||||
os = ''
|
||||
|
@ -265,6 +176,8 @@ class VcardWindow:
|
|||
self.xml.get_widget('os_label').set_text(os)
|
||||
|
||||
def fill_status_label(self):
|
||||
if self.xml.get_widget('information_notebook').get_n_pages() < 4:
|
||||
return
|
||||
contact_list = gajim.contacts.get_contact(self.account, self.contact.jid)
|
||||
# stats holds show and status message
|
||||
stats = ''
|
||||
|
@ -280,7 +193,7 @@ class VcardWindow:
|
|||
stats += '\n' + _('since %s') % time.strftime('%c',
|
||||
c.last_status_time).decode(locale.getpreferredencoding())
|
||||
one = False
|
||||
elif not self.vcard: # Maybe gc_vcard ?
|
||||
else: # Maybe gc_vcard ?
|
||||
stats = helpers.get_uf_show(self.contact.show)
|
||||
if self.contact.status:
|
||||
stats += ': ' + self.contact.status
|
||||
|
@ -294,8 +207,10 @@ class VcardWindow:
|
|||
|
||||
def fill_jabber_page(self):
|
||||
tooltips = gtk.Tooltips()
|
||||
self.xml.get_widget('nickname_label').set_text(
|
||||
self.contact.get_shown_name())
|
||||
self.xml.get_widget('nickname_label').set_markup(
|
||||
'<b><span size="x-large">' +
|
||||
self.contact.get_shown_name() +
|
||||
'</span></b>')
|
||||
self.xml.get_widget('jid_label').set_text(self.contact.jid)
|
||||
uf_sub = helpers.get_uf_sub(self.contact.sub)
|
||||
self.xml.get_widget('subscription_label').set_text(uf_sub)
|
||||
|
@ -317,7 +232,6 @@ class VcardWindow:
|
|||
if self.contact.ask == 'subscribe':
|
||||
tooltips.set_tip(eb,
|
||||
_("You are waiting contact's answer about your subscription request"))
|
||||
self.nickname_entry.set_text(self.contact.name)
|
||||
log = True
|
||||
if self.contact.jid in gajim.config.get_per('accounts', self.account,
|
||||
'no_log_for').split(' '):
|
||||
|
@ -339,8 +253,8 @@ class VcardWindow:
|
|||
|
||||
# Request os info in contact is connected
|
||||
if self.contact.show not in ('offline', 'error'):
|
||||
gajim.connections[self.account].request_os_info(self.contact.jid,
|
||||
self.contact.resource)
|
||||
gobject.idle_add(gajim.connections[self.account].request_os_info,
|
||||
self.contact.jid, self.contact.resource)
|
||||
self.os_info = {0: {'resource': self.contact.resource, 'client': '',
|
||||
'os': ''}}
|
||||
i = 1
|
||||
|
@ -353,7 +267,8 @@ class VcardWindow:
|
|||
uf_resources += '\n' + c.resource + \
|
||||
_(' resource with priority ') + unicode(c.priority)
|
||||
if c.show not in ('offline', 'error'):
|
||||
gajim.connections[self.account].request_os_info(c.jid,
|
||||
gobject.idle_add(
|
||||
gajim.connections[self.account].request_os_info, c.jid,
|
||||
c.resource)
|
||||
gajim.connections[self.account].request_last_status_time(c.jid,
|
||||
c.resource)
|
||||
|
@ -368,117 +283,3 @@ class VcardWindow:
|
|||
self.fill_status_label()
|
||||
|
||||
gajim.connections[self.account].request_vcard(self.contact.jid, self.is_fake)
|
||||
|
||||
def add_to_vcard(self, vcard, entry, txt):
|
||||
'''Add an information to the vCard dictionary'''
|
||||
entries = entry.split('_')
|
||||
loc = vcard
|
||||
if len(entries) == 3: # We need to use lists
|
||||
if not loc.has_key(entries[0]):
|
||||
loc[entries[0]] = []
|
||||
found = False
|
||||
for e in loc[entries[0]]:
|
||||
if entries[1] in e:
|
||||
found = True
|
||||
break
|
||||
if found:
|
||||
e[entries[2]] = txt
|
||||
else:
|
||||
loc[entries[0]].append({entries[1]: '', entries[2]: txt})
|
||||
return vcard
|
||||
while len(entries) > 1:
|
||||
if not loc.has_key(entries[0]):
|
||||
loc[entries[0]] = {}
|
||||
loc = loc[entries[0]]
|
||||
del entries[0]
|
||||
loc[entries[0]] = txt
|
||||
return vcard
|
||||
|
||||
def make_vcard(self):
|
||||
'''make the vCard dictionary'''
|
||||
entries = ['FN', 'NICKNAME', 'BDAY', 'EMAIL_HOME_USERID', 'URL',
|
||||
'TEL_HOME_NUMBER', 'N_FAMILY', 'N_GIVEN', 'N_MIDDLE', 'N_PREFIX',
|
||||
'N_SUFFIX', 'ADR_HOME_STREET', 'ADR_HOME_EXTADR', 'ADR_HOME_LOCALITY',
|
||||
'ADR_HOME_REGION', 'ADR_HOME_PCODE', 'ADR_HOME_CTRY', 'ORG_ORGNAME',
|
||||
'ORG_ORGUNIT', 'TITLE', 'ROLE', 'TEL_WORK_NUMBER', 'EMAIL_WORK_USERID',
|
||||
'ADR_WORK_STREET', 'ADR_WORK_EXTADR', 'ADR_WORK_LOCALITY',
|
||||
'ADR_WORK_REGION', 'ADR_WORK_PCODE', 'ADR_WORK_CTRY']
|
||||
vcard = {}
|
||||
for e in entries:
|
||||
txt = self.xml.get_widget(e + '_entry').get_text().decode('utf-8')
|
||||
if txt != '':
|
||||
vcard = self.add_to_vcard(vcard, e, txt)
|
||||
|
||||
# DESC textview
|
||||
buff = self.xml.get_widget('DESC_textview').get_buffer()
|
||||
start_iter = buff.get_start_iter()
|
||||
end_iter = buff.get_end_iter()
|
||||
txt = buff.get_text(start_iter, end_iter, 0)
|
||||
if txt != '':
|
||||
vcard['DESC'] = txt.decode('utf-8')
|
||||
|
||||
# Avatar
|
||||
if self.avatar_encoded:
|
||||
vcard['PHOTO'] = {'BINVAL': self.avatar_encoded}
|
||||
if self.avatar_mime_type:
|
||||
vcard['PHOTO']['TYPE'] = self.avatar_mime_type
|
||||
return vcard
|
||||
|
||||
def on_publish_button_clicked(self, widget):
|
||||
if gajim.connections[self.account].connected < 2:
|
||||
dialogs.ErrorDialog(_('You are not connected to the server'),
|
||||
_('Without a connection you can not publish your contact '
|
||||
'information.'))
|
||||
return
|
||||
vcard = self.make_vcard()
|
||||
nick = ''
|
||||
if vcard.has_key('NICKNAME'):
|
||||
nick = vcard['NICKNAME']
|
||||
if nick == '':
|
||||
nick = gajim.config.get_per('accounts', self.account, 'name')
|
||||
gajim.nicks[self.account] = nick
|
||||
gajim.connections[self.account].send_vcard(vcard)
|
||||
|
||||
def on_retrieve_button_clicked(self, widget):
|
||||
entries = ['FN', 'NICKNAME', 'BDAY', 'EMAIL_HOME_USERID', 'URL',
|
||||
'TEL_HOME_NUMBER', 'N_FAMILY', 'N_GIVEN', 'N_MIDDLE', 'N_PREFIX',
|
||||
'N_SUFFIX', 'ADR_HOME_STREET', 'ADR_HOME_EXTADR', 'ADR_HOME_LOCALITY',
|
||||
'ADR_HOME_REGION', 'ADR_HOME_PCODE', 'ADR_HOME_CTRY', 'ORG_ORGNAME',
|
||||
'ORG_ORGUNIT', 'TITLE', 'ROLE', 'ADR_WORK_STREET', 'ADR_WORK_EXTADR',
|
||||
'ADR_WORK_LOCALITY', 'ADR_WORK_REGION', 'ADR_WORK_PCODE',
|
||||
'ADR_WORK_CTRY']
|
||||
if gajim.connections[self.account].connected > 1:
|
||||
# clear all entries
|
||||
for e in entries:
|
||||
self.xml.get_widget(e + '_entry').set_text('')
|
||||
self.xml.get_widget('DESC_textview').get_buffer().set_text('')
|
||||
self.xml.get_widget('PHOTO_image').set_from_pixbuf(None)
|
||||
gajim.connections[self.account].request_vcard(self.jid)
|
||||
else:
|
||||
dialogs.ErrorDialog(_('You are not connected to the server'),
|
||||
_('Without a connection, you can not get your contact information.'))
|
||||
|
||||
def change_to_vcard(self):
|
||||
self.xml.get_widget('information_notebook').remove_page(0)
|
||||
self.xml.get_widget('nickname_label').set_text(_('Personal details'))
|
||||
|
||||
self.publish_button.show()
|
||||
self.retrieve_button.show()
|
||||
|
||||
#photo_vbuttonbox visible
|
||||
self.xml.get_widget('photo_vbuttonbox').show()
|
||||
|
||||
#make all entries editable
|
||||
entries = ['FN', 'NICKNAME', 'BDAY', 'EMAIL_HOME_USERID', 'URL',
|
||||
'TEL_HOME_NUMBER', 'N_FAMILY', 'N_GIVEN', 'N_MIDDLE', 'N_PREFIX',
|
||||
'N_SUFFIX', 'ADR_HOME_STREET', 'ADR_HOME_EXTADR', 'ADR_HOME_LOCALITY',
|
||||
'ADR_HOME_REGION', 'ADR_HOME_PCODE', 'ADR_HOME_CTRY', 'ORG_ORGNAME',
|
||||
'ORG_ORGUNIT', 'TITLE', 'ROLE', 'TEL_WORK_NUMBER', 'EMAIL_WORK_USERID',
|
||||
'ADR_WORK_STREET', 'ADR_WORK_EXTADR', 'ADR_WORK_LOCALITY',
|
||||
'ADR_WORK_REGION', 'ADR_WORK_PCODE', 'ADR_WORK_CTRY']
|
||||
for e in entries:
|
||||
self.xml.get_widget(e + '_entry').set_property('editable', True)
|
||||
|
||||
description_textview = self.xml.get_widget('DESC_textview')
|
||||
description_textview.set_editable(True)
|
||||
description_textview.set_cursor_visible(True)
|
||||
|
|
Loading…
Reference in New Issue