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
|
@ -1268,6 +1268,13 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
|
||||||
app.log('autoscroll').info('Autoscroll disabled')
|
app.log('autoscroll').info('Autoscroll disabled')
|
||||||
self.conv_textview.autoscroll = False
|
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):
|
def _on_scroll(self, widget, event):
|
||||||
if not self.conv_textview.autoscroll:
|
if not self.conv_textview.autoscroll:
|
||||||
# autoscroll is already disabled
|
# autoscroll is already disabled
|
||||||
|
|
|
@ -27,7 +27,6 @@ import hashlib
|
||||||
import hmac
|
import hmac
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
import os
|
|
||||||
from time import time as time_time
|
from time import time as time_time
|
||||||
|
|
||||||
import OpenSSL.crypto
|
import OpenSSL.crypto
|
||||||
|
@ -40,7 +39,6 @@ from gajim.common import helpers
|
||||||
from gajim.common import app
|
from gajim.common import app
|
||||||
from gajim.common import i18n
|
from gajim.common import i18n
|
||||||
from gajim.common import dataforms
|
from gajim.common import dataforms
|
||||||
from gajim.common import configpaths
|
|
||||||
from gajim.common.zeroconf.zeroconf import Constant
|
from gajim.common.zeroconf.zeroconf import Constant
|
||||||
from gajim.common.const import KindConstant, SSLError
|
from gajim.common.const import KindConstant, SSLError
|
||||||
from gajim.common.pep import SUPPORTED_PERSONAL_USER_EVENTS
|
from gajim.common.pep import SUPPORTED_PERSONAL_USER_EVENTS
|
||||||
|
@ -2583,8 +2581,50 @@ class GatewayPromptReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
|
||||||
|
|
||||||
class NotificationEvent(nec.NetworkIncomingEvent):
|
class NotificationEvent(nec.NetworkIncomingEvent):
|
||||||
name = 'notification'
|
name = 'notification'
|
||||||
base_network_events = ['decrypted-message-received', 'gc-message-received',
|
base_network_events = ['decrypted-message-received',
|
||||||
'presence-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):
|
def detect_type(self):
|
||||||
if self.base_event.name == 'decrypted-message-received':
|
if self.base_event.name == 'decrypted-message-received':
|
||||||
|
@ -2594,14 +2634,6 @@ class NotificationEvent(nec.NetworkIncomingEvent):
|
||||||
if self.base_event.name == 'presence-received':
|
if self.base_event.name == 'presence-received':
|
||||||
self.notif_type = 'pres'
|
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):
|
def handle_incoming_msg_event(self, msg_obj):
|
||||||
# don't alert for carbon copied messages from ourselves
|
# don't alert for carbon copied messages from ourselves
|
||||||
if msg_obj.sent:
|
if msg_obj.sent:
|
||||||
|
@ -2609,15 +2641,18 @@ class NotificationEvent(nec.NetworkIncomingEvent):
|
||||||
if not msg_obj.msgtxt:
|
if not msg_obj.msgtxt:
|
||||||
return
|
return
|
||||||
self.jid = msg_obj.jid
|
self.jid = msg_obj.jid
|
||||||
if msg_obj.session:
|
if msg_obj.mtype == 'pm':
|
||||||
self.control = msg_obj.session.control
|
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:
|
else:
|
||||||
self.control = None
|
self.control_focused = self.control.has_focus()
|
||||||
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
|
|
||||||
|
|
||||||
if msg_obj.mtype == 'pm':
|
if msg_obj.mtype == 'pm':
|
||||||
nick = msg_obj.resource
|
nick = msg_obj.resource
|
||||||
|
@ -2642,13 +2677,11 @@ class NotificationEvent(nec.NetworkIncomingEvent):
|
||||||
if msg_obj.mtype == 'normal': # single message
|
if msg_obj.mtype == 'normal': # single message
|
||||||
self.popup_msg_type = 'normal'
|
self.popup_msg_type = 'normal'
|
||||||
self.popup_event_type = _('New 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') % \
|
self.popup_title = _('New Single Message from %(nickname)s') % \
|
||||||
{'nickname': nick}
|
{'nickname': nick}
|
||||||
elif msg_obj.mtype == 'pm':
|
elif msg_obj.mtype == 'pm':
|
||||||
self.popup_msg_type = 'pm'
|
self.popup_msg_type = 'pm'
|
||||||
self.popup_event_type = _('New Private Message')
|
self.popup_event_type = _('New Private Message')
|
||||||
self.popup_image = 'gajim-priv_msg_recv'
|
|
||||||
self.popup_title = _('New Private Message from group chat %s') % \
|
self.popup_title = _('New Private Message from group chat %s') % \
|
||||||
msg_obj.jid
|
msg_obj.jid
|
||||||
if self.popup_text:
|
if self.popup_text:
|
||||||
|
@ -2660,11 +2693,9 @@ class NotificationEvent(nec.NetworkIncomingEvent):
|
||||||
else: # chat message
|
else: # chat message
|
||||||
self.popup_msg_type = 'chat'
|
self.popup_msg_type = 'chat'
|
||||||
self.popup_event_type = _('New Message')
|
self.popup_event_type = _('New Message')
|
||||||
self.popup_image = 'gajim-chat_msg_recv'
|
|
||||||
self.popup_title = _('New Message from %(nickname)s') % \
|
self.popup_title = _('New Message from %(nickname)s') % \
|
||||||
{'nickname': nick}
|
{'nickname': nick}
|
||||||
|
|
||||||
|
|
||||||
if app.config.get('notify_on_new_message'):
|
if app.config.get('notify_on_new_message'):
|
||||||
if self.first_unread or (app.config.get('autopopup_chat_opened') \
|
if self.first_unread or (app.config.get('autopopup_chat_opened') \
|
||||||
and not self.control_focused):
|
and not self.control_focused):
|
||||||
|
@ -2720,29 +2751,6 @@ class NotificationEvent(nec.NetworkIncomingEvent):
|
||||||
|
|
||||||
self.do_popup = False
|
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):
|
def handle_incoming_pres_event(self, pres_obj):
|
||||||
if app.jid_is_transport(pres_obj.jid):
|
if app.jid_is_transport(pres_obj.jid):
|
||||||
return True
|
return True
|
||||||
|
@ -2757,7 +2765,6 @@ class NotificationEvent(nec.NetworkIncomingEvent):
|
||||||
if c.show not in ('offline', 'error'):
|
if c.show not in ('offline', 'error'):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
# no other resource is connected, let's look in metacontacts
|
# no other resource is connected, let's look in metacontacts
|
||||||
family = app.contacts.get_metacontacts_family(account, self.jid)
|
family = app.contacts.get_metacontacts_family(account, self.jid)
|
||||||
for info in family:
|
for info in family:
|
||||||
|
@ -2773,8 +2780,6 @@ class NotificationEvent(nec.NetworkIncomingEvent):
|
||||||
|
|
||||||
if pres_obj.old_show < 2 and pres_obj.new_show > 1:
|
if pres_obj.old_show < 2 and pres_obj.new_show > 1:
|
||||||
event = 'contact_connected'
|
event = 'contact_connected'
|
||||||
show_image = 'online.png'
|
|
||||||
suffix = '_notif_size_colored'
|
|
||||||
server = app.get_server_from_jid(self.jid)
|
server = app.get_server_from_jid(self.jid)
|
||||||
account_server = account + '/' + server
|
account_server = account + '/' + server
|
||||||
block_transport = False
|
block_transport = False
|
||||||
|
@ -2794,8 +2799,6 @@ class NotificationEvent(nec.NetworkIncomingEvent):
|
||||||
|
|
||||||
elif pres_obj.old_show > 1 and pres_obj.new_show < 2:
|
elif pres_obj.old_show > 1 and pres_obj.new_show < 2:
|
||||||
event = 'contact_disconnected'
|
event = 'contact_disconnected'
|
||||||
show_image = 'offline.png'
|
|
||||||
suffix = '_notif_size_bw'
|
|
||||||
if helpers.allow_showing_notification(account, 'notify_on_signout'):
|
if helpers.allow_showing_notification(account, 'notify_on_signout'):
|
||||||
self.do_popup = True
|
self.do_popup = True
|
||||||
if app.config.get_per('soundevents', 'contact_disconnected',
|
if app.config.get_per('soundevents', 'contact_disconnected',
|
||||||
|
@ -2805,24 +2808,13 @@ class NotificationEvent(nec.NetworkIncomingEvent):
|
||||||
# Status change (not connected/disconnected or error (<1))
|
# Status change (not connected/disconnected or error (<1))
|
||||||
elif pres_obj.new_show > 1:
|
elif pres_obj.new_show > 1:
|
||||||
event = 'status_change'
|
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:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
transport_name = app.get_transport_name_from_jid(self.jid)
|
if app.jid_is_transport(self.jid):
|
||||||
img_path = None
|
self.transport_name = app.get_transport_name_from_jid(self.jid)
|
||||||
if transport_name:
|
|
||||||
img_path = os.path.join(helpers.get_transport_path(
|
self.show = pres_obj.show
|
||||||
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)
|
|
||||||
|
|
||||||
self.popup_timeout = app.config.get('notification_timeout')
|
self.popup_timeout = app.config.get('notification_timeout')
|
||||||
|
|
||||||
|
@ -2848,45 +2840,6 @@ class NotificationEvent(nec.NetworkIncomingEvent):
|
||||||
self.popup_text = pres_obj.status
|
self.popup_text = pres_obj.status
|
||||||
self.popup_event_type = _('Contact Signed Out')
|
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):
|
class MessageOutgoingEvent(nec.NetworkOutgoingEvent):
|
||||||
name = 'message-outgoing'
|
name = 'message-outgoing'
|
||||||
base_network_events = []
|
base_network_events = []
|
||||||
|
|
125
gajim/dialogs.py
125
gajim/dialogs.py
|
@ -3060,131 +3060,6 @@ class ChangePasswordDialog:
|
||||||
dialog.destroy()
|
dialog.destroy()
|
||||||
self.on_response(password1)
|
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:
|
class SingleMessageWindow:
|
||||||
"""
|
"""
|
||||||
SingleMessageWindow can send or show a received singled message depending on
|
SingleMessageWindow can send or show a received singled message depending on
|
||||||
|
|
|
@ -920,3 +920,11 @@ def pango_to_css_weight(number):
|
||||||
if number > 900:
|
if number > 900:
|
||||||
return 900
|
return 900
|
||||||
return int(math.ceil(number / 100.0)) * 100
|
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
|
@staticmethod
|
||||||
def handle_event_connection_lost(obj):
|
def handle_event_connection_lost(obj):
|
||||||
# ('CONNECTION_LOST', account, [title, text])
|
# ('CONNECTION_LOST', account, [title, text])
|
||||||
path = gtkgui_helpers.get_icon_path('gajim-connection_lost', 48)
|
|
||||||
account = obj.conn.name
|
account = obj.conn.name
|
||||||
notify.popup(_('Connection Failed'), account, account,
|
app.notification.popup(
|
||||||
'connection-lost', path, obj.title, obj.msg)
|
_('Connection Failed'), account, account,
|
||||||
|
'connection-lost', 'gajim-connection_lost', obj.title, obj.msg)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def unblock_signed_in_notifications(account):
|
def unblock_signed_in_notifications(account):
|
||||||
|
@ -509,11 +509,10 @@ class Interface:
|
||||||
self.add_event(account, obj.jid, event)
|
self.add_event(account, obj.jid, event)
|
||||||
|
|
||||||
if helpers.allow_showing_notification(account):
|
if helpers.allow_showing_notification(account):
|
||||||
path = gtkgui_helpers.get_icon_path('gajim-subscription_request',
|
|
||||||
48)
|
|
||||||
event_type = _('Subscription request')
|
event_type = _('Subscription request')
|
||||||
notify.popup(event_type, obj.jid, account, 'subscription_request',
|
app.notification.popup(
|
||||||
path, event_type, obj.jid)
|
event_type, obj.jid, account, 'subscription_request',
|
||||||
|
'gajim-subscription_request', event_type, obj.jid)
|
||||||
|
|
||||||
def handle_event_subscribed_presence(self, obj):
|
def handle_event_subscribed_presence(self, obj):
|
||||||
#('SUBSCRIBED', account, (jid, resource))
|
#('SUBSCRIBED', account, (jid, resource))
|
||||||
|
@ -567,9 +566,10 @@ class Interface:
|
||||||
self.add_event(account, obj.jid, event)
|
self.add_event(account, obj.jid, event)
|
||||||
|
|
||||||
if helpers.allow_showing_notification(account):
|
if helpers.allow_showing_notification(account):
|
||||||
path = gtkgui_helpers.get_icon_path('gajim-unsubscribed', 48)
|
|
||||||
event_type = _('Unsubscribed')
|
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)
|
event_type, obj.jid)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -660,10 +660,10 @@ class Interface:
|
||||||
self.add_event(account, obj.jid_from, event)
|
self.add_event(account, obj.jid_from, event)
|
||||||
|
|
||||||
if helpers.allow_showing_notification(account):
|
if helpers.allow_showing_notification(account):
|
||||||
path = gtkgui_helpers.get_icon_path('gajim-gc_invitation', 48)
|
|
||||||
event_type = _('Groupchat Invitation')
|
event_type = _('Groupchat Invitation')
|
||||||
notify.popup(event_type, obj.jid_from, account, 'gc-invitation',
|
app.notification.popup(
|
||||||
path, event_type, obj.room_jid)
|
event_type, obj.jid_from, account, 'gc-invitation',
|
||||||
|
'gajim-gc_invitation', event_type, obj.room_jid)
|
||||||
|
|
||||||
def forget_gpg_passphrase(self, keyid):
|
def forget_gpg_passphrase(self, keyid):
|
||||||
if keyid in self.gpg_passphrase:
|
if keyid in self.gpg_passphrase:
|
||||||
|
@ -680,9 +680,9 @@ class Interface:
|
||||||
'key.')
|
'key.')
|
||||||
dialogs.WarningDialog(_('Wrong passphrase'), sectext)
|
dialogs.WarningDialog(_('Wrong passphrase'), sectext)
|
||||||
else:
|
else:
|
||||||
path = gtkgui_helpers.get_icon_path('gtk-dialog-warning', 48)
|
|
||||||
account = obj.conn.name
|
account = obj.conn.name
|
||||||
notify.popup('warning', account, account, '', path,
|
app.notification.popup(
|
||||||
|
'warning', account, account, '', 'dialog-warning',
|
||||||
_('Wrong OpenPGP passphrase'),
|
_('Wrong OpenPGP passphrase'),
|
||||||
_('You are currently connected without your OpenPGP key.'))
|
_('You are currently connected without your OpenPGP key.'))
|
||||||
self.forget_gpg_passphrase(obj.keyID)
|
self.forget_gpg_passphrase(obj.keyID)
|
||||||
|
@ -858,9 +858,10 @@ class Interface:
|
||||||
self.add_event(account, jid, event)
|
self.add_event(account, jid, event)
|
||||||
|
|
||||||
if helpers.allow_showing_notification(account):
|
if helpers.allow_showing_notification(account):
|
||||||
path = gtkgui_helpers.get_icon_path('gajim-ft_error', 48)
|
|
||||||
event_type = _('File Transfer Error')
|
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)
|
event_type, file_props.name)
|
||||||
|
|
||||||
def handle_event_file_request_error(self, obj):
|
def handle_event_file_request_error(self, obj):
|
||||||
|
@ -888,9 +889,10 @@ class Interface:
|
||||||
|
|
||||||
if helpers.allow_showing_notification(obj.conn.name):
|
if helpers.allow_showing_notification(obj.conn.name):
|
||||||
# check if we should be notified
|
# check if we should be notified
|
||||||
path = gtkgui_helpers.get_icon_path('gajim-ft_error', 48)
|
|
||||||
event_type = _('File Transfer Error')
|
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)
|
title=event_type, text=obj.file_props.name)
|
||||||
|
|
||||||
def handle_event_file_request(self, obj):
|
def handle_event_file_request(self, obj):
|
||||||
|
@ -921,12 +923,12 @@ class Interface:
|
||||||
event = events.FileRequestEvent(obj.file_props)
|
event = events.FileRequestEvent(obj.file_props)
|
||||||
self.add_event(account, obj.jid, event)
|
self.add_event(account, obj.jid, event)
|
||||||
if helpers.allow_showing_notification(account):
|
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(
|
txt = _('%s wants to send you a file.') % app.get_name_from_jid(
|
||||||
account, obj.jid)
|
account, obj.jid)
|
||||||
event_type = _('File Transfer Request')
|
event_type = _('File Transfer Request')
|
||||||
notify.popup(event_type, obj.jid, account, 'file-request',
|
app.notification.popup(
|
||||||
path_to_image=path, title=event_type, text=txt)
|
event_type, obj.jid, account, 'file-request',
|
||||||
|
icon_name='gajim-ft_request', title=event_type, text=txt)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def handle_event_file_error(title, message):
|
def handle_event_file_error(title, message):
|
||||||
|
@ -1060,15 +1062,15 @@ class Interface:
|
||||||
if event_type == _('File Transfer Completed'):
|
if event_type == _('File Transfer Completed'):
|
||||||
txt = _('%(filename)s received from %(name)s.')\
|
txt = _('%(filename)s received from %(name)s.')\
|
||||||
% {'filename': filename, 'name': name}
|
% {'filename': filename, 'name': name}
|
||||||
img_name = 'gajim-ft_done'
|
icon_name = 'gajim-ft_done'
|
||||||
elif event_type == _('File Transfer Stopped'):
|
elif event_type == _('File Transfer Stopped'):
|
||||||
txt = _('File transfer of %(filename)s from %(name)s '
|
txt = _('File transfer of %(filename)s from %(name)s '
|
||||||
'stopped.') % {'filename': filename, 'name': name}
|
'stopped.') % {'filename': filename, 'name': name}
|
||||||
img_name = 'gajim-ft_stopped'
|
icon_name = 'gajim-ft_stopped'
|
||||||
else: # ft hash error
|
else: # ft hash error
|
||||||
txt = _('File transfer of %(filename)s from %(name)s '
|
txt = _('File transfer of %(filename)s from %(name)s '
|
||||||
'failed.') % {'filename': filename, 'name': name}
|
'failed.') % {'filename': filename, 'name': name}
|
||||||
img_name = 'gajim-ft_stopped'
|
icon_name = 'gajim-ft_stopped'
|
||||||
else:
|
else:
|
||||||
receiver = file_props.receiver
|
receiver = file_props.receiver
|
||||||
if hasattr(receiver, 'jid'):
|
if hasattr(receiver, 'jid'):
|
||||||
|
@ -1081,27 +1083,27 @@ class Interface:
|
||||||
if event_type == _('File Transfer Completed'):
|
if event_type == _('File Transfer Completed'):
|
||||||
txt = _('You successfully sent %(filename)s to %(name)s.')\
|
txt = _('You successfully sent %(filename)s to %(name)s.')\
|
||||||
% {'filename': filename, 'name': name}
|
% {'filename': filename, 'name': name}
|
||||||
img_name = 'gajim-ft_done'
|
icon_name = 'gajim-ft_done'
|
||||||
elif event_type == _('File Transfer Stopped'):
|
elif event_type == _('File Transfer Stopped'):
|
||||||
txt = _('File transfer of %(filename)s to %(name)s '
|
txt = _('File transfer of %(filename)s to %(name)s '
|
||||||
'stopped.') % {'filename': filename, 'name': name}
|
'stopped.') % {'filename': filename, 'name': name}
|
||||||
img_name = 'gajim-ft_stopped'
|
icon_name = 'gajim-ft_stopped'
|
||||||
else: # ft hash error
|
else: # ft hash error
|
||||||
txt = _('File transfer of %(filename)s to %(name)s '
|
txt = _('File transfer of %(filename)s to %(name)s '
|
||||||
'failed.') % {'filename': filename, 'name': name}
|
'failed.') % {'filename': filename, 'name': name}
|
||||||
img_name = 'gajim-ft_stopped'
|
icon_name = 'gajim-ft_stopped'
|
||||||
path = gtkgui_helpers.get_icon_path(img_name, 48)
|
|
||||||
else:
|
else:
|
||||||
txt = ''
|
txt = ''
|
||||||
path = ''
|
icon_name = None
|
||||||
|
|
||||||
if app.config.get('notify_on_file_complete') and \
|
if app.config.get('notify_on_file_complete') and \
|
||||||
(app.config.get('autopopupaway') or \
|
(app.config.get('autopopupaway') or \
|
||||||
app.connections[account].connected in (2, 3)):
|
app.connections[account].connected in (2, 3)):
|
||||||
# we want to be notified and we are online/chat or we don't mind
|
# we want to be notified and we are online/chat or we don't mind
|
||||||
# bugged when away/na/busy
|
# bugged when away/na/busy
|
||||||
notify.popup(event_type, jid, account, msg_type, path_to_image=path,
|
app.notification.popup(
|
||||||
title=event_type, text=txt)
|
event_type, jid, account, msg_type,
|
||||||
|
icon_name=icon_name, title=event_type, text=txt)
|
||||||
|
|
||||||
def handle_event_signed_in(self, obj):
|
def handle_event_signed_in(self, obj):
|
||||||
"""
|
"""
|
||||||
|
@ -1278,10 +1280,10 @@ class Interface:
|
||||||
# TODO: we should use another pixmap ;-)
|
# TODO: we should use another pixmap ;-)
|
||||||
txt = _('%s wants to start a voice chat.') % \
|
txt = _('%s wants to start a voice chat.') % \
|
||||||
app.get_name_from_jid(account, obj.fjid)
|
app.get_name_from_jid(account, obj.fjid)
|
||||||
path = gtkgui_helpers.get_icon_path('gajim-mic_active', 48)
|
|
||||||
event_type = _('Voice Chat Request')
|
event_type = _('Voice Chat Request')
|
||||||
notify.popup(event_type, obj.fjid, account, 'jingle-incoming',
|
app.notification.popup(
|
||||||
path_to_image=path, title=event_type, text=txt)
|
event_type, obj.fjid, account, 'jingle-incoming',
|
||||||
|
icon_name='gajim-mic_active', title=event_type, text=txt)
|
||||||
|
|
||||||
def handle_event_jingle_connected(self, obj):
|
def handle_event_jingle_connected(self, obj):
|
||||||
# ('JINGLE_CONNECTED', account, (peerjid, sid, media))
|
# ('JINGLE_CONNECTED', account, (peerjid, sid, media))
|
||||||
|
@ -1652,28 +1654,7 @@ class Interface:
|
||||||
elif type_ in ('printed_chat', 'chat', ''):
|
elif type_ in ('printed_chat', 'chat', ''):
|
||||||
# '' is for log in/out notifications
|
# '' is for log in/out notifications
|
||||||
|
|
||||||
if type_ != '':
|
ctrl = self.msg_win_mgr.search_control(jid, account, resource)
|
||||||
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)
|
|
||||||
|
|
||||||
if not ctrl:
|
if not ctrl:
|
||||||
highest_contact = app.contacts.\
|
highest_contact = app.contacts.\
|
||||||
|
@ -1692,33 +1673,20 @@ class Interface:
|
||||||
if not contact:
|
if not contact:
|
||||||
contact = highest_contact
|
contact = highest_contact
|
||||||
|
|
||||||
ctrl = self.new_chat(contact, account, resource=resource,
|
ctrl = self.new_chat(contact, account, resource=resource)
|
||||||
session=session)
|
|
||||||
|
|
||||||
app.last_message_time[account][jid] = 0 # long time ago
|
app.last_message_time[account][jid] = 0 # long time ago
|
||||||
|
|
||||||
w = ctrl.parent_win
|
w = ctrl.parent_win
|
||||||
elif type_ in ('printed_pm', 'pm'):
|
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 = self.msg_win_mgr.get_control(fjid, account)
|
||||||
ctrl = event.control
|
|
||||||
elif type_ == 'pm':
|
|
||||||
session = event.session
|
|
||||||
|
|
||||||
if session and session.control:
|
if not ctrl:
|
||||||
ctrl = session.control
|
|
||||||
elif not ctrl:
|
|
||||||
room_jid = jid
|
room_jid = jid
|
||||||
nick = resource
|
nick = resource
|
||||||
gc_contact = app.contacts.get_gc_contact(account, room_jid,
|
gc_contact = app.contacts.get_gc_contact(
|
||||||
nick)
|
account, room_jid, nick)
|
||||||
if gc_contact:
|
if gc_contact:
|
||||||
show = gc_contact.show
|
show = gc_contact.show
|
||||||
else:
|
else:
|
||||||
|
@ -1727,12 +1695,7 @@ class Interface:
|
||||||
room_jid=room_jid, account=account, name=nick,
|
room_jid=room_jid, account=account, name=nick,
|
||||||
show=show)
|
show=show)
|
||||||
|
|
||||||
if not session:
|
ctrl = self.new_private_chat(gc_contact, account)
|
||||||
session = app.connections[account].make_new_session(
|
|
||||||
fjid, None, type_='pm')
|
|
||||||
|
|
||||||
self.new_private_chat(gc_contact, account, session=session)
|
|
||||||
ctrl = session.control
|
|
||||||
|
|
||||||
w = ctrl.parent_win
|
w = ctrl.parent_win
|
||||||
elif type_ in ('normal', 'file-request', 'file-request-error',
|
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 Sebastian Estienne
|
# Copyright (C) 2005-2007 Nikos Kouremenos <kourem AT gmail.com>
|
||||||
## Copyright (C) 2005-2006 Andrew Sayman <lorien420 AT myrealbox.com>
|
# Copyright (C) 2005-2014 Yann Leboulanger <asterix AT lagaule.org>
|
||||||
## Copyright (C) 2005-2007 Nikos Kouremenos <kourem AT gmail.com>
|
# Copyright (C) 2006 Travis Shirk <travis AT pobox.com>
|
||||||
## Copyright (C) 2005-2014 Yann Leboulanger <asterix AT lagaule.org>
|
# Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org>
|
||||||
## Copyright (C) 2006 Travis Shirk <travis AT pobox.com>
|
# Copyright (C) 2007 Julien Pivotto <roidelapluie AT gmail.com>
|
||||||
## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org>
|
# Stephan Erb <steve-e AT h3c.de>
|
||||||
## Copyright (C) 2007 Julien Pivotto <roidelapluie AT gmail.com>
|
# Copyright (C) 2008 Brendan Taylor <whateley AT gmail.com>
|
||||||
## Stephan Erb <steve-e AT h3c.de>
|
# Jonathan Schleifer <js-gajim AT webkeks.org>
|
||||||
## Copyright (C) 2008 Brendan Taylor <whateley AT gmail.com>
|
# Copyright (C) 2018 Philipp Hörist <philipp AT hoerist.com>
|
||||||
## Jonathan Schleifer <js-gajim AT webkeks.org>
|
#
|
||||||
##
|
# This file is part of Gajim.
|
||||||
## This file is part of Gajim.
|
#
|
||||||
##
|
# Gajim is free software; you can redistribute it and/or modify
|
||||||
## Gajim is free software; you can redistribute it and/or modify
|
# it under the terms of the GNU General Public License as published
|
||||||
## it under the terms of the GNU General Public License as published
|
# by the Free Software Foundation; version 3 only.
|
||||||
## by the Free Software Foundation; version 3 only.
|
#
|
||||||
##
|
# Gajim is distributed in the hope that it will be useful,
|
||||||
## Gajim is distributed in the hope that it will be useful,
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
# GNU General Public License for more details.
|
||||||
## GNU General Public License for more details.
|
#
|
||||||
##
|
# You should have received a copy of the GNU General Public License
|
||||||
## You should have received a copy of the GNU General Public License
|
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
||||||
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
##
|
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from gajim.dialogs import PopupNotificationWindow
|
|
||||||
from gi.repository import GLib
|
from gi.repository import GLib
|
||||||
from gi.repository import Gio
|
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 app
|
||||||
from gajim.common import helpers
|
from gajim.common import helpers
|
||||||
from gajim.common import ged
|
from gajim.common import ged
|
||||||
|
|
||||||
|
|
||||||
def get_show_in_roster(event, account, contact, session=None):
|
def get_show_in_roster(event, account, contact, session=None):
|
||||||
"""
|
"""
|
||||||
Return True if this event must be shown in roster, else False
|
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 False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def get_show_in_systray(event, account, contact, type_=None):
|
def get_show_in_systray(event, account, contact, type_=None):
|
||||||
"""
|
"""
|
||||||
Return True if this event must be shown in systray, else False
|
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',
|
notify = app.config.get('notify_on_all_muc_messages')
|
||||||
contact.jid, 'notify_on_all_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
|
# it's not an highlighted message, don't show in systray
|
||||||
return False
|
return False
|
||||||
return app.config.get('trayicon_notification_on_events')
|
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:
|
class Notification:
|
||||||
"""
|
"""
|
||||||
Handle notifications
|
Handle notifications
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
app.ged.register_event_handler('notification', ged.GUI2,
|
app.ged.register_event_handler(
|
||||||
self._nec_notification)
|
'notification', ged.GUI2, self._nec_notification)
|
||||||
|
|
||||||
def _nec_notification(self, obj):
|
def _nec_notification(self, obj):
|
||||||
if obj.do_popup:
|
if obj.do_popup:
|
||||||
if obj.popup_image:
|
icon_name = self._get_icon_name(obj)
|
||||||
icon_path = gtkgui_helpers.get_icon_path(obj.popup_image, 48)
|
self.popup(obj.popup_event_type, obj.jid, obj.conn.name,
|
||||||
if icon_path:
|
obj.popup_msg_type, icon_name=icon_name,
|
||||||
image_path = icon_path
|
title=obj.popup_title, text=obj.popup_text,
|
||||||
elif obj.popup_image_path:
|
timeout=obj.popup_timeout)
|
||||||
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)
|
|
||||||
|
|
||||||
if obj.do_sound:
|
if obj.do_sound:
|
||||||
if obj.sound_file:
|
if obj.sound_file:
|
||||||
|
@ -145,3 +92,190 @@ class Notification:
|
||||||
helpers.exec_command(obj.command, use_shell=True)
|
helpers.exec_command(obj.command, use_shell=True)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
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…
Reference in New Issue