Merge pull request '1047' into v0.6
- notifications moved into plugins - improvements in setup.py - remove obsolete imports
This commit is contained in:
commit
4f75dcb9be
40
setup.py
40
setup.py
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import shutil
|
||||||
try:
|
try:
|
||||||
from setuptools import setup, Extension
|
from setuptools import setup, Extension
|
||||||
from setuptools.command.install import install
|
from setuptools.command.install import install
|
||||||
|
@ -167,6 +168,7 @@ def prereqToPackages():
|
||||||
print packageName[package].get('description')
|
print packageName[package].get('description')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def compilerToPackages():
|
def compilerToPackages():
|
||||||
if not detectOS() in compiling:
|
if not detectOS() in compiling:
|
||||||
return
|
return
|
||||||
|
@ -202,6 +204,14 @@ class InstallCmd(install):
|
||||||
except (EOFError, NameError):
|
except (EOFError, NameError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# prepare icons directories
|
||||||
|
os.makedirs('desktop/icons/scalable')
|
||||||
|
shutil.copyfile(
|
||||||
|
'desktop/can-icon.svg', 'desktop/icons/scalable/pybitmessage.svg')
|
||||||
|
os.makedirs('desktop/icons/24x24')
|
||||||
|
shutil.copyfile(
|
||||||
|
'desktop/icon24.png', 'desktop/icons/24x24/pybitmessage.png')
|
||||||
|
|
||||||
return install.run(self)
|
return install.run(self)
|
||||||
|
|
||||||
|
|
||||||
|
@ -253,8 +263,11 @@ if __name__ == "__main__":
|
||||||
#keywords='',
|
#keywords='',
|
||||||
install_requires=installRequires,
|
install_requires=installRequires,
|
||||||
extras_require={
|
extras_require={
|
||||||
|
'gir': ['pygobject'],
|
||||||
'qrcode': ['qrcode'],
|
'qrcode': ['qrcode'],
|
||||||
'pyopencl': ['pyopencl']
|
'pyopencl': ['pyopencl'],
|
||||||
|
'notify2': ['notify2'],
|
||||||
|
'sound:platform_system=="Windows"': ['winsound']
|
||||||
},
|
},
|
||||||
classifiers=[
|
classifiers=[
|
||||||
"License :: OSI Approved :: MIT License"
|
"License :: OSI Approved :: MIT License"
|
||||||
|
@ -271,13 +284,36 @@ if __name__ == "__main__":
|
||||||
'translations/*.ts', 'translations/*.qm',
|
'translations/*.ts', 'translations/*.qm',
|
||||||
'images/*.png', 'images/*.ico', 'images/*.icns'
|
'images/*.png', 'images/*.ico', 'images/*.icns'
|
||||||
]},
|
]},
|
||||||
|
data_files=[
|
||||||
|
('share/applications/',
|
||||||
|
['desktop/pybitmessage.desktop']),
|
||||||
|
('share/icons/hicolor/scalable/apps/',
|
||||||
|
['desktop/icons/scalable/pybitmessage.svg']),
|
||||||
|
('share/icons/hicolor/24x24/apps/',
|
||||||
|
['desktop/icons/24x24/pybitmessage.png'])
|
||||||
|
],
|
||||||
ext_modules=[bitmsghash],
|
ext_modules=[bitmsghash],
|
||||||
zip_safe=False,
|
zip_safe=False,
|
||||||
entry_points={
|
entry_points={
|
||||||
'gui.menu': [
|
'bitmessage.gui.menu': [
|
||||||
'popMenuYourIdentities.qrcode = '
|
'popMenuYourIdentities.qrcode = '
|
||||||
'pybitmessage.plugins.qrcodeui [qrcode]'
|
'pybitmessage.plugins.qrcodeui [qrcode]'
|
||||||
],
|
],
|
||||||
|
'bitmessage.notification.message': [
|
||||||
|
'notify2 = pybitmessage.plugins.notification_notify2'
|
||||||
|
'[gir, notify2]'
|
||||||
|
],
|
||||||
|
'bitmessage.notification.sound': [
|
||||||
|
'theme.canberra = pybitmessage.plugins.sound_canberra',
|
||||||
|
'file.gstreamer = pybitmessage.plugins.sound_gstreamer'
|
||||||
|
'[gir]',
|
||||||
|
'file.fallback = pybitmessage.plugins.sound_playfile'
|
||||||
|
'[sound]'
|
||||||
|
],
|
||||||
|
'bitmessage.indicator': [
|
||||||
|
'libmessaging ='
|
||||||
|
'pybitmessage.plugins.indicator_libmessaging [gir]'
|
||||||
|
],
|
||||||
# 'console_scripts': [
|
# 'console_scripts': [
|
||||||
# 'pybitmessage = pybitmessage.bitmessagemain:main'
|
# 'pybitmessage = pybitmessage.bitmessagemain:main'
|
||||||
# ]
|
# ]
|
||||||
|
|
|
@ -1,21 +1,10 @@
|
||||||
from debug import logger
|
from debug import logger
|
||||||
withMessagingMenu = False
|
|
||||||
try:
|
|
||||||
import gi
|
|
||||||
gi.require_version('MessagingMenu', '1.0')
|
|
||||||
from gi.repository import MessagingMenu
|
|
||||||
gi.require_version('Notify', '0.7')
|
|
||||||
from gi.repository import Notify
|
|
||||||
withMessagingMenu = True
|
|
||||||
except (ImportError, ValueError):
|
|
||||||
MessagingMenu = None
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from PyQt4 import QtCore, QtGui
|
from PyQt4 import QtCore, QtGui
|
||||||
from PyQt4.QtCore import *
|
from PyQt4.QtCore import *
|
||||||
from PyQt4.QtGui import *
|
from PyQt4.QtGui import *
|
||||||
from PyQt4.QtNetwork import QLocalSocket, QLocalServer
|
from PyQt4.QtNetwork import QLocalSocket, QLocalServer
|
||||||
|
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
logmsg = 'PyBitmessage requires PyQt unless you want to run it as a daemon and interact with it using the API. You can download it from http://www.riverbankcomputing.com/software/pyqt/download or by searching Google for \'PyQt Download\' (without quotes).'
|
logmsg = 'PyBitmessage requires PyQt unless you want to run it as a daemon and interact with it using the API. You can download it from http://www.riverbankcomputing.com/software/pyqt/download or by searching Google for \'PyQt Download\' (without quotes).'
|
||||||
logger.critical(logmsg, exc_info=True)
|
logger.critical(logmsg, exc_info=True)
|
||||||
|
@ -32,7 +21,7 @@ import shared
|
||||||
from bitmessageui import *
|
from bitmessageui import *
|
||||||
from bmconfigparser import BMConfigParser
|
from bmconfigparser import BMConfigParser
|
||||||
import defaults
|
import defaults
|
||||||
from namecoin import namecoinConnection, ensureNamecoinOptions
|
from namecoin import namecoinConnection
|
||||||
from newaddressdialog import *
|
from newaddressdialog import *
|
||||||
from newaddresswizard import *
|
from newaddresswizard import *
|
||||||
from messageview import MessageView
|
from messageview import MessageView
|
||||||
|
@ -53,31 +42,26 @@ from iconglossary import *
|
||||||
from connect import *
|
from connect import *
|
||||||
import locale
|
import locale
|
||||||
import sys
|
import sys
|
||||||
from time import strftime, localtime, gmtime
|
|
||||||
import time
|
import time
|
||||||
import os
|
import os
|
||||||
import hashlib
|
import hashlib
|
||||||
from pyelliptic.openssl import OpenSSL
|
from pyelliptic.openssl import OpenSSL
|
||||||
import platform
|
|
||||||
import textwrap
|
import textwrap
|
||||||
import debug
|
import debug
|
||||||
import random
|
import random
|
||||||
import subprocess
|
|
||||||
import string
|
import string
|
||||||
import datetime
|
from datetime import datetime, timedelta
|
||||||
from helper_sql import *
|
from helper_sql import *
|
||||||
import helper_search
|
import helper_search
|
||||||
import l10n
|
import l10n
|
||||||
import openclpow
|
import openclpow
|
||||||
import types
|
from utils import str_broadcast_subscribers, avatarize
|
||||||
from utils import *
|
|
||||||
from collections import OrderedDict
|
|
||||||
from account import *
|
from account import *
|
||||||
from class_objectHashHolder import objectHashHolder
|
|
||||||
from class_singleWorker import singleWorker
|
|
||||||
from dialogs import AddAddressDialog
|
from dialogs import AddAddressDialog
|
||||||
from helper_generic import powQueueSize
|
from helper_generic import powQueueSize
|
||||||
from inventory import Inventory, PendingDownloadQueue, PendingUpload, PendingUploadDeadlineException
|
from inventory import (
|
||||||
|
Inventory, PendingDownloadQueue, PendingUpload,
|
||||||
|
PendingUploadDeadlineException)
|
||||||
import knownnodes
|
import knownnodes
|
||||||
import paths
|
import paths
|
||||||
from proofofwork import getPowType
|
from proofofwork import getPowType
|
||||||
|
@ -87,9 +71,10 @@ import state
|
||||||
from statusbar import BMStatusBar
|
from statusbar import BMStatusBar
|
||||||
import throttle
|
import throttle
|
||||||
from version import softwareVersion
|
from version import softwareVersion
|
||||||
|
import sound
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from plugins.plugin import get_plugins
|
from plugins.plugin import get_plugin, get_plugins
|
||||||
except ImportError:
|
except ImportError:
|
||||||
get_plugins = False
|
get_plugins = False
|
||||||
|
|
||||||
|
@ -142,24 +127,15 @@ def change_translation(newlocale):
|
||||||
except:
|
except:
|
||||||
logger.error("Failed to set locale to %s", lang, exc_info=True)
|
logger.error("Failed to set locale to %s", lang, exc_info=True)
|
||||||
|
|
||||||
|
|
||||||
class MyForm(settingsmixin.SMainWindow):
|
class MyForm(settingsmixin.SMainWindow):
|
||||||
|
|
||||||
# sound type constants
|
|
||||||
SOUND_NONE = 0
|
|
||||||
SOUND_KNOWN = 1
|
|
||||||
SOUND_UNKNOWN = 2
|
|
||||||
SOUND_CONNECTED = 3
|
|
||||||
SOUND_DISCONNECTED = 4
|
|
||||||
SOUND_CONNECTION_GREEN = 5
|
|
||||||
|
|
||||||
# the last time that a message arrival sound was played
|
# the last time that a message arrival sound was played
|
||||||
lastSoundTime = datetime.datetime.now() - datetime.timedelta(days=1)
|
lastSoundTime = datetime.now() - timedelta(days=1)
|
||||||
|
|
||||||
# the maximum frequency of message sounds in seconds
|
# the maximum frequency of message sounds in seconds
|
||||||
maxSoundFrequencySec = 60
|
maxSoundFrequencySec = 60
|
||||||
|
|
||||||
str_chan = '[chan]'
|
|
||||||
|
|
||||||
REPLY_TYPE_SENDER = 0
|
REPLY_TYPE_SENDER = 0
|
||||||
REPLY_TYPE_CHAN = 1
|
REPLY_TYPE_CHAN = 1
|
||||||
|
|
||||||
|
@ -349,6 +325,10 @@ class MyForm(settingsmixin.SMainWindow):
|
||||||
_translate(
|
_translate(
|
||||||
"MainWindow", "Set avatar..."),
|
"MainWindow", "Set avatar..."),
|
||||||
self.on_action_AddressBookSetAvatar)
|
self.on_action_AddressBookSetAvatar)
|
||||||
|
self.actionAddressBookSetSound = \
|
||||||
|
self.ui.addressBookContextMenuToolbar.addAction(
|
||||||
|
_translate("MainWindow", "Set notification sound..."),
|
||||||
|
self.on_action_AddressBookSetSound)
|
||||||
self.actionAddressBookNew = self.ui.addressBookContextMenuToolbar.addAction(
|
self.actionAddressBookNew = self.ui.addressBookContextMenuToolbar.addAction(
|
||||||
_translate(
|
_translate(
|
||||||
"MainWindow", "Add New Address"), self.on_action_AddressBookNew)
|
"MainWindow", "Add New Address"), self.on_action_AddressBookNew)
|
||||||
|
@ -868,14 +848,6 @@ class MyForm(settingsmixin.SMainWindow):
|
||||||
self.raise_()
|
self.raise_()
|
||||||
self.activateWindow()
|
self.activateWindow()
|
||||||
|
|
||||||
# pointer to the application
|
|
||||||
# app = None
|
|
||||||
# The most recent message
|
|
||||||
newMessageItem = None
|
|
||||||
|
|
||||||
# The most recent broadcast
|
|
||||||
newBroadcastItem = None
|
|
||||||
|
|
||||||
# show the application window
|
# show the application window
|
||||||
def appIndicatorShow(self):
|
def appIndicatorShow(self):
|
||||||
if self.actionShow is None:
|
if self.actionShow is None:
|
||||||
|
@ -905,32 +877,19 @@ class MyForm(settingsmixin.SMainWindow):
|
||||||
self.appIndicatorShowOrHideWindow()"""
|
self.appIndicatorShowOrHideWindow()"""
|
||||||
|
|
||||||
# Show the program window and select inbox tab
|
# Show the program window and select inbox tab
|
||||||
def appIndicatorInbox(self, mm_app, source_id):
|
def appIndicatorInbox(self, item=None):
|
||||||
self.appIndicatorShow()
|
self.appIndicatorShow()
|
||||||
# select inbox
|
# select inbox
|
||||||
self.ui.tabWidget.setCurrentIndex(0)
|
self.ui.tabWidget.setCurrentIndex(0)
|
||||||
selectedItem = None
|
self.ui.treeWidgetYourIdentities.setCurrentItem(
|
||||||
if source_id == 'Subscriptions':
|
self.ui.treeWidgetYourIdentities.topLevelItem(0).child(0)
|
||||||
# select unread broadcast
|
)
|
||||||
if self.newBroadcastItem is not None:
|
|
||||||
selectedItem = self.newBroadcastItem
|
if item:
|
||||||
self.newBroadcastItem = None
|
self.ui.tableWidgetInbox.setCurrentItem(item)
|
||||||
else:
|
|
||||||
# select unread message
|
|
||||||
if self.newMessageItem is not None:
|
|
||||||
selectedItem = self.newMessageItem
|
|
||||||
self.newMessageItem = None
|
|
||||||
# make it the current item
|
|
||||||
if selectedItem is not None:
|
|
||||||
try:
|
|
||||||
self.ui.tableWidgetInbox.setCurrentItem(selectedItem)
|
|
||||||
except Exception:
|
|
||||||
self.ui.tableWidgetInbox.setCurrentCell(0, 0)
|
|
||||||
self.tableWidgetInboxItemClicked()
|
self.tableWidgetInboxItemClicked()
|
||||||
else:
|
else:
|
||||||
# just select the first item
|
|
||||||
self.ui.tableWidgetInbox.setCurrentCell(0, 0)
|
self.ui.tableWidgetInbox.setCurrentCell(0, 0)
|
||||||
self.tableWidgetInboxItemClicked()
|
|
||||||
|
|
||||||
# Show the program window and select send tab
|
# Show the program window and select send tab
|
||||||
def appIndicatorSend(self):
|
def appIndicatorSend(self):
|
||||||
|
@ -1228,262 +1187,136 @@ class MyForm(settingsmixin.SMainWindow):
|
||||||
self.tray.setContextMenu(m)
|
self.tray.setContextMenu(m)
|
||||||
self.tray.show()
|
self.tray.show()
|
||||||
|
|
||||||
# Ubuntu Messaging menu object
|
|
||||||
mmapp = None
|
|
||||||
|
|
||||||
# is the operating system Ubuntu?
|
|
||||||
def isUbuntu(self):
|
|
||||||
for entry in platform.uname():
|
|
||||||
if "Ubuntu" in entry:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
# When an unread inbox row is selected on then clear the messaging menu
|
|
||||||
def ubuntuMessagingMenuClear(self, inventoryHash):
|
|
||||||
# if this isn't ubuntu then don't do anything
|
|
||||||
if not self.isUbuntu():
|
|
||||||
return
|
|
||||||
|
|
||||||
# has messageing menu been installed
|
|
||||||
if not withMessagingMenu:
|
|
||||||
return
|
|
||||||
|
|
||||||
# if there are no items on the messaging menu then
|
|
||||||
# the subsequent query can be avoided
|
|
||||||
if not (self.mmapp.has_source("Subscriptions") or self.mmapp.has_source("Messages")):
|
|
||||||
return
|
|
||||||
|
|
||||||
queryreturn = sqlQuery(
|
|
||||||
'''SELECT toaddress, read FROM inbox WHERE msgid=?''', inventoryHash)
|
|
||||||
for row in queryreturn:
|
|
||||||
toAddress, read = row
|
|
||||||
if not read:
|
|
||||||
if toAddress == str_broadcast_subscribers:
|
|
||||||
if self.mmapp.has_source("Subscriptions"):
|
|
||||||
self.mmapp.remove_source("Subscriptions")
|
|
||||||
else:
|
|
||||||
if self.mmapp.has_source("Messages"):
|
|
||||||
self.mmapp.remove_source("Messages")
|
|
||||||
|
|
||||||
# returns the number of unread messages and subscriptions
|
# returns the number of unread messages and subscriptions
|
||||||
def getUnread(self):
|
def getUnread(self):
|
||||||
unreadMessages = 0
|
counters = [0, 0]
|
||||||
unreadSubscriptions = 0
|
|
||||||
|
|
||||||
queryreturn = sqlQuery(
|
queryreturn = sqlQuery('''
|
||||||
'''SELECT msgid, toaddress, read FROM inbox where folder='inbox' ''')
|
SELECT msgid, toaddress, read FROM inbox where folder='inbox'
|
||||||
for row in queryreturn:
|
''')
|
||||||
msgid, toAddress, read = row
|
for msgid, toAddress, read in queryreturn:
|
||||||
|
|
||||||
try:
|
|
||||||
if toAddress == str_broadcast_subscribers:
|
|
||||||
toLabel = str_broadcast_subscribers
|
|
||||||
else:
|
|
||||||
toLabel = BMConfigParser().get(toAddress, 'label')
|
|
||||||
except:
|
|
||||||
toLabel = ''
|
|
||||||
if toLabel == '':
|
|
||||||
toLabel = toAddress
|
|
||||||
|
|
||||||
if not read:
|
if not read:
|
||||||
if toLabel == str_broadcast_subscribers:
|
# increment the unread subscriptions if True (1)
|
||||||
# increment the unread subscriptions
|
# else messages (0)
|
||||||
unreadSubscriptions = unreadSubscriptions + 1
|
counters[toAddress == str_broadcast_subscribers] += 1
|
||||||
else:
|
|
||||||
# increment the unread messages
|
|
||||||
unreadMessages = unreadMessages + 1
|
|
||||||
return unreadMessages, unreadSubscriptions
|
|
||||||
|
|
||||||
# show the number of unread messages and subscriptions on the messaging
|
return counters
|
||||||
# menu
|
|
||||||
def ubuntuMessagingMenuUnread(self, drawAttention):
|
|
||||||
unreadMessages, unreadSubscriptions = self.getUnread()
|
|
||||||
# unread messages
|
|
||||||
if unreadMessages > 0:
|
|
||||||
self.mmapp.append_source(
|
|
||||||
"Messages", None, "Messages (" + str(unreadMessages) + ")")
|
|
||||||
if drawAttention:
|
|
||||||
self.mmapp.draw_attention("Messages")
|
|
||||||
|
|
||||||
# unread subscriptions
|
|
||||||
if unreadSubscriptions > 0:
|
|
||||||
self.mmapp.append_source("Subscriptions", None, "Subscriptions (" + str(
|
|
||||||
unreadSubscriptions) + ")")
|
|
||||||
if drawAttention:
|
|
||||||
self.mmapp.draw_attention("Subscriptions")
|
|
||||||
|
|
||||||
# initialise the Ubuntu messaging menu
|
|
||||||
def ubuntuMessagingMenuInit(self):
|
|
||||||
global withMessagingMenu
|
|
||||||
|
|
||||||
# if this isn't ubuntu then don't do anything
|
|
||||||
if not self.isUbuntu():
|
|
||||||
return
|
|
||||||
|
|
||||||
# has messageing menu been installed
|
|
||||||
if not withMessagingMenu:
|
|
||||||
logger.warning('WARNING: MessagingMenu is not available. Is libmessaging-menu-dev installed?')
|
|
||||||
return
|
|
||||||
|
|
||||||
# create the menu server
|
|
||||||
if withMessagingMenu:
|
|
||||||
try:
|
|
||||||
self.mmapp = MessagingMenu.App(
|
|
||||||
desktop_id='pybitmessage.desktop')
|
|
||||||
self.mmapp.register()
|
|
||||||
self.mmapp.connect('activate-source', self.appIndicatorInbox)
|
|
||||||
self.ubuntuMessagingMenuUnread(True)
|
|
||||||
except Exception:
|
|
||||||
withMessagingMenu = False
|
|
||||||
logger.warning('WARNING: messaging menu disabled')
|
|
||||||
|
|
||||||
# update the Ubuntu messaging menu
|
|
||||||
def ubuntuMessagingMenuUpdate(self, drawAttention, newItem, toLabel):
|
|
||||||
# if this isn't ubuntu then don't do anything
|
|
||||||
if not self.isUbuntu():
|
|
||||||
return
|
|
||||||
|
|
||||||
# has messageing menu been installed
|
|
||||||
if not withMessagingMenu:
|
|
||||||
logger.warning('WARNING: messaging menu disabled or libmessaging-menu-dev not installed')
|
|
||||||
return
|
|
||||||
|
|
||||||
# remember this item to that the messaging menu can find it
|
|
||||||
if toLabel == str_broadcast_subscribers:
|
|
||||||
self.newBroadcastItem = newItem
|
|
||||||
else:
|
|
||||||
self.newMessageItem = newItem
|
|
||||||
|
|
||||||
# Remove previous messages and subscriptions entries, then recreate them
|
|
||||||
# There might be a better way to do it than this
|
|
||||||
if self.mmapp.has_source("Messages"):
|
|
||||||
self.mmapp.remove_source("Messages")
|
|
||||||
|
|
||||||
if self.mmapp.has_source("Subscriptions"):
|
|
||||||
self.mmapp.remove_source("Subscriptions")
|
|
||||||
|
|
||||||
# update the menu entries
|
|
||||||
self.ubuntuMessagingMenuUnread(drawAttention)
|
|
||||||
|
|
||||||
# returns true if the given sound category is a connection sound
|
|
||||||
# rather than a received message sound
|
|
||||||
def isConnectionSound(self, category):
|
|
||||||
if (category is self.SOUND_CONNECTED or
|
|
||||||
category is self.SOUND_DISCONNECTED or
|
|
||||||
category is self.SOUND_CONNECTION_GREEN):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
# play a sound
|
# play a sound
|
||||||
def playSound(self, category, label):
|
def playSound(self, category, label):
|
||||||
# filename of the sound to be played
|
# filename of the sound to be played
|
||||||
soundFilename = None
|
soundFilename = None
|
||||||
|
|
||||||
# whether to play a sound or not
|
def _choose_ext(basename):
|
||||||
play = True
|
for ext in sound.extensions:
|
||||||
|
if os.path.isfile(os.extsep.join([basename, ext])):
|
||||||
|
return os.extsep + ext
|
||||||
|
|
||||||
# if the address had a known label in the address book
|
# if the address had a known label in the address book
|
||||||
if label is not None:
|
if label:
|
||||||
# Does a sound file exist for this particular contact?
|
# Does a sound file exist for this particular contact?
|
||||||
if (os.path.isfile(state.appdata + 'sounds/' + label + '.wav') or
|
|
||||||
os.path.isfile(state.appdata + 'sounds/' + label + '.mp3')):
|
|
||||||
soundFilename = state.appdata + 'sounds/' + label
|
soundFilename = state.appdata + 'sounds/' + label
|
||||||
|
ext = _choose_ext(soundFilename)
|
||||||
|
if not ext:
|
||||||
|
category = sound.SOUND_KNOWN
|
||||||
|
soundFilename = None
|
||||||
|
|
||||||
|
if soundFilename is None:
|
||||||
# Avoid making sounds more frequently than the threshold.
|
# Avoid making sounds more frequently than the threshold.
|
||||||
# This suppresses playing sounds repeatedly when there
|
# This suppresses playing sounds repeatedly when there
|
||||||
# are many new messages
|
# are many new messages
|
||||||
if (soundFilename is None and
|
if not sound.is_connection_sound(category):
|
||||||
not self.isConnectionSound(category)):
|
|
||||||
# elapsed time since the last sound was played
|
# elapsed time since the last sound was played
|
||||||
dt = datetime.datetime.now() - self.lastSoundTime
|
dt = datetime.now() - self.lastSoundTime
|
||||||
# suppress sounds which are more frequent than the threshold
|
# suppress sounds which are more frequent than the threshold
|
||||||
if dt.total_seconds() < self.maxSoundFrequencySec:
|
if dt.total_seconds() < self.maxSoundFrequencySec:
|
||||||
play = False
|
return
|
||||||
|
|
||||||
if soundFilename is None:
|
|
||||||
# the sound is for an address which exists in the address book
|
# the sound is for an address which exists in the address book
|
||||||
if category is self.SOUND_KNOWN:
|
if category is sound.SOUND_KNOWN:
|
||||||
soundFilename = state.appdata + 'sounds/known'
|
soundFilename = state.appdata + 'sounds/known'
|
||||||
# the sound is for an unknown address
|
# the sound is for an unknown address
|
||||||
elif category is self.SOUND_UNKNOWN:
|
elif category is sound.SOUND_UNKNOWN:
|
||||||
soundFilename = state.appdata + 'sounds/unknown'
|
soundFilename = state.appdata + 'sounds/unknown'
|
||||||
# initial connection sound
|
# initial connection sound
|
||||||
elif category is self.SOUND_CONNECTED:
|
elif category is sound.SOUND_CONNECTED:
|
||||||
soundFilename = state.appdata + 'sounds/connected'
|
soundFilename = state.appdata + 'sounds/connected'
|
||||||
# disconnected sound
|
# disconnected sound
|
||||||
elif category is self.SOUND_DISCONNECTED:
|
elif category is sound.SOUND_DISCONNECTED:
|
||||||
soundFilename = state.appdata + 'sounds/disconnected'
|
soundFilename = state.appdata + 'sounds/disconnected'
|
||||||
# sound when the connection status becomes green
|
# sound when the connection status becomes green
|
||||||
elif category is self.SOUND_CONNECTION_GREEN:
|
elif category is sound.SOUND_CONNECTION_GREEN:
|
||||||
soundFilename = state.appdata + 'sounds/green'
|
soundFilename = state.appdata + 'sounds/green'
|
||||||
|
|
||||||
if soundFilename is not None and play is True:
|
if soundFilename is None:
|
||||||
if not self.isConnectionSound(category):
|
logger.warning("Probably wrong category number in playSound()")
|
||||||
|
return
|
||||||
|
|
||||||
|
if not sound.is_connection_sound(category):
|
||||||
# record the last time that a received message sound was played
|
# record the last time that a received message sound was played
|
||||||
self.lastSoundTime = datetime.datetime.now()
|
self.lastSoundTime = datetime.now()
|
||||||
|
|
||||||
# if not wav then try mp3 format
|
try: # try already known format
|
||||||
if not os.path.isfile(soundFilename + '.wav'):
|
soundFilename += ext
|
||||||
soundFilename = soundFilename + '.mp3'
|
except (TypeError, NameError):
|
||||||
else:
|
ext = _choose_ext(soundFilename)
|
||||||
soundFilename = soundFilename + '.wav'
|
if not ext:
|
||||||
|
try: # if no user sound file found try to play from theme
|
||||||
|
return self._theme_player(category, label)
|
||||||
|
except TypeError:
|
||||||
|
return
|
||||||
|
|
||||||
|
soundFilename += ext
|
||||||
|
|
||||||
|
self._player(soundFilename)
|
||||||
|
|
||||||
|
# Try init the distro specific appindicator,
|
||||||
|
# for example the Ubuntu MessagingMenu
|
||||||
|
def indicatorInit(self):
|
||||||
|
def _noop_update(*args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
if os.path.isfile(soundFilename):
|
|
||||||
if 'linux' in sys.platform:
|
|
||||||
# Note: QSound was a nice idea but it didn't work
|
|
||||||
if '.mp3' in soundFilename:
|
|
||||||
gst_available=False
|
|
||||||
try:
|
try:
|
||||||
subprocess.call(["gst123", soundFilename],
|
self.indicatorUpdate = get_plugin('indicator')(self)
|
||||||
stdin=subprocess.PIPE,
|
except (NameError, TypeError):
|
||||||
stdout=subprocess.PIPE)
|
self.indicatorUpdate = _noop_update
|
||||||
gst_available=True
|
|
||||||
except:
|
|
||||||
logger.warning("WARNING: gst123 must be installed in order to play mp3 sounds")
|
|
||||||
if not gst_available:
|
|
||||||
try:
|
|
||||||
subprocess.call(["mpg123", soundFilename],
|
|
||||||
stdin=subprocess.PIPE,
|
|
||||||
stdout=subprocess.PIPE)
|
|
||||||
gst_available=True
|
|
||||||
except:
|
|
||||||
logger.warning("WARNING: mpg123 must be installed in order to play mp3 sounds")
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
subprocess.call(["aplay", soundFilename],
|
|
||||||
stdin=subprocess.PIPE,
|
|
||||||
stdout=subprocess.PIPE)
|
|
||||||
except:
|
|
||||||
logger.warning("WARNING: aplay must be installed in order to play WAV sounds")
|
|
||||||
elif sys.platform[0:3] == 'win':
|
|
||||||
# use winsound on Windows
|
|
||||||
import winsound
|
|
||||||
winsound.PlaySound(soundFilename, winsound.SND_FILENAME)
|
|
||||||
|
|
||||||
# initialise the message notifier
|
# initialise the message notifier
|
||||||
def notifierInit(self):
|
def notifierInit(self):
|
||||||
if withMessagingMenu:
|
def _simple_notify(
|
||||||
Notify.init('pybitmessage')
|
title, subtitle, category, label=None, icon=None):
|
||||||
|
|
||||||
# shows a notification
|
|
||||||
def notifierShow(self, title, subtitle, fromCategory, label):
|
|
||||||
self.playSound(fromCategory, label)
|
|
||||||
|
|
||||||
if withMessagingMenu:
|
|
||||||
n = Notify.Notification.new(
|
|
||||||
title, subtitle, 'notification-message-email')
|
|
||||||
try:
|
|
||||||
n.show()
|
|
||||||
except:
|
|
||||||
# n.show() has been known to throw this exception:
|
|
||||||
# gi._glib.GError: GDBus.Error:org.freedesktop.Notifications.
|
|
||||||
# MaxNotificationsExceeded: Exceeded maximum number of
|
|
||||||
# notifications
|
|
||||||
pass
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
self.tray.showMessage(title, subtitle, 1, 2000)
|
self.tray.showMessage(title, subtitle, 1, 2000)
|
||||||
|
|
||||||
|
self._notifier = _simple_notify
|
||||||
|
# does nothing if isAvailable returns false
|
||||||
|
self._player = QtGui.QSound.play
|
||||||
|
|
||||||
|
if not get_plugins:
|
||||||
|
return
|
||||||
|
|
||||||
|
_plugin = get_plugin('notification.message')
|
||||||
|
if _plugin:
|
||||||
|
self._notifier = _plugin
|
||||||
|
else:
|
||||||
|
logger.warning("No notification.message plugin found")
|
||||||
|
|
||||||
|
self._theme_player = get_plugin('notification.sound', 'theme')
|
||||||
|
|
||||||
|
if not QtGui.QSound.isAvailable():
|
||||||
|
_plugin = get_plugin(
|
||||||
|
'notification.sound', 'file', fallback='file.fallback')
|
||||||
|
if _plugin:
|
||||||
|
self._player = _plugin
|
||||||
|
else:
|
||||||
|
logger.warning("No notification.sound plugin found")
|
||||||
|
|
||||||
|
def notifierShow(
|
||||||
|
self, title, subtitle, category, label=None, icon=None):
|
||||||
|
self.playSound(category, label)
|
||||||
|
self._notifier(
|
||||||
|
unicode(title), unicode(subtitle), category, label, icon)
|
||||||
|
|
||||||
# tree
|
# tree
|
||||||
def treeWidgetKeyPressEvent(self, event):
|
def treeWidgetKeyPressEvent(self, event):
|
||||||
return self.handleKeyPress(event, self.getCurrentTreeWidget())
|
return self.handleKeyPress(event, self.getCurrentTreeWidget())
|
||||||
|
@ -1663,15 +1496,18 @@ class MyForm(settingsmixin.SMainWindow):
|
||||||
|
|
||||||
def setStatusIcon(self, color):
|
def setStatusIcon(self, color):
|
||||||
# print 'setting status icon color'
|
# print 'setting status icon color'
|
||||||
|
_notifications_enabled = not BMConfigParser().getboolean(
|
||||||
|
'bitmessagesettings', 'hidetrayconnectionnotifications')
|
||||||
if color == 'red':
|
if color == 'red':
|
||||||
self.pushButtonStatusIcon.setIcon(
|
self.pushButtonStatusIcon.setIcon(
|
||||||
QIcon(":/newPrefix/images/redicon.png"))
|
QIcon(":/newPrefix/images/redicon.png"))
|
||||||
shared.statusIconColor = 'red'
|
shared.statusIconColor = 'red'
|
||||||
# if the connection is lost then show a notification
|
# if the connection is lost then show a notification
|
||||||
if self.connected and not BMConfigParser().getboolean('bitmessagesettings', 'hidetrayconnectionnotifications'):
|
if self.connected and _notifications_enabled:
|
||||||
self.notifierShow('Bitmessage', unicode(_translate(
|
self.notifierShow(
|
||||||
"MainWindow", "Connection lost").toUtf8(),'utf-8'),
|
'Bitmessage',
|
||||||
self.SOUND_DISCONNECTED, None)
|
_translate("MainWindow", "Connection lost"),
|
||||||
|
sound.SOUND_DISCONNECTED)
|
||||||
if not BMConfigParser().safeGetBoolean('bitmessagesettings', 'upnp') and \
|
if not BMConfigParser().safeGetBoolean('bitmessagesettings', 'upnp') and \
|
||||||
BMConfigParser().get('bitmessagesettings', 'socksproxytype') == "none":
|
BMConfigParser().get('bitmessagesettings', 'socksproxytype') == "none":
|
||||||
self.statusBar().showMessage(_translate(
|
self.statusBar().showMessage(_translate(
|
||||||
|
@ -1689,10 +1525,11 @@ class MyForm(settingsmixin.SMainWindow):
|
||||||
":/newPrefix/images/yellowicon.png"))
|
":/newPrefix/images/yellowicon.png"))
|
||||||
shared.statusIconColor = 'yellow'
|
shared.statusIconColor = 'yellow'
|
||||||
# if a new connection has been established then show a notification
|
# if a new connection has been established then show a notification
|
||||||
if not self.connected and not BMConfigParser().getboolean('bitmessagesettings', 'hidetrayconnectionnotifications'):
|
if not self.connected and _notifications_enabled:
|
||||||
self.notifierShow('Bitmessage', unicode(_translate(
|
self.notifierShow(
|
||||||
"MainWindow", "Connected").toUtf8(),'utf-8'),
|
'Bitmessage',
|
||||||
self.SOUND_CONNECTED, None)
|
_translate("MainWindow", "Connected"),
|
||||||
|
sound.SOUND_CONNECTED)
|
||||||
self.connected = True
|
self.connected = True
|
||||||
|
|
||||||
if self.actionStatus is not None:
|
if self.actionStatus is not None:
|
||||||
|
@ -1705,10 +1542,11 @@ class MyForm(settingsmixin.SMainWindow):
|
||||||
self.pushButtonStatusIcon.setIcon(
|
self.pushButtonStatusIcon.setIcon(
|
||||||
QIcon(":/newPrefix/images/greenicon.png"))
|
QIcon(":/newPrefix/images/greenicon.png"))
|
||||||
shared.statusIconColor = 'green'
|
shared.statusIconColor = 'green'
|
||||||
if not self.connected and not BMConfigParser().getboolean('bitmessagesettings', 'hidetrayconnectionnotifications'):
|
if not self.connected and _notifications_enabled:
|
||||||
self.notifierShow('Bitmessage', unicode(_translate(
|
self.notifierShow(
|
||||||
"MainWindow", "Connected").toUtf8(),'utf-8'),
|
'Bitmessage',
|
||||||
self.SOUND_CONNECTION_GREEN, None)
|
_translate("MainWindow", "Connected"),
|
||||||
|
sound.SOUND_CONNECTION_GREEN)
|
||||||
self.connected = True
|
self.connected = True
|
||||||
|
|
||||||
if self.actionStatus is not None:
|
if self.actionStatus is not None:
|
||||||
|
@ -2253,11 +2091,19 @@ class MyForm(settingsmixin.SMainWindow):
|
||||||
else:
|
else:
|
||||||
acct = ret
|
acct = ret
|
||||||
self.propagateUnreadCount(acct.address)
|
self.propagateUnreadCount(acct.address)
|
||||||
if BMConfigParser().getboolean('bitmessagesettings', 'showtraynotifications'):
|
if BMConfigParser().getboolean(
|
||||||
self.notifierShow(unicode(_translate("MainWindow",'New Message').toUtf8(),'utf-8'), unicode(_translate("MainWindow",'From ').toUtf8(),'utf-8') + unicode(acct.fromLabel, 'utf-8'), self.SOUND_UNKNOWN, None)
|
'bitmessagesettings', 'showtraynotifications'):
|
||||||
|
self.notifierShow(
|
||||||
|
_translate("MainWindow", "New Message"),
|
||||||
|
_translate("MainWindow", "From %1").arg(
|
||||||
|
unicode(acct.fromLabel, 'utf-8')),
|
||||||
|
sound.SOUND_UNKNOWN
|
||||||
|
)
|
||||||
if self.getCurrentAccount() is not None and ((self.getCurrentFolder(treeWidget) != "inbox" and self.getCurrentFolder(treeWidget) is not None) or self.getCurrentAccount(treeWidget) != acct.address):
|
if self.getCurrentAccount() is not None and ((self.getCurrentFolder(treeWidget) != "inbox" and self.getCurrentFolder(treeWidget) is not None) or self.getCurrentAccount(treeWidget) != acct.address):
|
||||||
# Ubuntu should notify of new message irespective of whether it's in current message list or not
|
# Ubuntu should notify of new message irespective of
|
||||||
self.ubuntuMessagingMenuUpdate(True, None, acct.toLabel)
|
# whether it's in current message list or not
|
||||||
|
self.indicatorUpdate(True, to_label=acct.toLabel)
|
||||||
|
# cannot find item to pass here ):
|
||||||
if hasattr(acct, "feedback") and acct.feedback != GatewayAccount.ALL_OK:
|
if hasattr(acct, "feedback") and acct.feedback != GatewayAccount.ALL_OK:
|
||||||
if acct.feedback == GatewayAccount.REGISTRATION_DENIED:
|
if acct.feedback == GatewayAccount.REGISTRATION_DENIED:
|
||||||
self.dialog = EmailGatewayRegistrationDialog(self, _translate("EmailGatewayRegistrationDialog", "Registration failed:"),
|
self.dialog = EmailGatewayRegistrationDialog(self, _translate("EmailGatewayRegistrationDialog", "Registration failed:"),
|
||||||
|
@ -2851,9 +2697,6 @@ class MyForm(settingsmixin.SMainWindow):
|
||||||
shutdown.doCleanShutdown()
|
shutdown.doCleanShutdown()
|
||||||
self.statusBar().showMessage(_translate("MainWindow", "Stopping notifications... %1%").arg(str(90)))
|
self.statusBar().showMessage(_translate("MainWindow", "Stopping notifications... %1%").arg(str(90)))
|
||||||
self.tray.hide()
|
self.tray.hide()
|
||||||
# unregister the messaging system
|
|
||||||
if self.mmapp is not None:
|
|
||||||
self.mmapp.unregister()
|
|
||||||
|
|
||||||
self.statusBar().showMessage(_translate("MainWindow", "Shutdown imminent... %1%").arg(str(100)))
|
self.statusBar().showMessage(_translate("MainWindow", "Shutdown imminent... %1%").arg(str(100)))
|
||||||
shared.thisapp.cleanup()
|
shared.thisapp.cleanup()
|
||||||
|
@ -3336,6 +3179,7 @@ class MyForm(settingsmixin.SMainWindow):
|
||||||
self.popMenuAddressBook.addAction(self.actionAddressBookClipboard)
|
self.popMenuAddressBook.addAction(self.actionAddressBookClipboard)
|
||||||
self.popMenuAddressBook.addAction(self.actionAddressBookSubscribe)
|
self.popMenuAddressBook.addAction(self.actionAddressBookSubscribe)
|
||||||
self.popMenuAddressBook.addAction(self.actionAddressBookSetAvatar)
|
self.popMenuAddressBook.addAction(self.actionAddressBookSetAvatar)
|
||||||
|
self.popMenuAddressBook.addAction(self.actionAddressBookSetSound)
|
||||||
self.popMenuAddressBook.addSeparator()
|
self.popMenuAddressBook.addSeparator()
|
||||||
self.popMenuAddressBook.addAction(self.actionAddressBookNew)
|
self.popMenuAddressBook.addAction(self.actionAddressBookNew)
|
||||||
normal = True
|
normal = True
|
||||||
|
@ -3740,6 +3584,50 @@ class MyForm(settingsmixin.SMainWindow):
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def on_action_AddressBookSetSound(self):
|
||||||
|
widget = self.ui.tableWidgetAddressBook
|
||||||
|
self.setAddressSound(widget.item(widget.currentRow(), 0).text())
|
||||||
|
|
||||||
|
def setAddressSound(self, addr):
|
||||||
|
filters = [unicode(_translate(
|
||||||
|
"MainWindow", "Sound files (%s)" %
|
||||||
|
' '.join(['*%s%s' % (os.extsep, ext) for ext in sound.extensions])
|
||||||
|
))]
|
||||||
|
sourcefile = unicode(QtGui.QFileDialog.getOpenFileName(
|
||||||
|
self, _translate("MainWindow", "Set notification sound..."),
|
||||||
|
filter=';;'.join(filters)
|
||||||
|
))
|
||||||
|
|
||||||
|
if not sourcefile:
|
||||||
|
return
|
||||||
|
|
||||||
|
destdir = os.path.join(state.appdata, 'sounds')
|
||||||
|
destfile = unicode(addr) + os.path.splitext(sourcefile)[-1]
|
||||||
|
destination = os.path.join(destdir, destfile)
|
||||||
|
|
||||||
|
if sourcefile == destination:
|
||||||
|
return
|
||||||
|
|
||||||
|
pattern = destfile.lower()
|
||||||
|
for item in os.listdir(destdir):
|
||||||
|
if item.lower() == pattern:
|
||||||
|
overwrite = QtGui.QMessageBox.question(
|
||||||
|
self, _translate("MainWindow", "Message"),
|
||||||
|
_translate(
|
||||||
|
"MainWindow",
|
||||||
|
"You have already set a notification sound"
|
||||||
|
" for this address book entry."
|
||||||
|
" Do you really want to overwrite it?"),
|
||||||
|
QtGui.QMessageBox.Yes, QtGui.QMessageBox.No
|
||||||
|
) == QtGui.QMessageBox.Yes
|
||||||
|
if overwrite:
|
||||||
|
QtCore.QFile.remove(os.path.join(destdir, item))
|
||||||
|
break
|
||||||
|
|
||||||
|
if not QtCore.QFile.copy(sourcefile, destination):
|
||||||
|
logger.error(
|
||||||
|
'couldn\'t copy %s to %s', sourcefile, destination)
|
||||||
|
|
||||||
def on_context_menuYourIdentities(self, point):
|
def on_context_menuYourIdentities(self, point):
|
||||||
currentItem = self.getCurrentItem()
|
currentItem = self.getCurrentItem()
|
||||||
self.popMenuYourIdentities = QtGui.QMenu(self)
|
self.popMenuYourIdentities = QtGui.QMenu(self)
|
||||||
|
@ -4525,7 +4413,7 @@ def run():
|
||||||
myapp = MyForm()
|
myapp = MyForm()
|
||||||
|
|
||||||
myapp.appIndicatorInit(app)
|
myapp.appIndicatorInit(app)
|
||||||
myapp.ubuntuMessagingMenuInit()
|
myapp.indicatorInit()
|
||||||
myapp.notifierInit()
|
myapp.notifierInit()
|
||||||
myapp._firstrun = BMConfigParser().safeGetBoolean(
|
myapp._firstrun = BMConfigParser().safeGetBoolean(
|
||||||
'bitmessagesettings', 'dontconnect')
|
'bitmessagesettings', 'dontconnect')
|
||||||
|
|
21
src/bitmessageqt/sound.py
Normal file
21
src/bitmessageqt/sound.py
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# sound type constants
|
||||||
|
SOUND_NONE = 0
|
||||||
|
SOUND_KNOWN = 1
|
||||||
|
SOUND_UNKNOWN = 2
|
||||||
|
SOUND_CONNECTED = 3
|
||||||
|
SOUND_DISCONNECTED = 4
|
||||||
|
SOUND_CONNECTION_GREEN = 5
|
||||||
|
|
||||||
|
|
||||||
|
# returns true if the given sound category is a connection sound
|
||||||
|
# rather than a received message sound
|
||||||
|
def is_connection_sound(category):
|
||||||
|
return category in (
|
||||||
|
SOUND_CONNECTED,
|
||||||
|
SOUND_DISCONNECTED,
|
||||||
|
SOUND_CONNECTION_GREEN
|
||||||
|
)
|
||||||
|
|
||||||
|
extensions = ('wav', 'mp3', 'oga')
|
71
src/plugins/indicator_libmessaging.py
Normal file
71
src/plugins/indicator_libmessaging.py
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import gi
|
||||||
|
gi.require_version('MessagingMenu', '1.0')
|
||||||
|
from gi.repository import MessagingMenu
|
||||||
|
|
||||||
|
from pybitmessage.bitmessageqt.utils import str_broadcast_subscribers
|
||||||
|
from pybitmessage.tr import _translate
|
||||||
|
|
||||||
|
|
||||||
|
class IndicatorLibmessaging(object):
|
||||||
|
def __init__(self, form):
|
||||||
|
try:
|
||||||
|
self.app = MessagingMenu.App(desktop_id='pybitmessage.desktop')
|
||||||
|
self.app.register()
|
||||||
|
self.app.connect('activate-source', self.activate)
|
||||||
|
except:
|
||||||
|
self.app = None
|
||||||
|
return
|
||||||
|
|
||||||
|
self._menu = {
|
||||||
|
'send': str(_translate('MainWindow', 'Send')),
|
||||||
|
'messages': str(_translate('MainWindow', 'Messages')),
|
||||||
|
'subscriptions': str(_translate('MainWindow', 'Subscriptions'))
|
||||||
|
}
|
||||||
|
|
||||||
|
self.new_message_item = self.new_broadcast_item = None
|
||||||
|
self.form = form
|
||||||
|
self.show_unread()
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
if self.app:
|
||||||
|
self.app.unregister()
|
||||||
|
|
||||||
|
def activate(self, app, source):
|
||||||
|
self.form.appIndicatorInbox(
|
||||||
|
self.new_message_item if source == 'messages'
|
||||||
|
else self.new_broadcast_item
|
||||||
|
)
|
||||||
|
|
||||||
|
# show the number of unread messages and subscriptions
|
||||||
|
# on the messaging menu
|
||||||
|
def show_unread(self, draw_attention=False):
|
||||||
|
for source, count in zip(
|
||||||
|
('messages', 'subscriptions'),
|
||||||
|
self.form.getUnread()
|
||||||
|
):
|
||||||
|
if count > 0:
|
||||||
|
if self.app.has_source(source):
|
||||||
|
self.app.set_source_count(source, count)
|
||||||
|
else:
|
||||||
|
self.app.append_source_with_count(
|
||||||
|
source, None, self._menu[source], count)
|
||||||
|
if draw_attention:
|
||||||
|
self.app.draw_attention(source)
|
||||||
|
|
||||||
|
# update the Ubuntu messaging menu
|
||||||
|
def __call__(self, draw_attention, item=None, to_label=None):
|
||||||
|
if not self.app:
|
||||||
|
return
|
||||||
|
# remember this item to that the activate() can find it
|
||||||
|
if item:
|
||||||
|
if to_label == str_broadcast_subscribers:
|
||||||
|
self.new_broadcast_item = item
|
||||||
|
else:
|
||||||
|
self.new_message_item = item
|
||||||
|
|
||||||
|
self.show_unread(draw_attention)
|
||||||
|
|
||||||
|
|
||||||
|
connect_plugin = IndicatorLibmessaging
|
13
src/plugins/notification_notify2.py
Normal file
13
src/plugins/notification_notify2.py
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import gi
|
||||||
|
gi.require_version('Notify', '0.7')
|
||||||
|
from gi.repository import Notify
|
||||||
|
|
||||||
|
Notify.init('pybitmessage')
|
||||||
|
|
||||||
|
|
||||||
|
def connect_plugin(title, subtitle, category, label, icon):
|
||||||
|
if not icon:
|
||||||
|
icon = 'mail-message-new' if category == 2 else 'pybitmessage'
|
||||||
|
Notify.Notification.new(title, subtitle, icon).show()
|
|
@ -3,10 +3,33 @@
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
|
|
||||||
|
|
||||||
def get_plugins(group, point, name=None):
|
def get_plugins(group, point='', name=None, fallback=None):
|
||||||
for plugin in pkg_resources.iter_entry_points(group):
|
"""
|
||||||
if plugin.name.startswith(point):
|
Iterate through plugins (`connect_plugin` attribute of entry point)
|
||||||
|
which name starts with `point` or equals to `name`.
|
||||||
|
If `fallback` kwarg specified, plugin with that name yield last.
|
||||||
|
"""
|
||||||
|
for ep in pkg_resources.iter_entry_points('bitmessage.' + group):
|
||||||
|
if name and ep.name == name or ep.name.startswith(point):
|
||||||
try:
|
try:
|
||||||
yield plugin.load().connect_plugin
|
plugin = ep.load().connect_plugin
|
||||||
except (AttributeError, pkg_resources.DistributionNotFound):
|
if ep.name == fallback:
|
||||||
|
_fallback = plugin
|
||||||
|
else:
|
||||||
|
yield plugin
|
||||||
|
except (AttributeError,
|
||||||
|
ImportError,
|
||||||
|
ValueError,
|
||||||
|
pkg_resources.DistributionNotFound,
|
||||||
|
pkg_resources.UnknownExtra):
|
||||||
continue
|
continue
|
||||||
|
try:
|
||||||
|
yield _fallback
|
||||||
|
except NameError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def get_plugin(*args, **kwargs):
|
||||||
|
"""Returns first available plugin `from get_plugins()` if any."""
|
||||||
|
for plugin in get_plugins(*args, **kwargs):
|
||||||
|
return plugin
|
||||||
|
|
21
src/plugins/sound_canberra.py
Normal file
21
src/plugins/sound_canberra.py
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from pybitmessage.bitmessageqt import sound
|
||||||
|
|
||||||
|
import pycanberra
|
||||||
|
|
||||||
|
_canberra = pycanberra.Canberra()
|
||||||
|
|
||||||
|
_theme = {
|
||||||
|
sound.SOUND_UNKNOWN: 'message-new-email',
|
||||||
|
sound.SOUND_CONNECTED: 'network-connectivity-established',
|
||||||
|
sound.SOUND_DISCONNECTED: 'network-connectivity-lost',
|
||||||
|
sound.SOUND_CONNECTION_GREEN: 'network-connectivity-established'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def connect_plugin(category, label=None):
|
||||||
|
try:
|
||||||
|
_canberra.play(0, pycanberra.CA_PROP_EVENT_ID, _theme[category], None)
|
||||||
|
except (KeyError, pycanberra.CanberraException):
|
||||||
|
pass
|
14
src/plugins/sound_gstreamer.py
Normal file
14
src/plugins/sound_gstreamer.py
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import gi
|
||||||
|
gi.require_version('Gst', '1.0')
|
||||||
|
from gi.repository import Gst # noqa: E402
|
||||||
|
|
||||||
|
Gst.init(None)
|
||||||
|
_player = Gst.ElementFactory.make("playbin", "player")
|
||||||
|
|
||||||
|
|
||||||
|
def connect_plugin(sound_file):
|
||||||
|
_player.set_state(Gst.State.NULL)
|
||||||
|
_player.set_property("uri", "file://" + sound_file)
|
||||||
|
_player.set_state(Gst.State.PLAYING)
|
41
src/plugins/sound_playfile.py
Normal file
41
src/plugins/sound_playfile.py
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
import winsound
|
||||||
|
|
||||||
|
def connect_plugin(sound_file):
|
||||||
|
winsound.PlaySound(sound_file, winsound.SND_FILENAME)
|
||||||
|
except ImportError:
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
play_cmd = {}
|
||||||
|
|
||||||
|
def _subprocess(*args):
|
||||||
|
FNULL = open(os.devnull, 'wb')
|
||||||
|
subprocess.call(
|
||||||
|
args, stdout=FNULL, stderr=subprocess.STDOUT, close_fds=True)
|
||||||
|
|
||||||
|
def connect_plugin(sound_file):
|
||||||
|
global play_cmd
|
||||||
|
|
||||||
|
ext = os.path.splitext(sound_file)[-1]
|
||||||
|
try:
|
||||||
|
return _subprocess(play_cmd[ext], sound_file)
|
||||||
|
except (KeyError, AttributeError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
programs = ['gst123', 'gst-play-1.0']
|
||||||
|
if ext == '.wav':
|
||||||
|
programs.append('aplay')
|
||||||
|
elif ext == '.mp3':
|
||||||
|
programs += ['mpg123', 'mpg321', 'mpg321-mpg123']
|
||||||
|
for cmd in programs:
|
||||||
|
try:
|
||||||
|
_subprocess(cmd, sound_file)
|
||||||
|
except OSError:
|
||||||
|
pass # log here!
|
||||||
|
else:
|
||||||
|
play_cmd[ext] = cmd
|
||||||
|
break
|
Reference in New Issue
Block a user