# Copyright (C) 2006 Jean-Marie Traissard # Nikos Kouremenos # Copyright (C) 2006-2014 Yann Leboulanger # Copyright (C) 2007 Julien Pivotto # Copyright (C) 2007-2008 Stephan Erb # Copyright (C) 2008 Brendan Taylor # Jonathan Schleifer # # This file is part of Gajim. # # Gajim 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 3 only. # # Gajim 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. # # You should have received a copy of the GNU General Public License # along with Gajim. If not, see . import time from gajim.common import app class Event: """ Information concerning each event """ def __init__(self, time_=None, 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_marked_gc_msg, printed_pm, gc-invitation, subscription_request, unsubscribedm jingle-incoming parameters is (per type_): chat, normal, pm: [message, subject, kind, time, encrypted, resource, msg_log_id] where kind in error, incoming file-*: file_props gc_msg: None printed_chat: [message, subject, control, msg_log_id] printed_*: None messages that are already printed in chat, but not read gc-invitation: [room_jid, reason, password, jid_from] subscription_request: [text, nick] unsubscribed: contact jingle-incoming: (fulljid, sessionid, content_types) """ if time_: self.time_ = time_ else: self.time_ = time.time() self.show_in_roster = show_in_roster self.show_in_systray = show_in_systray # Set when adding the event self.jid = None self.account = None class ChatEvent(Event): type_ = 'chat' def __init__(self, message, subject, kind, time_, resource, msg_log_id, correct_id=None, message_id=None, session=None, displaymarking=None, sent_forwarded=False, show_in_roster=False, show_in_systray=True, additional_data=None): Event.__init__(self, time_, show_in_roster=show_in_roster, show_in_systray=show_in_systray) self.message = message self.subject = subject self.kind = kind self.time = time_ self.resource = resource self.msg_log_id = msg_log_id self.message_id = message_id self.correct_id = correct_id self.session = session self.displaymarking = displaymarking self.sent_forwarded = sent_forwarded if additional_data is None: from gajim.common.helpers import AdditionalDataDict additional_data = AdditionalDataDict() self.additional_data = additional_data class PmEvent(ChatEvent): type_ = 'pm' class PrintedChatEvent(Event): type_ = 'printed_chat' def __init__(self, message, subject, control, msg_log_id, time_=None, message_id=None, stanza_id=None, show_in_roster=False, show_in_systray=True): Event.__init__(self, time_, show_in_roster=show_in_roster, show_in_systray=show_in_systray) self.message = message self.subject = subject self.control = control self.msg_log_id = msg_log_id self.message_id = message_id self.stanza_id = stanza_id class PrintedGcMsgEvent(PrintedChatEvent): type_ = 'printed_gc_msg' class PrintedMarkedGcMsgEvent(PrintedChatEvent): type_ = 'printed_marked_gc_msg' class PrintedPmEvent(PrintedChatEvent): type_ = 'printed_pm' class SubscriptionRequestEvent(Event): type_ = 'subscription_request' def __init__(self, text, nick, time_=None, show_in_roster=False, show_in_systray=True): Event.__init__(self, time_, show_in_roster=show_in_roster, show_in_systray=show_in_systray) self.text = text self.nick = nick class UnsubscribedEvent(Event): type_ = 'unsubscribed' def __init__(self, contact, time_=None, show_in_roster=False, show_in_systray=True): Event.__init__(self, time_, show_in_roster=show_in_roster, show_in_systray=show_in_systray) self.contact = contact class GcInvitationtEvent(Event): type_ = 'gc-invitation' def __init__(self, event): Event.__init__(self, None, show_in_roster=False, show_in_systray=True) for key, value in vars(event).items(): setattr(self, key, value) def get_inviter_name(self): if self.from_.bare_match(self.muc): return self.from_.resource contact = app.contacts.get_first_contact_from_jid( self.account, self.from_.bare) if contact is None: return str(self.from_) return contact.get_shown_name() class FileRequestEvent(Event): type_ = 'file-request' def __init__(self, file_props, time_=None, show_in_roster=False, show_in_systray=True): Event.__init__(self, time_, show_in_roster=show_in_roster, show_in_systray=show_in_systray) self.file_props = file_props class FileSendErrorEvent(FileRequestEvent): type_ = 'file-send-error' class FileErrorEvent(FileRequestEvent): type_ = 'file-error' class FileRequestErrorEvent(FileRequestEvent): type_ = 'file-request-error' class FileCompletedEvent(FileRequestEvent): type_ = 'file-completed' class FileStoppedEvent(FileRequestEvent): type_ = 'file-stopped' class FileHashErrorEvent(FileRequestEvent): type_ = 'file-hash-error' class JingleIncomingEvent(Event): type_ = 'jingle-incoming' def __init__(self, peerjid, sid, content_types, time_=None, show_in_roster=False, show_in_systray=True): Event.__init__(self, time_, show_in_roster=show_in_roster, show_in_systray=show_in_systray) self.peerjid = peerjid self.sid = sid self.content_types = content_types class Events: """ Information concerning all events """ def __init__(self): self._events = {} # list of events {acct: {jid1: [E1, E2]}, } self._event_added_listeners = [] self._event_removed_listeners = [] def event_added_subscribe(self, listener): """ Add a listener when an event is added to the queue """ if not listener in self._event_added_listeners: self._event_added_listeners.append(listener) def event_added_unsubscribe(self, listener): """ Remove a listener when an event is added to the queue """ if listener in self._event_added_listeners: self._event_added_listeners.remove(listener) def event_removed_subscribe(self, listener): """ Add a listener when an event is removed from the queue """ if not listener in self._event_removed_listeners: self._event_removed_listeners.append(listener) def event_removed_unsubscribe(self, listener): """ Remove a listener when an event is removed from the queue """ if listener in self._event_removed_listeners: self._event_removed_listeners.remove(listener) def fire_event_added(self, event): for listener in self._event_added_listeners: listener(event) def fire_event_removed(self, event_list): for listener in self._event_removed_listeners: listener(event_list) 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 add_event(self, account, jid, event): # No such account before ? if account not in self._events: self._events[account] = {jid: [event]} # no such jid before ? elif jid not in self._events[account]: self._events[account][jid] = [event] else: self._events[account][jid].append(event) event.jid = jid event.account = account self.fire_event_added(event) def remove_events(self, account, jid, event=None, types=None): """ If event is not specified, remove all events from this jid, optionally only from given type return True if no such event found """ if types is None: types = [] if account not in self._events: return True if jid not in self._events[account]: 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) self.fire_event_removed([event]) return return True if types: new_list = [] # list of events to keep removed_list = [] # list of removed events for ev in self._events[account][jid]: if ev.type_ not in types: new_list.append(ev) else: removed_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] self.fire_event_removed(removed_list) return # No event nor type given, remove them all removed_list = self._events[account][jid] del self._events[account][jid] self.fire_event_removed(removed_list) def change_jid(self, account, old_jid, new_jid): if account not in self._events: return if old_jid not in self._events[account]: return if new_jid in self._events[account]: self._events[account][new_jid] += self._events[account][old_jid] else: self._events[account][new_jid] = self._events[account][old_jid] del self._events[account][old_jid] def get_nb_events(self, types=None, account=None): if types is None: types = [] return self._get_nb_events(types=types, account=account) def get_events(self, account, jid=None, types=None): """ Return all events from the given account of the form {jid1: [], jid2: []}. If jid is given, returns all events from the given jid in a list: [] optionally only from given type """ if types is None: types = [] if account not in self._events: return [] if not jid: events_list = {} # list of events for jid_ in self._events[account]: events = [] for ev in self._events[account][jid_]: if not types or ev.type_ in types: events.append(ev) if events: events_list[jid_] = events return events_list if jid not in self._events[account]: 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_all_events(self, types=None): accounts = self._events.keys() events = [] for account in accounts: for jid in self._events[account]: for event in self._events[account][jid]: if types is None or event.type_ in types: events.append(event) return events def get_first_event(self, account=None, jid=None, type_=None): """ Return the first event of type type_ if given """ if not account: return self._get_first_event_with_attribute(self._events) 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=None): """ Return the number of pending events """ if types is None: types = [] nb = 0 if account: accounts = [account] else: accounts = self._events.keys() for acct in accounts: if acct not in self._events: continue if jid: jids = [jid] else: jids = self._events[acct].keys() for j in jids: if j not in self._events[acct]: 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=None): """ Return the number of events displayed in roster """ if types is None: types = [] 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=None): """ Return the number of events displayed in roster """ if types is None: types = [] 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')