Refactor Notifications
- Use icon names instead of path - Move PopupNotificationWindow into notify.py - Make popup class method instead of module method - Dont use sessions to get control on notification action Fixes #9140 - Add has_focus() method to ChatControlBase
This commit is contained in:
		
							parent
							
								
									2abbb1e224
								
							
						
					
					
						commit
						4bed8ace95
					
				
					 6 changed files with 349 additions and 409 deletions
				
			
		| 
						 | 
				
			
			@ -1268,6 +1268,13 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
 | 
			
		|||
            app.log('autoscroll').info('Autoscroll disabled')
 | 
			
		||||
            self.conv_textview.autoscroll = False
 | 
			
		||||
 | 
			
		||||
    def has_focus(self):
 | 
			
		||||
        if self.parent_win:
 | 
			
		||||
            if self.parent_win.window.get_property('has-toplevel-focus'):
 | 
			
		||||
                if self == self.parent_win.get_active_control():
 | 
			
		||||
                    return True
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    def _on_scroll(self, widget, event):
 | 
			
		||||
        if not self.conv_textview.autoscroll:
 | 
			
		||||
            # autoscroll is already disabled
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,7 +27,6 @@ import hashlib
 | 
			
		|||
import hmac
 | 
			
		||||
import logging
 | 
			
		||||
import sys
 | 
			
		||||
import os
 | 
			
		||||
from time import time as time_time
 | 
			
		||||
 | 
			
		||||
import OpenSSL.crypto
 | 
			
		||||
| 
						 | 
				
			
			@ -40,7 +39,6 @@ from gajim.common import helpers
 | 
			
		|||
from gajim.common import app
 | 
			
		||||
from gajim.common import i18n
 | 
			
		||||
from gajim.common import dataforms
 | 
			
		||||
from gajim.common import configpaths
 | 
			
		||||
from gajim.common.zeroconf.zeroconf import Constant
 | 
			
		||||
from gajim.common.const import KindConstant, SSLError
 | 
			
		||||
from gajim.common.pep import SUPPORTED_PERSONAL_USER_EVENTS
 | 
			
		||||
| 
						 | 
				
			
			@ -2583,8 +2581,50 @@ class GatewayPromptReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
 | 
			
		|||
 | 
			
		||||
class NotificationEvent(nec.NetworkIncomingEvent):
 | 
			
		||||
    name = 'notification'
 | 
			
		||||
    base_network_events = ['decrypted-message-received', 'gc-message-received',
 | 
			
		||||
        'presence-received']
 | 
			
		||||
    base_network_events = ['decrypted-message-received',
 | 
			
		||||
                           'gc-message-received',
 | 
			
		||||
                           'presence-received']
 | 
			
		||||
 | 
			
		||||
    def generate(self):
 | 
			
		||||
        # what's needed to compute output
 | 
			
		||||
        self.account = self.base_event.conn.name
 | 
			
		||||
        self.conn = self.base_event.conn
 | 
			
		||||
        self.jid = ''
 | 
			
		||||
        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.icon_name = None
 | 
			
		||||
        self.transport_name = None
 | 
			
		||||
        self.show = None
 | 
			
		||||
        self.popup_timeout = -1
 | 
			
		||||
 | 
			
		||||
        self.do_command = False
 | 
			
		||||
        self.command = ''
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
 | 
			
		||||
    def detect_type(self):
 | 
			
		||||
        if self.base_event.name == 'decrypted-message-received':
 | 
			
		||||
| 
						 | 
				
			
			@ -2594,14 +2634,6 @@ class NotificationEvent(nec.NetworkIncomingEvent):
 | 
			
		|||
        if self.base_event.name == 'presence-received':
 | 
			
		||||
            self.notif_type = 'pres'
 | 
			
		||||
 | 
			
		||||
    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.get_property('has-toplevel-focus'):
 | 
			
		||||
                self.control_focused = True
 | 
			
		||||
 | 
			
		||||
    def handle_incoming_msg_event(self, msg_obj):
 | 
			
		||||
        # don't alert for carbon copied messages from ourselves
 | 
			
		||||
        if msg_obj.sent:
 | 
			
		||||
| 
						 | 
				
			
			@ -2609,15 +2641,18 @@ class NotificationEvent(nec.NetworkIncomingEvent):
 | 
			
		|||
        if not msg_obj.msgtxt:
 | 
			
		||||
            return
 | 
			
		||||
        self.jid = msg_obj.jid
 | 
			
		||||
        if msg_obj.session:
 | 
			
		||||
            self.control = msg_obj.session.control
 | 
			
		||||
        if msg_obj.mtype == 'pm':
 | 
			
		||||
            self.jid = msg_obj.fjid
 | 
			
		||||
 | 
			
		||||
        self.control = app.interface.msg_win_mgr.search_control(
 | 
			
		||||
            msg_obj.jid, self.account, msg_obj.resource)
 | 
			
		||||
 | 
			
		||||
        if self.control is None:
 | 
			
		||||
            if len(app.events.get_events(
 | 
			
		||||
                    self.account, msg_obj.jid, [msg_obj.mtype])) <= 1:
 | 
			
		||||
                self.first_unread = True
 | 
			
		||||
        else:
 | 
			
		||||
            self.control = None
 | 
			
		||||
        self.get_focused()
 | 
			
		||||
        # This event has already been added to event list
 | 
			
		||||
        if not self.control and len(app.events.get_events(self.conn.name, \
 | 
			
		||||
        self.jid, [msg_obj.mtype])) <= 1:
 | 
			
		||||
            self.first_unread = True
 | 
			
		||||
            self.control_focused = self.control.has_focus()
 | 
			
		||||
 | 
			
		||||
        if msg_obj.mtype == 'pm':
 | 
			
		||||
            nick = msg_obj.resource
 | 
			
		||||
| 
						 | 
				
			
			@ -2642,13 +2677,11 @@ class NotificationEvent(nec.NetworkIncomingEvent):
 | 
			
		|||
        if msg_obj.mtype == 'normal': # single message
 | 
			
		||||
            self.popup_msg_type = 'normal'
 | 
			
		||||
            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_msg_type = '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:
 | 
			
		||||
| 
						 | 
				
			
			@ -2660,11 +2693,9 @@ class NotificationEvent(nec.NetworkIncomingEvent):
 | 
			
		|||
        else: # chat message
 | 
			
		||||
            self.popup_msg_type = 'chat'
 | 
			
		||||
            self.popup_event_type = _('New Message')
 | 
			
		||||
            self.popup_image = 'gajim-chat_msg_recv'
 | 
			
		||||
            self.popup_title = _('New Message from %(nickname)s') % \
 | 
			
		||||
                {'nickname': nick}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        if app.config.get('notify_on_new_message'):
 | 
			
		||||
            if self.first_unread or (app.config.get('autopopup_chat_opened') \
 | 
			
		||||
            and not self.control_focused):
 | 
			
		||||
| 
						 | 
				
			
			@ -2720,29 +2751,6 @@ class NotificationEvent(nec.NetworkIncomingEvent):
 | 
			
		|||
 | 
			
		||||
        self.do_popup = False
 | 
			
		||||
 | 
			
		||||
    def get_path_to_generic_or_avatar(self, generic, jid=None, suffix=None):
 | 
			
		||||
        """
 | 
			
		||||
        Choose between avatar image and default image
 | 
			
		||||
 | 
			
		||||
        Returns full path to the avatar image if it exists, otherwise returns full
 | 
			
		||||
        path to the image.  generic must be with extension and suffix without
 | 
			
		||||
        """
 | 
			
		||||
        if jid:
 | 
			
		||||
            # we want an avatar
 | 
			
		||||
            puny_jid = helpers.sanitize_filename(jid)
 | 
			
		||||
            path_to_file = os.path.join(
 | 
			
		||||
                configpaths.get('AVATAR'), puny_jid) + suffix
 | 
			
		||||
            path_to_local_file = path_to_file + '_local'
 | 
			
		||||
            for extension in ('.png', '.jpeg'):
 | 
			
		||||
                path_to_local_file_full = path_to_local_file + extension
 | 
			
		||||
                if os.path.exists(path_to_local_file_full):
 | 
			
		||||
                    return path_to_local_file_full
 | 
			
		||||
            for extension in ('.png', '.jpeg'):
 | 
			
		||||
                path_to_file_full = path_to_file + extension
 | 
			
		||||
                if os.path.exists(path_to_file_full):
 | 
			
		||||
                    return path_to_file_full
 | 
			
		||||
        return os.path.abspath(generic)
 | 
			
		||||
 | 
			
		||||
    def handle_incoming_pres_event(self, pres_obj):
 | 
			
		||||
        if app.jid_is_transport(pres_obj.jid):
 | 
			
		||||
            return True
 | 
			
		||||
| 
						 | 
				
			
			@ -2757,7 +2765,6 @@ class NotificationEvent(nec.NetworkIncomingEvent):
 | 
			
		|||
            if c.show not in ('offline', 'error'):
 | 
			
		||||
                return True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        # no other resource is connected, let's look in metacontacts
 | 
			
		||||
        family = app.contacts.get_metacontacts_family(account, self.jid)
 | 
			
		||||
        for info in family:
 | 
			
		||||
| 
						 | 
				
			
			@ -2773,8 +2780,6 @@ class NotificationEvent(nec.NetworkIncomingEvent):
 | 
			
		|||
 | 
			
		||||
        if pres_obj.old_show < 2 and pres_obj.new_show > 1:
 | 
			
		||||
            event = 'contact_connected'
 | 
			
		||||
            show_image = 'online.png'
 | 
			
		||||
            suffix = '_notif_size_colored'
 | 
			
		||||
            server = app.get_server_from_jid(self.jid)
 | 
			
		||||
            account_server = account + '/' + server
 | 
			
		||||
            block_transport = False
 | 
			
		||||
| 
						 | 
				
			
			@ -2794,8 +2799,6 @@ class NotificationEvent(nec.NetworkIncomingEvent):
 | 
			
		|||
 | 
			
		||||
        elif pres_obj.old_show > 1 and pres_obj.new_show < 2:
 | 
			
		||||
            event = 'contact_disconnected'
 | 
			
		||||
            show_image = 'offline.png'
 | 
			
		||||
            suffix = '_notif_size_bw'
 | 
			
		||||
            if helpers.allow_showing_notification(account, 'notify_on_signout'):
 | 
			
		||||
                self.do_popup = True
 | 
			
		||||
            if app.config.get_per('soundevents', 'contact_disconnected',
 | 
			
		||||
| 
						 | 
				
			
			@ -2805,24 +2808,13 @@ class NotificationEvent(nec.NetworkIncomingEvent):
 | 
			
		|||
        # Status change (not connected/disconnected or error (<1))
 | 
			
		||||
        elif pres_obj.new_show > 1:
 | 
			
		||||
            event = 'status_change'
 | 
			
		||||
            # FIXME: we don't always 'online.png', but we first need 48x48 for
 | 
			
		||||
            # all status
 | 
			
		||||
            show_image = 'online.png'
 | 
			
		||||
            suffix = '_notif_size_colored'
 | 
			
		||||
        else:
 | 
			
		||||
            return True
 | 
			
		||||
 | 
			
		||||
        transport_name = app.get_transport_name_from_jid(self.jid)
 | 
			
		||||
        img_path = None
 | 
			
		||||
        if transport_name:
 | 
			
		||||
            img_path = os.path.join(helpers.get_transport_path(
 | 
			
		||||
                transport_name), '48x48', show_image)
 | 
			
		||||
        if not img_path or not os.path.isfile(img_path):
 | 
			
		||||
            iconset = app.config.get('iconset')
 | 
			
		||||
            img_path = os.path.join(helpers.get_iconset_path(iconset),
 | 
			
		||||
                '48x48', show_image)
 | 
			
		||||
        self.popup_image_path = self.get_path_to_generic_or_avatar(img_path,
 | 
			
		||||
            jid=self.jid, suffix=suffix)
 | 
			
		||||
        if app.jid_is_transport(self.jid):
 | 
			
		||||
            self.transport_name = app.get_transport_name_from_jid(self.jid)
 | 
			
		||||
 | 
			
		||||
        self.show = pres_obj.show
 | 
			
		||||
 | 
			
		||||
        self.popup_timeout = app.config.get('notification_timeout')
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2848,45 +2840,6 @@ class NotificationEvent(nec.NetworkIncomingEvent):
 | 
			
		|||
                self.popup_text = pres_obj.status
 | 
			
		||||
            self.popup_event_type = _('Contact Signed Out')
 | 
			
		||||
 | 
			
		||||
    def generate(self):
 | 
			
		||||
        # what's needed to compute output
 | 
			
		||||
        self.conn = self.base_event.conn
 | 
			
		||||
        self.jid = ''
 | 
			
		||||
        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.popup_image_path = ''
 | 
			
		||||
        self.popup_timeout = -1
 | 
			
		||||
 | 
			
		||||
        self.do_command = False
 | 
			
		||||
        self.command = ''
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
 | 
			
		||||
class MessageOutgoingEvent(nec.NetworkOutgoingEvent):
 | 
			
		||||
    name = 'message-outgoing'
 | 
			
		||||
    base_network_events = []
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										125
									
								
								gajim/dialogs.py
									
										
									
									
									
								
							
							
						
						
									
										125
									
								
								gajim/dialogs.py
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -3060,131 +3060,6 @@ class ChangePasswordDialog:
 | 
			
		|||
        dialog.destroy()
 | 
			
		||||
        self.on_response(password1)
 | 
			
		||||
 | 
			
		||||
class PopupNotificationWindow:
 | 
			
		||||
    def __init__(self, event_type, jid, account, msg_type='',
 | 
			
		||||
    path_to_image=None, title=None, text=None, timeout=-1):
 | 
			
		||||
        self.account = account
 | 
			
		||||
        self.jid = jid
 | 
			
		||||
        self.msg_type = msg_type
 | 
			
		||||
        self.index = len(app.interface.roster.popup_notification_windows)
 | 
			
		||||
        xml = gtkgui_helpers.get_gtk_builder('popup_notification_window.ui')
 | 
			
		||||
        self.window = xml.get_object('popup_notification_window')
 | 
			
		||||
        self.window.set_type_hint(Gdk.WindowTypeHint.TOOLTIP)
 | 
			
		||||
        self.window.set_name('NotificationPopup')
 | 
			
		||||
        close_button = xml.get_object('close_button')
 | 
			
		||||
        event_type_label = xml.get_object('event_type_label')
 | 
			
		||||
        event_description_label = xml.get_object('event_description_label')
 | 
			
		||||
        eventbox = xml.get_object('eventbox')
 | 
			
		||||
        image = xml.get_object('notification_image')
 | 
			
		||||
 | 
			
		||||
        if not text:
 | 
			
		||||
            text = app.get_name_from_jid(account, jid) # default value of text
 | 
			
		||||
        if not title:
 | 
			
		||||
            title = ''
 | 
			
		||||
 | 
			
		||||
        event_type_label.set_markup(
 | 
			
		||||
            '<span foreground="black" weight="bold">%s</span>' %
 | 
			
		||||
            GLib.markup_escape_text(title))
 | 
			
		||||
 | 
			
		||||
        css = '#NotificationPopup {background-color: black }'
 | 
			
		||||
        gtkgui_helpers.add_css_to_widget(self.window, css)
 | 
			
		||||
 | 
			
		||||
        # default image
 | 
			
		||||
        if not path_to_image:
 | 
			
		||||
            path_to_image = gtkgui_helpers.get_icon_path('gajim-chat_msg_recv', 48)
 | 
			
		||||
 | 
			
		||||
        if event_type == _('Contact Signed In'):
 | 
			
		||||
            bg_color = app.config.get('notif_signin_color')
 | 
			
		||||
        elif event_type == _('Contact Signed Out'):
 | 
			
		||||
            bg_color = app.config.get('notif_signout_color')
 | 
			
		||||
        elif event_type in (_('New Message'), _('New Single Message'),
 | 
			
		||||
            _('New Private Message'), _('New E-mail')):
 | 
			
		||||
            bg_color = app.config.get('notif_message_color')
 | 
			
		||||
        elif event_type == _('File Transfer Request'):
 | 
			
		||||
            bg_color = app.config.get('notif_ftrequest_color')
 | 
			
		||||
        elif event_type == _('File Transfer Error'):
 | 
			
		||||
            bg_color = app.config.get('notif_fterror_color')
 | 
			
		||||
        elif event_type in (_('File Transfer Completed'),
 | 
			
		||||
        _('File Transfer Stopped')):
 | 
			
		||||
            bg_color = app.config.get('notif_ftcomplete_color')
 | 
			
		||||
        elif event_type == _('Groupchat Invitation'):
 | 
			
		||||
            bg_color = app.config.get('notif_invite_color')
 | 
			
		||||
        elif event_type == _('Contact Changed Status'):
 | 
			
		||||
            bg_color = app.config.get('notif_status_color')
 | 
			
		||||
        else: # Unknown event! Shouldn't happen but deal with it
 | 
			
		||||
            bg_color = app.config.get('notif_other_color')
 | 
			
		||||
 | 
			
		||||
        background_class = '''
 | 
			
		||||
            .popup-style {
 | 
			
		||||
                border-image: none;
 | 
			
		||||
                background-image: none;
 | 
			
		||||
                background-color: %s }''' % bg_color
 | 
			
		||||
 | 
			
		||||
        gtkgui_helpers.add_css_to_widget(eventbox, background_class)
 | 
			
		||||
        eventbox.get_style_context().add_class('popup-style')
 | 
			
		||||
 | 
			
		||||
        gtkgui_helpers.add_css_to_widget(close_button, background_class)
 | 
			
		||||
        eventbox.get_style_context().add_class('popup-style')
 | 
			
		||||
 | 
			
		||||
        event_description_label.set_markup('<span foreground="black">%s</span>' %
 | 
			
		||||
            GLib.markup_escape_text(text))
 | 
			
		||||
 | 
			
		||||
        # set the image
 | 
			
		||||
        image.set_from_file(path_to_image)
 | 
			
		||||
 | 
			
		||||
        # position the window to bottom-right of screen
 | 
			
		||||
        window_width, self.window_height = self.window.get_size()
 | 
			
		||||
        app.interface.roster.popups_notification_height += self.window_height
 | 
			
		||||
        pos_x = app.config.get('notification_position_x')
 | 
			
		||||
        screen_w, screen_h = gtkgui_helpers.get_total_screen_geometry()
 | 
			
		||||
        if pos_x < 0:
 | 
			
		||||
            pos_x = screen_w - window_width + pos_x + 1
 | 
			
		||||
        pos_y = app.config.get('notification_position_y')
 | 
			
		||||
        if pos_y < 0:
 | 
			
		||||
            pos_y = screen_h - \
 | 
			
		||||
                app.interface.roster.popups_notification_height + pos_y + 1
 | 
			
		||||
        self.window.move(pos_x, pos_y)
 | 
			
		||||
 | 
			
		||||
        xml.connect_signals(self)
 | 
			
		||||
        self.window.show_all()
 | 
			
		||||
        if timeout > 0:
 | 
			
		||||
            GLib.timeout_add_seconds(timeout, self.on_timeout)
 | 
			
		||||
 | 
			
		||||
    def on_close_button_clicked(self, widget):
 | 
			
		||||
        self.adjust_height_and_move_popup_notification_windows()
 | 
			
		||||
 | 
			
		||||
    def on_timeout(self):
 | 
			
		||||
        self.adjust_height_and_move_popup_notification_windows()
 | 
			
		||||
 | 
			
		||||
    def adjust_height_and_move_popup_notification_windows(self):
 | 
			
		||||
        #remove
 | 
			
		||||
        app.interface.roster.popups_notification_height -= self.window_height
 | 
			
		||||
        self.window.destroy()
 | 
			
		||||
 | 
			
		||||
        if len(app.interface.roster.popup_notification_windows) > self.index:
 | 
			
		||||
            # we want to remove the destroyed window from the list
 | 
			
		||||
            app.interface.roster.popup_notification_windows.pop(self.index)
 | 
			
		||||
 | 
			
		||||
        # move the rest of popup windows
 | 
			
		||||
        app.interface.roster.popups_notification_height = 0
 | 
			
		||||
        current_index = 0
 | 
			
		||||
        for window_instance in app.interface.roster.popup_notification_windows:
 | 
			
		||||
            window_instance.index = current_index
 | 
			
		||||
            current_index += 1
 | 
			
		||||
            window_width, window_height = window_instance.window.get_size()
 | 
			
		||||
            app.interface.roster.popups_notification_height += window_height
 | 
			
		||||
            screen_w, screen_h = gtkgui_helpers.get_total_screen_geometry()
 | 
			
		||||
            window_instance.window.move(screen_w - window_width,
 | 
			
		||||
                screen_h - \
 | 
			
		||||
                app.interface.roster.popups_notification_height)
 | 
			
		||||
 | 
			
		||||
    def on_popup_notification_window_button_press_event(self, widget, event):
 | 
			
		||||
        if event.button != 1:
 | 
			
		||||
            self.window.destroy()
 | 
			
		||||
            return
 | 
			
		||||
        app.interface.handle_event(self.account, self.jid, self.msg_type)
 | 
			
		||||
        self.adjust_height_and_move_popup_notification_windows()
 | 
			
		||||
 | 
			
		||||
class SingleMessageWindow:
 | 
			
		||||
    """
 | 
			
		||||
    SingleMessageWindow can send or show a received singled message depending on
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -920,3 +920,11 @@ def pango_to_css_weight(number):
 | 
			
		|||
    if number > 900:
 | 
			
		||||
        return 900
 | 
			
		||||
    return int(math.ceil(number / 100.0)) * 100
 | 
			
		||||
 | 
			
		||||
def get_monitor_scale_factor():
 | 
			
		||||
    display = Gdk.Display.get_default()
 | 
			
		||||
    monitor = display.get_primary_monitor()
 | 
			
		||||
    if monitor is None:
 | 
			
		||||
        log.warning('Could not determine scale factor')
 | 
			
		||||
        return 1
 | 
			
		||||
    return monitor.get_scale_factor()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -226,10 +226,10 @@ class Interface:
 | 
			
		|||
    @staticmethod
 | 
			
		||||
    def handle_event_connection_lost(obj):
 | 
			
		||||
        # ('CONNECTION_LOST', account, [title, text])
 | 
			
		||||
        path = gtkgui_helpers.get_icon_path('gajim-connection_lost', 48)
 | 
			
		||||
        account = obj.conn.name
 | 
			
		||||
        notify.popup(_('Connection Failed'), account, account,
 | 
			
		||||
            'connection-lost', path, obj.title, obj.msg)
 | 
			
		||||
        app.notification.popup(
 | 
			
		||||
            _('Connection Failed'), account, account,
 | 
			
		||||
            'connection-lost', 'gajim-connection_lost', obj.title, obj.msg)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def unblock_signed_in_notifications(account):
 | 
			
		||||
| 
						 | 
				
			
			@ -509,11 +509,10 @@ class Interface:
 | 
			
		|||
        self.add_event(account, obj.jid, event)
 | 
			
		||||
 | 
			
		||||
        if helpers.allow_showing_notification(account):
 | 
			
		||||
            path = gtkgui_helpers.get_icon_path('gajim-subscription_request',
 | 
			
		||||
                48)
 | 
			
		||||
            event_type = _('Subscription request')
 | 
			
		||||
            notify.popup(event_type, obj.jid, account, 'subscription_request',
 | 
			
		||||
                path, event_type, obj.jid)
 | 
			
		||||
            app.notification.popup(
 | 
			
		||||
                event_type, obj.jid, account, 'subscription_request',
 | 
			
		||||
                'gajim-subscription_request', event_type, obj.jid)
 | 
			
		||||
 | 
			
		||||
    def handle_event_subscribed_presence(self, obj):
 | 
			
		||||
        #('SUBSCRIBED', account, (jid, resource))
 | 
			
		||||
| 
						 | 
				
			
			@ -567,9 +566,10 @@ class Interface:
 | 
			
		|||
        self.add_event(account, obj.jid, event)
 | 
			
		||||
 | 
			
		||||
        if helpers.allow_showing_notification(account):
 | 
			
		||||
            path = gtkgui_helpers.get_icon_path('gajim-unsubscribed', 48)
 | 
			
		||||
            event_type = _('Unsubscribed')
 | 
			
		||||
            notify.popup(event_type, obj.jid, account, 'unsubscribed', path,
 | 
			
		||||
            app.notification.popup(
 | 
			
		||||
                event_type, obj.jid, account,
 | 
			
		||||
                'unsubscribed', 'gajim-unsubscribed',
 | 
			
		||||
                event_type, obj.jid)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
| 
						 | 
				
			
			@ -660,10 +660,10 @@ class Interface:
 | 
			
		|||
        self.add_event(account, obj.jid_from, event)
 | 
			
		||||
 | 
			
		||||
        if helpers.allow_showing_notification(account):
 | 
			
		||||
            path = gtkgui_helpers.get_icon_path('gajim-gc_invitation', 48)
 | 
			
		||||
            event_type = _('Groupchat Invitation')
 | 
			
		||||
            notify.popup(event_type, obj.jid_from, account, 'gc-invitation',
 | 
			
		||||
                path, event_type, obj.room_jid)
 | 
			
		||||
            app.notification.popup(
 | 
			
		||||
                event_type, obj.jid_from, account, 'gc-invitation',
 | 
			
		||||
                'gajim-gc_invitation', event_type, obj.room_jid)
 | 
			
		||||
 | 
			
		||||
    def forget_gpg_passphrase(self, keyid):
 | 
			
		||||
        if keyid in self.gpg_passphrase:
 | 
			
		||||
| 
						 | 
				
			
			@ -680,9 +680,9 @@ class Interface:
 | 
			
		|||
                'key.')
 | 
			
		||||
            dialogs.WarningDialog(_('Wrong passphrase'), sectext)
 | 
			
		||||
        else:
 | 
			
		||||
            path = gtkgui_helpers.get_icon_path('gtk-dialog-warning', 48)
 | 
			
		||||
            account = obj.conn.name
 | 
			
		||||
            notify.popup('warning', account, account, '', path,
 | 
			
		||||
            app.notification.popup(
 | 
			
		||||
                'warning', account, account, '', 'dialog-warning',
 | 
			
		||||
                _('Wrong OpenPGP passphrase'),
 | 
			
		||||
                _('You are currently connected without your OpenPGP key.'))
 | 
			
		||||
        self.forget_gpg_passphrase(obj.keyID)
 | 
			
		||||
| 
						 | 
				
			
			@ -858,9 +858,10 @@ class Interface:
 | 
			
		|||
        self.add_event(account, jid, event)
 | 
			
		||||
 | 
			
		||||
        if helpers.allow_showing_notification(account):
 | 
			
		||||
            path = gtkgui_helpers.get_icon_path('gajim-ft_error', 48)
 | 
			
		||||
            event_type = _('File Transfer Error')
 | 
			
		||||
            notify.popup(event_type, jid, account, 'file-send-error', path,
 | 
			
		||||
            app.notification.popup(
 | 
			
		||||
                event_type, jid, account,
 | 
			
		||||
                'file-send-error', 'gajim-ft_error',
 | 
			
		||||
                event_type, file_props.name)
 | 
			
		||||
 | 
			
		||||
    def handle_event_file_request_error(self, obj):
 | 
			
		||||
| 
						 | 
				
			
			@ -888,9 +889,10 @@ class Interface:
 | 
			
		|||
 | 
			
		||||
        if helpers.allow_showing_notification(obj.conn.name):
 | 
			
		||||
            # check if we should be notified
 | 
			
		||||
            path = gtkgui_helpers.get_icon_path('gajim-ft_error', 48)
 | 
			
		||||
            event_type = _('File Transfer Error')
 | 
			
		||||
            notify.popup(event_type, obj.jid, obj.conn.name, msg_type, path,
 | 
			
		||||
            app.notification.popup(
 | 
			
		||||
                event_type, obj.jid, obj.conn.name,
 | 
			
		||||
                msg_type, 'gajim-ft_error',
 | 
			
		||||
                title=event_type, text=obj.file_props.name)
 | 
			
		||||
 | 
			
		||||
    def handle_event_file_request(self, obj):
 | 
			
		||||
| 
						 | 
				
			
			@ -921,12 +923,12 @@ class Interface:
 | 
			
		|||
        event = events.FileRequestEvent(obj.file_props)
 | 
			
		||||
        self.add_event(account, obj.jid, event)
 | 
			
		||||
        if helpers.allow_showing_notification(account):
 | 
			
		||||
            path = gtkgui_helpers.get_icon_path('gajim-ft_request', 48)
 | 
			
		||||
            txt = _('%s wants to send you a file.') % app.get_name_from_jid(
 | 
			
		||||
                account, obj.jid)
 | 
			
		||||
            event_type = _('File Transfer Request')
 | 
			
		||||
            notify.popup(event_type, obj.jid, account, 'file-request',
 | 
			
		||||
                path_to_image=path, title=event_type, text=txt)
 | 
			
		||||
            app.notification.popup(
 | 
			
		||||
                event_type, obj.jid, account, 'file-request',
 | 
			
		||||
                icon_name='gajim-ft_request', title=event_type, text=txt)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def handle_event_file_error(title, message):
 | 
			
		||||
| 
						 | 
				
			
			@ -1060,15 +1062,15 @@ class Interface:
 | 
			
		|||
                if event_type == _('File Transfer Completed'):
 | 
			
		||||
                    txt = _('%(filename)s received from %(name)s.')\
 | 
			
		||||
                    	% {'filename': filename, 'name': name}
 | 
			
		||||
                    img_name = 'gajim-ft_done'
 | 
			
		||||
                    icon_name = 'gajim-ft_done'
 | 
			
		||||
                elif event_type == _('File Transfer Stopped'):
 | 
			
		||||
                    txt = _('File transfer of %(filename)s from %(name)s '
 | 
			
		||||
                        'stopped.') % {'filename': filename, 'name': name}
 | 
			
		||||
                    img_name = 'gajim-ft_stopped'
 | 
			
		||||
                    icon_name = 'gajim-ft_stopped'
 | 
			
		||||
                else: # ft hash error
 | 
			
		||||
                    txt = _('File transfer of %(filename)s from %(name)s '
 | 
			
		||||
                        'failed.') % {'filename': filename, 'name': name}
 | 
			
		||||
                    img_name = 'gajim-ft_stopped'
 | 
			
		||||
                    icon_name = 'gajim-ft_stopped'
 | 
			
		||||
            else:
 | 
			
		||||
                receiver = file_props.receiver
 | 
			
		||||
                if hasattr(receiver, 'jid'):
 | 
			
		||||
| 
						 | 
				
			
			@ -1081,27 +1083,27 @@ class Interface:
 | 
			
		|||
                if event_type == _('File Transfer Completed'):
 | 
			
		||||
                    txt = _('You successfully sent %(filename)s to %(name)s.')\
 | 
			
		||||
                        % {'filename': filename, 'name': name}
 | 
			
		||||
                    img_name = 'gajim-ft_done'
 | 
			
		||||
                    icon_name = 'gajim-ft_done'
 | 
			
		||||
                elif event_type == _('File Transfer Stopped'):
 | 
			
		||||
                    txt = _('File transfer of %(filename)s to %(name)s '
 | 
			
		||||
                        'stopped.') % {'filename': filename, 'name': name}
 | 
			
		||||
                    img_name = 'gajim-ft_stopped'
 | 
			
		||||
                    icon_name = 'gajim-ft_stopped'
 | 
			
		||||
                else: # ft hash error
 | 
			
		||||
                    txt = _('File transfer of %(filename)s to %(name)s '
 | 
			
		||||
                        'failed.') % {'filename': filename, 'name': name}
 | 
			
		||||
                    img_name = 'gajim-ft_stopped'
 | 
			
		||||
            path = gtkgui_helpers.get_icon_path(img_name, 48)
 | 
			
		||||
                    icon_name = 'gajim-ft_stopped'
 | 
			
		||||
        else:
 | 
			
		||||
            txt = ''
 | 
			
		||||
            path = ''
 | 
			
		||||
            icon_name = None
 | 
			
		||||
 | 
			
		||||
        if app.config.get('notify_on_file_complete') and \
 | 
			
		||||
        (app.config.get('autopopupaway') or \
 | 
			
		||||
        app.connections[account].connected in (2, 3)):
 | 
			
		||||
            # we want to be notified and we are online/chat or we don't mind
 | 
			
		||||
            # bugged when away/na/busy
 | 
			
		||||
            notify.popup(event_type, jid, account, msg_type, path_to_image=path,
 | 
			
		||||
                    title=event_type, text=txt)
 | 
			
		||||
            app.notification.popup(
 | 
			
		||||
                event_type, jid, account, msg_type,
 | 
			
		||||
                icon_name=icon_name, title=event_type, text=txt)
 | 
			
		||||
 | 
			
		||||
    def handle_event_signed_in(self, obj):
 | 
			
		||||
        """
 | 
			
		||||
| 
						 | 
				
			
			@ -1278,10 +1280,10 @@ class Interface:
 | 
			
		|||
            # TODO: we should use another pixmap ;-)
 | 
			
		||||
            txt = _('%s wants to start a voice chat.') % \
 | 
			
		||||
                app.get_name_from_jid(account, obj.fjid)
 | 
			
		||||
            path = gtkgui_helpers.get_icon_path('gajim-mic_active', 48)
 | 
			
		||||
            event_type = _('Voice Chat Request')
 | 
			
		||||
            notify.popup(event_type, obj.fjid, account, 'jingle-incoming',
 | 
			
		||||
                    path_to_image=path, title=event_type, text=txt)
 | 
			
		||||
            app.notification.popup(
 | 
			
		||||
                event_type, obj.fjid, account, 'jingle-incoming',
 | 
			
		||||
                icon_name='gajim-mic_active', title=event_type, text=txt)
 | 
			
		||||
 | 
			
		||||
    def handle_event_jingle_connected(self, obj):
 | 
			
		||||
        # ('JINGLE_CONNECTED', account, (peerjid, sid, media))
 | 
			
		||||
| 
						 | 
				
			
			@ -1652,28 +1654,7 @@ class Interface:
 | 
			
		|||
        elif type_ in ('printed_chat', 'chat', ''):
 | 
			
		||||
            # '' is for log in/out notifications
 | 
			
		||||
 | 
			
		||||
            if type_ != '':
 | 
			
		||||
                event = app.events.get_first_event(account, fjid, type_)
 | 
			
		||||
                if not event:
 | 
			
		||||
                    event = app.events.get_first_event(account, jid, type_)
 | 
			
		||||
                if not event:
 | 
			
		||||
                    # If autopopup_chat_opened = True, then we send out
 | 
			
		||||
                    # notifications even if a control is open. This means the
 | 
			
		||||
                    # event is already deleted (because its printed to the
 | 
			
		||||
                    # control) when the notification is clicked. So try to
 | 
			
		||||
                    # get a control from account/jid
 | 
			
		||||
                    ctrl = self.msg_win_mgr.get_control(fjid, account)
 | 
			
		||||
                    if ctrl is None:
 | 
			
		||||
                        return
 | 
			
		||||
                    w = ctrl.parent_win
 | 
			
		||||
 | 
			
		||||
            if type_ == 'printed_chat':
 | 
			
		||||
                ctrl = event.control
 | 
			
		||||
            elif type_ == 'chat':
 | 
			
		||||
                session = event.session
 | 
			
		||||
                ctrl = session.control
 | 
			
		||||
            elif type_ == '':
 | 
			
		||||
                ctrl = self.msg_win_mgr.get_control(fjid, account)
 | 
			
		||||
            ctrl = self.msg_win_mgr.search_control(jid, account, resource)
 | 
			
		||||
 | 
			
		||||
            if not ctrl:
 | 
			
		||||
                highest_contact = app.contacts.\
 | 
			
		||||
| 
						 | 
				
			
			@ -1692,33 +1673,20 @@ class Interface:
 | 
			
		|||
                if not contact:
 | 
			
		||||
                    contact = highest_contact
 | 
			
		||||
 | 
			
		||||
                ctrl = self.new_chat(contact, account, resource=resource,
 | 
			
		||||
                    session=session)
 | 
			
		||||
                ctrl = self.new_chat(contact, account, resource=resource)
 | 
			
		||||
 | 
			
		||||
                app.last_message_time[account][jid] = 0 # long time ago
 | 
			
		||||
 | 
			
		||||
            w = ctrl.parent_win
 | 
			
		||||
        elif type_ in ('printed_pm', 'pm'):
 | 
			
		||||
            # assume that the most recently updated control we have for this
 | 
			
		||||
            # party is the one that this event was in
 | 
			
		||||
            event = app.events.get_first_event(account, fjid, type_)
 | 
			
		||||
            if not event:
 | 
			
		||||
                event = app.events.get_first_event(account, jid, type_)
 | 
			
		||||
            if not event:
 | 
			
		||||
                return
 | 
			
		||||
 | 
			
		||||
            if type_ == 'printed_pm':
 | 
			
		||||
                ctrl = event.control
 | 
			
		||||
            elif type_ == 'pm':
 | 
			
		||||
                session = event.session
 | 
			
		||||
            ctrl = self.msg_win_mgr.get_control(fjid, account)
 | 
			
		||||
 | 
			
		||||
            if session and session.control:
 | 
			
		||||
                ctrl = session.control
 | 
			
		||||
            elif not ctrl:
 | 
			
		||||
            if not ctrl:
 | 
			
		||||
                room_jid = jid
 | 
			
		||||
                nick = resource
 | 
			
		||||
                gc_contact = app.contacts.get_gc_contact(account, room_jid,
 | 
			
		||||
                    nick)
 | 
			
		||||
                gc_contact = app.contacts.get_gc_contact(
 | 
			
		||||
                    account, room_jid, nick)
 | 
			
		||||
                if gc_contact:
 | 
			
		||||
                    show = gc_contact.show
 | 
			
		||||
                else:
 | 
			
		||||
| 
						 | 
				
			
			@ -1727,12 +1695,7 @@ class Interface:
 | 
			
		|||
                        room_jid=room_jid, account=account, name=nick,
 | 
			
		||||
                        show=show)
 | 
			
		||||
 | 
			
		||||
                if not session:
 | 
			
		||||
                    session = app.connections[account].make_new_session(
 | 
			
		||||
                        fjid, None, type_='pm')
 | 
			
		||||
 | 
			
		||||
                self.new_private_chat(gc_contact, account, session=session)
 | 
			
		||||
                ctrl = session.control
 | 
			
		||||
                ctrl = self.new_private_chat(gc_contact, account)
 | 
			
		||||
 | 
			
		||||
            w = ctrl.parent_win
 | 
			
		||||
        elif type_ in ('normal', 'file-request', 'file-request-error',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										330
									
								
								gajim/notify.py
									
										
									
									
									
								
							
							
						
						
									
										330
									
								
								gajim/notify.py
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,42 +1,43 @@
 | 
			
		|||
# -*- coding:utf-8 -*-
 | 
			
		||||
## src/notify.py
 | 
			
		||||
##
 | 
			
		||||
## Copyright (C) 2005 Sebastian Estienne
 | 
			
		||||
## Copyright (C) 2005-2006 Andrew Sayman <lorien420 AT myrealbox.com>
 | 
			
		||||
## Copyright (C) 2005-2007 Nikos Kouremenos <kourem AT gmail.com>
 | 
			
		||||
## Copyright (C) 2005-2014 Yann Leboulanger <asterix AT lagaule.org>
 | 
			
		||||
## Copyright (C) 2006 Travis Shirk <travis AT pobox.com>
 | 
			
		||||
## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org>
 | 
			
		||||
## Copyright (C) 2007 Julien Pivotto <roidelapluie AT gmail.com>
 | 
			
		||||
##                    Stephan Erb <steve-e AT h3c.de>
 | 
			
		||||
## Copyright (C) 2008 Brendan Taylor <whateley AT gmail.com>
 | 
			
		||||
##                    Jonathan Schleifer <js-gajim AT webkeks.org>
 | 
			
		||||
##
 | 
			
		||||
## 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 <http://www.gnu.org/licenses/>.
 | 
			
		||||
##
 | 
			
		||||
#
 | 
			
		||||
# Copyright (C) 2005 Sebastian Estienne
 | 
			
		||||
# Copyright (C) 2005-2006 Andrew Sayman <lorien420 AT myrealbox.com>
 | 
			
		||||
# Copyright (C) 2005-2007 Nikos Kouremenos <kourem AT gmail.com>
 | 
			
		||||
# Copyright (C) 2005-2014 Yann Leboulanger <asterix AT lagaule.org>
 | 
			
		||||
# Copyright (C) 2006 Travis Shirk <travis AT pobox.com>
 | 
			
		||||
# Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org>
 | 
			
		||||
# Copyright (C) 2007 Julien Pivotto <roidelapluie AT gmail.com>
 | 
			
		||||
#                    Stephan Erb <steve-e AT h3c.de>
 | 
			
		||||
# Copyright (C) 2008 Brendan Taylor <whateley AT gmail.com>
 | 
			
		||||
#                    Jonathan Schleifer <js-gajim AT webkeks.org>
 | 
			
		||||
# Copyright (C) 2018 Philipp Hörist <philipp AT hoerist.com>
 | 
			
		||||
#
 | 
			
		||||
# 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 <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
import sys
 | 
			
		||||
from gajim.dialogs import PopupNotificationWindow
 | 
			
		||||
 | 
			
		||||
from gi.repository import GLib
 | 
			
		||||
from gi.repository import Gio
 | 
			
		||||
from gajim import gtkgui_helpers
 | 
			
		||||
from gi.repository import Gdk
 | 
			
		||||
from gi.repository import Gtk
 | 
			
		||||
 | 
			
		||||
from gajim import gtkgui_helpers
 | 
			
		||||
from gajim.common import app
 | 
			
		||||
from gajim.common import helpers
 | 
			
		||||
from gajim.common import ged
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_show_in_roster(event, account, contact, session=None):
 | 
			
		||||
    """
 | 
			
		||||
    Return True if this event must be shown in roster, else False
 | 
			
		||||
| 
						 | 
				
			
			@ -48,91 +49,37 @@ def get_show_in_roster(event, account, contact, session=None):
 | 
			
		|||
            return False
 | 
			
		||||
    return True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_show_in_systray(event, account, contact, type_=None):
 | 
			
		||||
    """
 | 
			
		||||
    Return True if this event must be shown in systray, else False
 | 
			
		||||
    """
 | 
			
		||||
    if type_ == 'printed_gc_msg' and not app.config.get(
 | 
			
		||||
    'notify_on_all_muc_messages') and not app.config.get_per('rooms',
 | 
			
		||||
    contact.jid, 'notify_on_all_messages'):
 | 
			
		||||
 | 
			
		||||
    notify = app.config.get('notify_on_all_muc_messages')
 | 
			
		||||
    notify_for_jid = app.config.get_per(
 | 
			
		||||
        'rooms', contact.jid, 'notify_on_all_messages')
 | 
			
		||||
 | 
			
		||||
    if type_ == 'printed_gc_msg' and not notify and not notify_for_jid:
 | 
			
		||||
        # it's not an highlighted message, don't show in systray
 | 
			
		||||
        return False
 | 
			
		||||
    return app.config.get('trayicon_notification_on_events')
 | 
			
		||||
 | 
			
		||||
def popup(event_type, jid, account, type_='', path_to_image=None, title=None,
 | 
			
		||||
text=None, timeout=-1):
 | 
			
		||||
    """
 | 
			
		||||
    Notify a user of an event using GNotification and GApplication under linux,
 | 
			
		||||
    the older style PopupNotificationWindow method under windows
 | 
			
		||||
    """
 | 
			
		||||
    # default image
 | 
			
		||||
    if not path_to_image:
 | 
			
		||||
        path_to_image = gtkgui_helpers.get_icon_path('gajim-chat_msg_recv', 48)
 | 
			
		||||
 | 
			
		||||
    if timeout < 0:
 | 
			
		||||
        timeout = app.config.get('notification_timeout')
 | 
			
		||||
 | 
			
		||||
    if sys.platform == 'win32':
 | 
			
		||||
        instance = PopupNotificationWindow(event_type, jid, account, type_,
 | 
			
		||||
            path_to_image, title, text, timeout)
 | 
			
		||||
        app.interface.roster.popup_notification_windows.append(instance)
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    # use GNotification
 | 
			
		||||
    # TODO: Move to standard GTK+ icons here.
 | 
			
		||||
    icon = Gio.FileIcon.new(Gio.File.new_for_path(path_to_image))
 | 
			
		||||
    notification = Gio.Notification()
 | 
			
		||||
    if title is not None:
 | 
			
		||||
        notification.set_title(title)
 | 
			
		||||
    if text is not None:
 | 
			
		||||
        notification.set_body(text)
 | 
			
		||||
    notification.set_icon(icon)
 | 
			
		||||
    notif_id = None
 | 
			
		||||
    if event_type in (_('Contact Signed In'), _('Contact Signed Out'),
 | 
			
		||||
    _('New Message'), _('New Single Message'), _('New Private Message'),
 | 
			
		||||
    _('Contact Changed Status'), _('File Transfer Request'),
 | 
			
		||||
    _('File Transfer Error'), _('File Transfer Completed'),
 | 
			
		||||
    _('File Transfer Stopped'), _('Groupchat Invitation'),
 | 
			
		||||
    _('Connection Failed'), _('Subscription request'), _('Unsubscribed')):
 | 
			
		||||
        # Create Variant Dict
 | 
			
		||||
        dict_ = {'account': GLib.Variant('s', account),
 | 
			
		||||
                 'jid': GLib.Variant('s', jid),
 | 
			
		||||
                 'type_': GLib.Variant('s', type_)}
 | 
			
		||||
        variant_dict = GLib.Variant('a{sv}', dict_)
 | 
			
		||||
        action = 'app.{}-open-event'.format(account)
 | 
			
		||||
        #Button in notification
 | 
			
		||||
        notification.add_button_with_target(_('Open'), action, variant_dict)
 | 
			
		||||
        notification.set_default_action_and_target(action, variant_dict)
 | 
			
		||||
        if event_type in (_('New Message'), _('New Single Message'),
 | 
			
		||||
        _('New Private Message')):
 | 
			
		||||
            # Only one notification per JID
 | 
			
		||||
            notif_id = jid
 | 
			
		||||
    notification.set_priority(Gio.NotificationPriority.NORMAL)
 | 
			
		||||
    app.app.send_notification(notif_id, notification)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Notification:
 | 
			
		||||
    """
 | 
			
		||||
    Handle notifications
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        app.ged.register_event_handler('notification', ged.GUI2,
 | 
			
		||||
            self._nec_notification)
 | 
			
		||||
        app.ged.register_event_handler(
 | 
			
		||||
            'notification', ged.GUI2, self._nec_notification)
 | 
			
		||||
 | 
			
		||||
    def _nec_notification(self, obj):
 | 
			
		||||
        if obj.do_popup:
 | 
			
		||||
            if obj.popup_image:
 | 
			
		||||
                icon_path = gtkgui_helpers.get_icon_path(obj.popup_image, 48)
 | 
			
		||||
                if icon_path:
 | 
			
		||||
                    image_path = icon_path
 | 
			
		||||
            elif obj.popup_image_path:
 | 
			
		||||
                image_path = obj.popup_image_path
 | 
			
		||||
            else:
 | 
			
		||||
                image_path = ''
 | 
			
		||||
            popup(obj.popup_event_type, obj.jid, obj.conn.name,
 | 
			
		||||
                obj.popup_msg_type, path_to_image=image_path,
 | 
			
		||||
                title=obj.popup_title, text=obj.popup_text,
 | 
			
		||||
                timeout=obj.popup_timeout)
 | 
			
		||||
            icon_name = self._get_icon_name(obj)
 | 
			
		||||
            self.popup(obj.popup_event_type, obj.jid, obj.conn.name,
 | 
			
		||||
                       obj.popup_msg_type, icon_name=icon_name,
 | 
			
		||||
                       title=obj.popup_title, text=obj.popup_text,
 | 
			
		||||
                       timeout=obj.popup_timeout)
 | 
			
		||||
 | 
			
		||||
        if obj.do_sound:
 | 
			
		||||
            if obj.sound_file:
 | 
			
		||||
| 
						 | 
				
			
			@ -145,3 +92,190 @@ class Notification:
 | 
			
		|||
                helpers.exec_command(obj.command, use_shell=True)
 | 
			
		||||
            except Exception:
 | 
			
		||||
                pass
 | 
			
		||||
 | 
			
		||||
    def _get_icon_name(self, obj):
 | 
			
		||||
        if obj.notif_type == 'msg':
 | 
			
		||||
            if obj.base_event.mtype == 'pm':
 | 
			
		||||
                return 'gajim-priv_msg_recv'
 | 
			
		||||
            if obj.base_event.mtype == 'normal':
 | 
			
		||||
                return 'gajim-single_msg_recv'
 | 
			
		||||
 | 
			
		||||
        elif obj.notif_type == 'pres':
 | 
			
		||||
            if obj.transport_name is not None:
 | 
			
		||||
                return '%s-%s' % (obj.transport_name, obj.show)
 | 
			
		||||
            else:
 | 
			
		||||
                return gtkgui_helpers.get_iconset_name_for(obj.show)
 | 
			
		||||
 | 
			
		||||
    def popup(self, event_type, jid, account, type_='', icon_name=None,
 | 
			
		||||
              title=None, text=None, timeout=-1):
 | 
			
		||||
        """
 | 
			
		||||
        Notify a user of an event using GNotification and GApplication under
 | 
			
		||||
        Linux, Use PopupNotificationWindow under Windows
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        if icon_name is None:
 | 
			
		||||
            icon_name = 'gajim-chat_msg_recv'
 | 
			
		||||
 | 
			
		||||
        if timeout < 0:
 | 
			
		||||
            timeout = app.config.get('notification_timeout')
 | 
			
		||||
 | 
			
		||||
        if sys.platform == 'win32':
 | 
			
		||||
            instance = PopupNotificationWindow(event_type, jid, account, type_,
 | 
			
		||||
                                               icon_name, title, text, timeout)
 | 
			
		||||
            app.interface.roster.popup_notification_windows.append(instance)
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        scale = app.get_monitor_scale_factor()
 | 
			
		||||
        icon_pixbuf = gtkgui_helpers.gtk_icon_theme.load_icon_for_scale(
 | 
			
		||||
            icon_name, 48, scale, 0)
 | 
			
		||||
 | 
			
		||||
        notification = Gio.Notification()
 | 
			
		||||
        if title is not None:
 | 
			
		||||
            notification.set_title(title)
 | 
			
		||||
        if text is not None:
 | 
			
		||||
            notification.set_body(text)
 | 
			
		||||
        notification.set_icon(icon_pixbuf)
 | 
			
		||||
        notif_id = None
 | 
			
		||||
        if event_type in (_('Contact Signed In'), _('Contact Signed Out'),
 | 
			
		||||
        _('New Message'), _('New Single Message'), _('New Private Message'),
 | 
			
		||||
        _('Contact Changed Status'), _('File Transfer Request'),
 | 
			
		||||
        _('File Transfer Error'), _('File Transfer Completed'),
 | 
			
		||||
        _('File Transfer Stopped'), _('Groupchat Invitation'),
 | 
			
		||||
        _('Connection Failed'), _('Subscription request'), _('Unsubscribed')):
 | 
			
		||||
            # Create Variant Dict
 | 
			
		||||
            dict_ = {'account': GLib.Variant('s', account),
 | 
			
		||||
                     'jid': GLib.Variant('s', jid),
 | 
			
		||||
                     'type_': GLib.Variant('s', type_)}
 | 
			
		||||
            variant_dict = GLib.Variant('a{sv}', dict_)
 | 
			
		||||
            action = 'app.{}-open-event'.format(account)
 | 
			
		||||
            #Button in notification
 | 
			
		||||
            notification.add_button_with_target(_('Open'), action, variant_dict)
 | 
			
		||||
            notification.set_default_action_and_target(action, variant_dict)
 | 
			
		||||
            if event_type in (_('New Message'), _('New Single Message'),
 | 
			
		||||
            _('New Private Message')):
 | 
			
		||||
                # Only one notification per JID
 | 
			
		||||
                notif_id = jid
 | 
			
		||||
        notification.set_priority(Gio.NotificationPriority.NORMAL)
 | 
			
		||||
        app.app.send_notification(notif_id, notification)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PopupNotificationWindow:
 | 
			
		||||
    def __init__(self, event_type, jid, account, msg_type='',
 | 
			
		||||
                 icon_name=None, title=None, text=None, timeout=-1):
 | 
			
		||||
        self.account = account
 | 
			
		||||
        self.jid = jid
 | 
			
		||||
        self.msg_type = msg_type
 | 
			
		||||
        self.index = len(app.interface.roster.popup_notification_windows)
 | 
			
		||||
        xml = gtkgui_helpers.get_gtk_builder('popup_notification_window.ui')
 | 
			
		||||
        self.window = xml.get_object('popup_notification_window')
 | 
			
		||||
        self.window.set_type_hint(Gdk.WindowTypeHint.TOOLTIP)
 | 
			
		||||
        self.window.set_name('NotificationPopup')
 | 
			
		||||
        close_button = xml.get_object('close_button')
 | 
			
		||||
        event_type_label = xml.get_object('event_type_label')
 | 
			
		||||
        event_description_label = xml.get_object('event_description_label')
 | 
			
		||||
        eventbox = xml.get_object('eventbox')
 | 
			
		||||
        image = xml.get_object('notification_image')
 | 
			
		||||
 | 
			
		||||
        if not text:
 | 
			
		||||
            text = app.get_name_from_jid(account, jid)  # default value of text
 | 
			
		||||
        if not title:
 | 
			
		||||
            title = ''
 | 
			
		||||
 | 
			
		||||
        event_type_label.set_markup(
 | 
			
		||||
            '<span foreground="black" weight="bold">%s</span>' %
 | 
			
		||||
            GLib.markup_escape_text(title))
 | 
			
		||||
 | 
			
		||||
        css = '#NotificationPopup {background-color: black }'
 | 
			
		||||
        gtkgui_helpers.add_css_to_widget(self.window, css)
 | 
			
		||||
 | 
			
		||||
        if event_type == _('Contact Signed In'):
 | 
			
		||||
            bg_color = app.config.get('notif_signin_color')
 | 
			
		||||
        elif event_type == _('Contact Signed Out'):
 | 
			
		||||
            bg_color = app.config.get('notif_signout_color')
 | 
			
		||||
        elif event_type in (_('New Message'), _('New Single Message'),
 | 
			
		||||
            _('New Private Message'), _('New E-mail')):
 | 
			
		||||
            bg_color = app.config.get('notif_message_color')
 | 
			
		||||
        elif event_type == _('File Transfer Request'):
 | 
			
		||||
            bg_color = app.config.get('notif_ftrequest_color')
 | 
			
		||||
        elif event_type == _('File Transfer Error'):
 | 
			
		||||
            bg_color = app.config.get('notif_fterror_color')
 | 
			
		||||
        elif event_type in (_('File Transfer Completed'),
 | 
			
		||||
        _('File Transfer Stopped')):
 | 
			
		||||
            bg_color = app.config.get('notif_ftcomplete_color')
 | 
			
		||||
        elif event_type == _('Groupchat Invitation'):
 | 
			
		||||
            bg_color = app.config.get('notif_invite_color')
 | 
			
		||||
        elif event_type == _('Contact Changed Status'):
 | 
			
		||||
            bg_color = app.config.get('notif_status_color')
 | 
			
		||||
        else: # Unknown event! Shouldn't happen but deal with it
 | 
			
		||||
            bg_color = app.config.get('notif_other_color')
 | 
			
		||||
 | 
			
		||||
        background_class = '''
 | 
			
		||||
            .popup-style {
 | 
			
		||||
                border-image: none;
 | 
			
		||||
                background-image: none;
 | 
			
		||||
                background-color: %s }''' % bg_color
 | 
			
		||||
 | 
			
		||||
        gtkgui_helpers.add_css_to_widget(eventbox, background_class)
 | 
			
		||||
        eventbox.get_style_context().add_class('popup-style')
 | 
			
		||||
 | 
			
		||||
        gtkgui_helpers.add_css_to_widget(close_button, background_class)
 | 
			
		||||
        eventbox.get_style_context().add_class('popup-style')
 | 
			
		||||
 | 
			
		||||
        event_description_label.set_markup('<span foreground="black">%s</span>' %
 | 
			
		||||
            GLib.markup_escape_text(text))
 | 
			
		||||
 | 
			
		||||
        # set the image
 | 
			
		||||
        image.set_from_icon_name(icon_name, Gtk.IconSize.DIALOG)
 | 
			
		||||
 | 
			
		||||
        # position the window to bottom-right of screen
 | 
			
		||||
        window_width, self.window_height = self.window.get_size()
 | 
			
		||||
        app.interface.roster.popups_notification_height += self.window_height
 | 
			
		||||
        pos_x = app.config.get('notification_position_x')
 | 
			
		||||
        screen_w, screen_h = gtkgui_helpers.get_total_screen_geometry()
 | 
			
		||||
        if pos_x < 0:
 | 
			
		||||
            pos_x = screen_w - window_width + pos_x + 1
 | 
			
		||||
        pos_y = app.config.get('notification_position_y')
 | 
			
		||||
        if pos_y < 0:
 | 
			
		||||
            pos_y = screen_h - \
 | 
			
		||||
                app.interface.roster.popups_notification_height + pos_y + 1
 | 
			
		||||
        self.window.move(pos_x, pos_y)
 | 
			
		||||
 | 
			
		||||
        xml.connect_signals(self)
 | 
			
		||||
        self.window.show_all()
 | 
			
		||||
        if timeout > 0:
 | 
			
		||||
            GLib.timeout_add_seconds(timeout, self.on_timeout)
 | 
			
		||||
 | 
			
		||||
    def on_close_button_clicked(self, widget):
 | 
			
		||||
        self.adjust_height_and_move_popup_notification_windows()
 | 
			
		||||
 | 
			
		||||
    def on_timeout(self):
 | 
			
		||||
        self.adjust_height_and_move_popup_notification_windows()
 | 
			
		||||
 | 
			
		||||
    def adjust_height_and_move_popup_notification_windows(self):
 | 
			
		||||
        #remove
 | 
			
		||||
        app.interface.roster.popups_notification_height -= self.window_height
 | 
			
		||||
        self.window.destroy()
 | 
			
		||||
 | 
			
		||||
        if len(app.interface.roster.popup_notification_windows) > self.index:
 | 
			
		||||
            # we want to remove the destroyed window from the list
 | 
			
		||||
            app.interface.roster.popup_notification_windows.pop(self.index)
 | 
			
		||||
 | 
			
		||||
        # move the rest of popup windows
 | 
			
		||||
        app.interface.roster.popups_notification_height = 0
 | 
			
		||||
        current_index = 0
 | 
			
		||||
        for window_instance in app.interface.roster.popup_notification_windows:
 | 
			
		||||
            window_instance.index = current_index
 | 
			
		||||
            current_index += 1
 | 
			
		||||
            window_width, window_height = window_instance.window.get_size()
 | 
			
		||||
            app.interface.roster.popups_notification_height += window_height
 | 
			
		||||
            screen_w, screen_h = gtkgui_helpers.get_total_screen_geometry()
 | 
			
		||||
            window_instance.window.move(screen_w - window_width,
 | 
			
		||||
                screen_h - \
 | 
			
		||||
                app.interface.roster.popups_notification_height)
 | 
			
		||||
 | 
			
		||||
    def on_popup_notification_window_button_press_event(self, widget, event):
 | 
			
		||||
        if event.button != 1:
 | 
			
		||||
            self.window.destroy()
 | 
			
		||||
            return
 | 
			
		||||
        app.interface.handle_event(self.account, self.jid, self.msg_type)
 | 
			
		||||
        self.adjust_height_and_move_popup_notification_windows()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue