Commit 857ba5f7 authored by Yann Leboulanger's avatar Yann Leboulanger

new way to handle incominf messages, new notification event.

parent 92505408
......@@ -45,7 +45,7 @@ def init(self):
#self.gui_extension_points = {}
#self.config_default_values = {}
self.events_handlers = {'NewMessage' : (ged.POSTCORE, self.newMessage)}
self.events_handlers = {'notification' : (ged.POSTCORE, self.notif)}
@log_calls('SnarlNotificationsPlugin')
def activate(self):
......@@ -56,29 +56,12 @@ def deactivate(self):
pass
@log_calls('SnarlNotificationsPlugin')
def newMessage(self, args):
event_name = "NewMessage"
data = args
account = data[0]
jid = data[1][0]
jid_without_resource = gajim.get_jid_without_resource(jid)
msg = data[1][1]
msg_type = data[1][4]
if msg_type == 'chat':
nickname = gajim.get_contact_name_from_jid(account, jid_without_resource)
elif msg_type in ('pm', 'groupchat'):
nickname = gajim.get_resource_from_jid(jid)
else:
nickname = jid
print "Event '%s' occured. Arguments: %s\n\n===\n"%(event_name, pformat(args))
print "Event '%s' occured. Arguments: \naccount = %s\njid = %s\nmsg = %s\nnickname = %s"%(
event_name, account, jid, msg, nickname)
def notif(self, obj):
print "Event '%s' occured.\n\n===\n" % obj.popup_event_type
#if PySnarl.snGetVersion() != False:
#(major, minor) = PySnarl.snGetVersion()
#print "Found Snarl version",str(major)+"."+str(minor),"running."
#PySnarl.snShowMessage(nickname, msg[:20]+'...')
#PySnarl.snShowMessage(obj.popup_title, obj.popup_text)
#else:
#print "Sorry Snarl does not appear to be running"
......@@ -2954,7 +2954,7 @@ def read_queue(self):
if len(data) > 6 and isinstance(data[6], int):
message_ids.append(data[6])
if len(data) > 8:
if len(data) > 8 and not self.session:
self.set_session(data[8])
if message_ids:
gajim.logger.set_read_messages(message_ids)
......
......@@ -1240,6 +1240,7 @@ def __init__(self):
gajim.nec.register_incoming_event(ArchivingErrorReceivedEvent)
gajim.nec.register_incoming_event(
ArchivingPreferencesChangedReceivedEvent)
gajim.nec.register_incoming_event(NotificationEvent)
gajim.ged.register_event_handler('http-auth-received', ged.CORE,
self._nec_http_auth_received)
......
......@@ -35,6 +35,8 @@
from common.logger import LOG_DB_PATH
from common.pep import SUPPORTED_PERSONAL_USER_EVENTS
import gtkgui_helpers
import logging
log = logging.getLogger('gajim.c.connection_handlers_events')
......@@ -1888,3 +1890,155 @@ def generate(self):
self.prompt = None
self.prompt_jid = None
return True
class NotificationEvent(nec.NetworkIncomingEvent):
name = 'notification'
base_network_events = ['decrypted-message-received', 'gc-message-received']
def detect_type(self):
if self.base_event.name == 'decrypted-message-received':
self.notif_type = 'msg'
if self.base_event.name == 'gc-message-received':
self.notif_type = 'gc-msg'
def get_focused(self):
self.control_focused = False
if self.control:
parent_win = self.control.parent_win
if parent_win and self.control == parent_win.get_active_control() \
and parent_win.window.has_focus:
self.control_focused = True
def handle_incoming_msg_event(self, msg_obj):
if not msg_obj.msgtxt:
return
self.jid = msg_obj.jid
if msg_obj.session:
self.control = msg_obj.session.control
else:
self.control = None
self.get_focused()
# This event has already been added to event list
if not self.control and len(gajim.events.get_events(self.conn.name, \
self.jid, [msg_obj.mtype])) <= 1:
self.first_unread = True
if msg_obj.mtype == 'pm':
nick = msg_obj.resource
else:
nick = gajim.get_name_from_jid(self.conn.name, self.jid)
if self.first_unread:
self.sound_event = 'first_message_received'
elif self.control_focused:
self.sound_event = 'next_message_received_focused'
else:
self.sound_event = 'next_message_received_unfocused'
if gajim.config.get('notification_preview_message'):
self.popup_text = msg_obj.msgtxt
if self.popup_text and (self.popup_text.startswith('/me ') or \
self.popup_text.startswith('/me\n')):
self.popup_text = '* ' + nick + self.popup_text[3:]
else:
# We don't want message preview, do_preview = False
self.popup_text = ''
if msg_obj.mtype == 'normal': # single message
self.popup_event_type = _('New Single Message')
self.popup_image = 'gajim-single_msg_recv'
self.popup_title = _('New Single Message from %(nickname)s') % \
{'nickname': nick}
elif msg_obj.mtype == 'pm':
self.popup_event_type = _('New Private Message')
self.popup_image = 'gajim-priv_msg_recv'
self.popup_title = _('New Private Message from group chat %s') % \
msg_obj.jid
if self.popup_text:
self.popup_text = _('%(nickname)s: %(message)s') % \
{'nickname': nick, 'message': self.popup_text}
else:
self.popup_text = _('Messaged by %(nickname)s') % \
{'nickname': nick}
else: # chat message
self.popup_event_type = _('New Message')
self.popup_image = 'gajim-chat_msg_recv'
self.popup_title = _('New Message from %(nickname)s') % \
{'nickname': nick}
self.popup_image = gtkgui_helpers.get_icon_path(self.popup_image, 48)
if not gajim.config.get('notify_on_new_message') or \
not self.first_unread:
self.do_popup = False
elif gajim.config.get('autopopupaway'):
# always show notification
self.do_popup = True
elif gajim.connections[self.conn.name].connected in (2, 3):
# we're online or chat
self.do_popup = True
if self.first_unread and helpers.allow_sound_notification(
self.conn.name, 'first_message_received'):
self.do_sound = True
elif not self.first_unread and self.control_focused and \
helpers.allow_sound_notification(self.conn.name,
'next_message_received_focused'):
self.do_sound = True
elif not self.first_unread and not self.control_focused and \
helpers.allow_sound_notification(self.conn.name,
'next_message_received_unfocused'):
self.do_sound = True
def handle_incoming_gc_msg_event(self, msg_obj):
sound = msg_obj.msg_obj.gc_control.highlighting_for_message(
msg_obj.msgtxt, msg_obj.timestamp)[1]
self.do_sound = True
if sound == 'received':
self.sound_event = 'muc_message_received'
elif sound == 'highlight':
self.sound_event = 'muc_message_highlight'
else:
self.do_sound = False
self.do_popup = False
def handle_incoming_pres_event(self, msg_obj):
pass
def generate(self):
# what's needed to compute output
self.conn = self.base_event.conn
self.control = None
self.control_focused = False
self.first_unread = False
# For output
self.do_sound = False
self.sound_file = ''
self.sound_event = '' # gajim sound played if not sound_file is set
self.show_popup = False
self.do_popup = False
self.popup_title = ''
self.popup_text = ''
self.popup_event_type = ''
self.popup_msg_type = ''
self.popup_image = ''
self.do_command = False
self.command = ''
self.open_chat = False
self.activate_urgency_hint = False
self.show_in_notification_area = False
self.show_in_roster = False
self.detect_type()
if self.notif_type == 'msg':
self.handle_incoming_msg_event(self.base_event)
elif self.notif_type == 'gc-msg':
self.handle_incoming_gc_msg_event(self.base_event)
elif self.notif_type == 'pres':
self.handle_incoming_pres_event(self.base_event)
return True
......@@ -35,6 +35,7 @@
import xmpp
import defs
import common.ged
import notify
interface = None # The actual interface (the gtk one for the moment)
thread_interface = None # Interface to run a thread and then a callback
......@@ -105,6 +106,8 @@
events = Events()
notification = notify.Notification()
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
......
......@@ -27,12 +27,17 @@
import logging
log = logging.getLogger('gajim.common.ged')
PRECORE = 30
CORE = 40
POSTCORE = 50
PRECORE = 10
CORE = 20
POSTCORE = 30
PREGUI = 40
PREGUI1 = 50
GUI1 = 60
GUI2 = 70
POSTGUI = 80
POSTGUI1 = 70
PREGUI2 = 80
GUI2 = 90
POSTGUI2 = 100
POSTGUI = 110
class GlobalEventsDispatcher(object):
......@@ -68,4 +73,4 @@ def raise_event(self, event_name, *args, **kwargs):
if event_name in self.handlers:
for priority, handler in self.handlers[event_name]:
if handler(*args, **kwargs):
return
return True
......@@ -58,8 +58,8 @@ def unregister_outgoing_event(self, event_class):
def push_incoming_event(self, event_object):
if event_object.generate():
gajim.ged.raise_event(event_object.name, event_object)
self._generate_events_based_on_incoming_event(event_object)
if not gajim.ged.raise_event(event_object.name, event_object):
self._generate_events_based_on_incoming_event(event_object)
def push_outgoing_event(self, event_object):
pass
......@@ -78,8 +78,8 @@ def _generate_events_based_on_incoming_event(self, event_object):
for new_event_class in self.incoming_events_generators[base_event_name]:
new_event_object = new_event_class(None, base_event=event_object)
if new_event_object.generate():
gajim.ged.raise_event(new_event_object.name, new_event_object)
self._generate_events_based_on_incoming_event(new_event_object)
if not gajim.ged.raise_event(new_event_object.name, new_event_object):
self._generate_events_based_on_incoming_event(new_event_object)
class NetworkEvent(object):
name = ''
......
......@@ -1474,7 +1474,6 @@ def update_actions(self):
if self.search_button:
self.search_button.set_sensitive(False)
model, iter_ = self.window.services_treeview.get_selection().get_selected()
model, iter_ = self.window.services_treeview.get_selection().get_selected()
if not iter_:
return
if not model[iter_][0]:
......
......@@ -480,6 +480,8 @@ def __init__(self, parent_win, contact, acct, is_continued=False):
self._nec_gc_config_changed_received)
gajim.ged.register_event_handler('signed-in', ged.GUI1,
self._nec_signed_in)
gajim.ged.register_event_handler('decrypted-message-received', ged.GUI2,
self._nec_decrypted_message_received)
gajim.gc_connected[self.account][self.room_jid] = False
# disable win, we are not connected yet
ChatControlBase.got_disconnected(self)
......@@ -1108,10 +1110,6 @@ def print_conversation(self, text, contact='', tim=None, xhtml=None,
self.attention_list.pop(0) # remove older
self.attention_list.append(contact)
if sound == 'received':
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]))
......@@ -1271,6 +1269,23 @@ def _nec_signed_in(self, obj):
password = gajim.gc_passwords.get(self.room_jid, '')
obj.conn.join_gc(self.nick, self.room_jid, password)
def _nec_decrypted_message_received(self, obj):
if obj.conn.name != self.account:
return
if obj.gc_control == self and obj.resource:
# We got a pm from this room
nick = obj.resource
if obj.session.control:
# print if a control is open
obj.session.control.print_conversation(obj.msgtxt,
tim=obj.timestamp, xhtml=obj.xhtml, encrypted=obj.encrypted,
displaymarking=obj.displaymarking)
else:
# otherwise pass it off to the control to be queued
self.on_private_message(nick, obj.msgtxt, obj.timestamp,
obj.xhtml, self, msg_id=obj.msg_id, encrypted=obj.encrypted,
displaymarking=obj.displaymarking)
def got_connected(self):
# Make autorejoin stop.
if self.autorejoin:
......
......@@ -36,6 +36,7 @@
from common import gajim
from common import helpers
from common import ged
from common import dbus_support
if dbus_support.supported:
......@@ -390,6 +391,32 @@ def on_pynotify_notification_clicked(notification, action):
notification.close()
gajim.interface.handle_event(account, jid, msg_type)
class Notification:
"""
Handle notifications
"""
def __init__(self):
gajim.ged.register_event_handler('notification', ged.GUI2,
self._nec_notification)
def _nec_notification(self, obj):
if obj.do_popup:
popup(obj.popup_event_type, obj.jid, obj.conn.name,
obj.popup_msg_type, path_to_image=obj.popup_image,
title=obj.popup_title, text=obj.popup_text)
if obj.do_sound:
if obj.sound_file:
helpers.play_sound_file(obj.sound_file)
elif obj.sound_event:
helpers.play_sound(obj.sound_event)
if obj.do_command:
try:
helpers.exec_command(obj.command)
except Exception:
pass
class NotificationResponseManager:
"""
Collect references to pending DesktopNotifications and manages there
......
......@@ -2568,6 +2568,44 @@ def _nec_signed_in(self, obj):
self.set_actions_menu_needs_rebuild()
self.draw_account(obj.conn.name)
def _nec_decrypted_message_received(self, obj):
if not obj.msgtxt: # empty message text
return True
if obj.mtype not in ('norml', 'chat'):
return
if obj.session.control:
typ = ''
if obj.mtype == 'error':
typ = 'error'
obj.session.control.print_conversation(obj.msgtxt, typ,
tim=obj.timestamp, encrypted=obj.encrypted, subject=obj.subject,
xhtml=obj.xhtml, displaymarking=obj.displaymarking)
elif obj.popup:
if not obj.session.control:
contact = gajim.contacts.get_contact(obj.conn.name, obj.jid,
obj.resource_for_chat)
obj.session.control = gajim.interface.new_chat(contact,
obj.conn.name, resource=obj.resource_for_chat,
session=obj.session)
if len(gajim.events.get_events(obj.conn.name, obj.fjid)):
obj.session.control.read_queue()
if obj.show_in_roster:
self.draw_contact(obj.jid, obj.conn.name)
self.show_title() # we show the * or [n]
# Select the big brother contact in roster, it's visible because it
# has events.
family = gajim.contacts.get_metacontacts_family(obj.conn.name,
obj.jid)
if family:
nearby_family, bb_jid, bb_account = \
gajim.contacts.get_nearby_family_and_big_brother(family,
obj.conn.name)
else:
bb_jid, bb_account = obj.jid, obj.conn.name
self.select_contact(bb_jid, bb_account)
################################################################################
### Menu and GUI callbacks
### FIXME: order callbacks in itself...
......@@ -6404,3 +6442,5 @@ def _open_wizard():
self._nec_metacontacts_received)
gajim.ged.register_event_handler('signed-in', ged.GUI1,
self._nec_signed_in)
gajim.ged.register_event_handler('decrypted-message-received', ged.GUI2,
self._nec_decrypted_message_received)
......@@ -97,6 +97,8 @@ def _nec_decrypted_message_received(self, obj):
common.logger.LOG_DB_PATH
self.conn.dispatch('ERROR', (pritext, sectext))
obj.msg_id = msg_id
treat_as = gajim.config.get('treat_incoming_messages')
if treat_as:
obj.mtype = treat_as
......@@ -106,14 +108,6 @@ def _nec_decrypted_message_received(self, obj):
pm = True
obj.mtype = 'pm'
highest_contact = gajim.contacts.get_contact_with_highest_priority(
self.conn.name, obj.jid)
# does this resource have the highest priority of any available?
is_highest = not highest_contact or not highest_contact.resource or \
obj.resource == highest_contact.resource or highest_contact.show ==\
'offline'
# Handle chat states
contact = gajim.contacts.get_contact(self.conn.name, obj.jid,
obj.resource)
......@@ -142,21 +136,20 @@ def _nec_decrypted_message_received(self, obj):
# THIS MUST BE AFTER chatstates handling
# AND BEFORE playsound (else we ear sounding on chatstates!)
if not obj.msgtxt: # empty message text
return
return True
if gajim.config.get_per('accounts', self.conn.name,
'ignore_unknown_contacts') and not gajim.contacts.get_contacts(
self.conn.name, obj.jid) and not pm:
return
return True
if not contact:
# contact is not in the roster, create a fake one to display
# notification
contact = gajim.contacts.create_not_in_roster_contact(jid=obj.jid,
account=self.conn.name, resource=obj.resource)
highest_contact = gajim.contacts.get_contact_with_highest_priority(
self.conn.name, obj.jid)
advanced_notif_num = notify.get_advanced_notification(
'message_received', self.conn.name, contact)
# does this resource have the highest priority of any available?
is_highest = not highest_contact or not highest_contact.resource or \
obj.resource == highest_contact.resource or highest_contact.show ==\
'offline'
if not pm and is_highest:
jid_of_control = obj.jid
......@@ -170,47 +163,8 @@ def _nec_decrypted_message_received(self, obj):
self.control = ctrl
self.control.set_session(self)
# Is it a first or next message received ?
first = False
if not self.control and not gajim.events.get_events(self.conn.name, \
jid_of_control, [obj.mtype]):
first = True
if pm:
nickname = obj.resource
if self.control:
# print if a control is open
self.control.print_conversation(obj.msgtxt, tim=obj.timestamp,
xhtml=obj.xhtml, encrypted=obj.encrypted,
displaymarking=obj.displaymarking)
else:
# otherwise pass it off to the control to be queued
obj.gc_control.on_private_message(nickname, obj.msgtxt,
obj.timestamp, obj.xhtml, self, msg_id=msg_id,
encrypted=obj.encrypted, displaymarking=obj.displaymarking)
else:
self.roster_message(obj.jid, obj.msgtxt, obj.timestamp,
obj.encrypted, obj.mtype,
obj.subject, obj.resource, msg_id, obj.user_nick,
advanced_notif_num, xhtml=obj.xhtml, form_node=obj.form_node,
displaymarking=obj.displaymarking)
nickname = gajim.get_name_from_jid(self.conn.name, obj.jid)
# Check and do wanted notifications
msg = obj.msgtxt
if obj.subject:
msg = _('Subject: %s') % obj.subject + '\n' + msg
focused = False
if self.control:
parent_win = self.control.parent_win
if parent_win and self.control == parent_win.get_active_control() \
and parent_win.window.has_focus:
focused = True
notify.notify('new_message', jid_of_control, self.conn.name, [obj.mtype,
first, nickname, msg, focused], advanced_notif_num)
if not pm:
self.roster_message2(obj)
if gajim.interface.remote_ctrl:
gajim.interface.remote_ctrl.raise_signal('NewMessage', (
......@@ -218,14 +172,94 @@ def _nec_decrypted_message_received(self, obj):
obj.encrypted, obj.mtype, obj.subject, obj.chatstate, msg_id,
obj.composing_xep, obj.user_nick, obj.xhtml, obj.form_node]))
gajim.ged.raise_event('NewMessage',
(self.conn.name, [obj.fjid, obj.msgtxt, obj.timestamp,
obj.encrypted, obj.mtype, obj.subject, obj.chatstate, msg_id,
obj.composing_xep, obj.user_nick, obj.xhtml, obj.form_node]))
def roster_message2(self, obj):
"""
Display the message or show notification in the roster
"""
contact = None
jid = obj.jid
resource = obj.resource
# if chat window will be for specific resource
resource_for_chat = resource
fjid = jid
# Try to catch the contact with correct resource
if resource:
fjid = jid + '/' + resource
contact = gajim.contacts.get_contact(obj.conn.name, jid, resource)
highest_contact = gajim.contacts.get_contact_with_highest_priority(
obj.conn.name, jid)
if not contact:
# If there is another resource, it may be a message from an
# invisible resource
lcontact = gajim.contacts.get_contacts(obj.conn.name, jid)
if (len(lcontact) > 1 or (lcontact and lcontact[0].resource and \
lcontact[0].show != 'offline')) and jid.find('@') > 0:
contact = gajim.contacts.copy_contact(highest_contact)
contact.resource = resource
contact.priority = 0
contact.show = 'offline'
contact.status = ''
gajim.contacts.add_contact(obj.conn.name, contact)
else:
# Default to highest prio
fjid = jid
resource_for_chat = None
contact = highest_contact
if not contact:
# contact is not in roster
contact = gajim.interface.roster.add_to_not_in_the_roster(
obj.conn.name, jid, obj.user_nick)
if not self.control:
ctrl = gajim.interface.msg_win_mgr.get_control(fjid, self.conn.name)
if ctrl:
self.control = ctrl
self.control.set_session(self)
else:
# if no control exists and message comes from highest prio,
# the new control shouldn't have a resource
if highest_contact and contact.resource == \
highest_contact.resource and jid != gajim.get_jid_from_account(
self.conn.name):
fjid = jid
resource_for_chat = None
obj.popup = helpers.allow_popup_window(self.conn.name)
obj.resource_for_chat = resource_for_chat
type_ = 'chat'
event_type = 'message_received'
if obj.mtype == 'normal':
type_ = 'normal'
event_type = 'single_message_received'
if self.control and obj.mtype != 'normal':
obj.show_in_roster = False
obj.show_in_systray = False
else:
obj.show_in_roster = notify.get_show_in_roster(event_type,
self.conn.name, contact, self)
obj.show_in_systray = notify.get_show_in_systray(event_type,
self.conn.name, contact)
if not self.control: