summaryrefslogtreecommitdiff
path: root/src/leap/baseapp/systray.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/leap/baseapp/systray.py')
-rw-r--r--src/leap/baseapp/systray.py268
1 files changed, 268 insertions, 0 deletions
diff --git a/src/leap/baseapp/systray.py b/src/leap/baseapp/systray.py
new file mode 100644
index 00000000..77eb3fe9
--- /dev/null
+++ b/src/leap/baseapp/systray.py
@@ -0,0 +1,268 @@
+import logging
+import sys
+
+import sip
+sip.setapi('QString', 2)
+sip.setapi('QVariant', 2)
+
+from PyQt4 import QtCore
+from PyQt4 import QtGui
+
+from leap import __branding as BRANDING
+from leap import __version__ as VERSION
+
+from leap.gui import mainwindow_rc
+
+logger = logging.getLogger(__name__)
+
+
+class StatusAwareTrayIconMixin(object):
+ """
+ a mix of several functions needed
+ to create a systray and make it
+ get updated from conductor status
+ polling.
+ """
+ states = {
+ "disconnected": 0,
+ "connecting": 1,
+ "connected": 2}
+
+ iconpath = {
+ "disconnected": ':/images/conn_error.png',
+ "connecting": ':/images/conn_connecting.png',
+ "connected": ':/images/conn_connected.png'}
+
+ Icons = {
+ 'disconnected': lambda self: QtGui.QIcon(
+ self.iconpath['disconnected']),
+ 'connecting': lambda self: QtGui.QIcon(
+ self.iconpath['connecting']),
+ 'connected': lambda self: QtGui.QIcon(
+ self.iconpath['connected'])
+ }
+
+ def __init__(self, *args, **kwargs):
+ self.createIconGroupBox()
+ self.createActions()
+ self.createTrayIcon()
+
+ # not sure if this really belongs here, but...
+ self.timer = QtCore.QTimer()
+
+ def show_systray_icon(self):
+ #logger.debug('showing tray icon................')
+ self.trayIcon.show()
+
+ def createIconGroupBox(self):
+ """
+ dummy icongroupbox
+ (to be removed from here -- reference only)
+ """
+ con_widgets = {
+ 'disconnected': QtGui.QLabel(),
+ 'connecting': QtGui.QLabel(),
+ 'connected': QtGui.QLabel(),
+ }
+ con_widgets['disconnected'].setPixmap(
+ QtGui.QPixmap(
+ self.iconpath['disconnected']))
+ con_widgets['connecting'].setPixmap(
+ QtGui.QPixmap(
+ self.iconpath['connecting']))
+ con_widgets['connected'].setPixmap(
+ QtGui.QPixmap(
+ self.iconpath['connected'])),
+ self.ConnectionWidgets = con_widgets
+
+ self.statusIconBox = QtGui.QGroupBox(
+ self.tr("EIP Connection Status"))
+ statusIconLayout = QtGui.QHBoxLayout()
+ statusIconLayout.addWidget(self.ConnectionWidgets['disconnected'])
+ statusIconLayout.addWidget(self.ConnectionWidgets['connecting'])
+ statusIconLayout.addWidget(self.ConnectionWidgets['connected'])
+ statusIconLayout.itemAt(1).widget().hide()
+ statusIconLayout.itemAt(2).widget().hide()
+
+ self.leapConnStatus = QtGui.QLabel(
+ self.tr("<b>disconnected</b>"))
+ statusIconLayout.addWidget(self.leapConnStatus)
+
+ self.statusIconBox.setLayout(statusIconLayout)
+
+ def createTrayIcon(self):
+ """
+ creates the tray icon
+ """
+ self.trayIconMenu = QtGui.QMenu(self)
+
+ self.trayIconMenu.addAction(self.connAct)
+ self.trayIconMenu.addSeparator()
+ self.trayIconMenu.addAction(self.detailsAct)
+ self.trayIconMenu.addSeparator()
+ self.trayIconMenu.addAction(self.aboutAct)
+ # we should get this hidden inside the "about" dialog
+ # (as a little button maybe)
+ #self.trayIconMenu.addAction(self.aboutQtAct)
+ self.trayIconMenu.addSeparator()
+ self.trayIconMenu.addAction(self.quitAction)
+
+ self.trayIcon = QtGui.QSystemTrayIcon(self)
+ self.setIcon('disconnected')
+ self.trayIcon.setContextMenu(self.trayIconMenu)
+
+ #self.trayIconMenu.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
+ #self.trayIconMenu.customContextMenuRequested.connect(
+ #self.on_context_menu)
+
+ #def bad(self):
+ #logger.error('this should not be called')
+
+ def createActions(self):
+ """
+ creates actions to be binded to tray icon
+ """
+ # XXX change action name on (dis)connect
+ self.connAct = QtGui.QAction(
+ self.tr("Encryption ON turn &off"),
+ self,
+ triggered=lambda: self.start_or_stopVPN())
+
+ self.detailsAct = QtGui.QAction(
+ self.tr("&Details..."),
+ self,
+ triggered=self.detailsWin)
+ self.aboutAct = QtGui.QAction(
+ self.tr("&About"), self,
+ triggered=self.about)
+ self.aboutQtAct = QtGui.QAction(
+ self.tr("About Q&t"), self,
+ triggered=QtGui.qApp.aboutQt)
+ self.quitAction = QtGui.QAction(
+ self.tr("&Quit"), self,
+ triggered=self.cleanupAndQuit)
+
+ def toggleEIPAct(self):
+ # this is too simple by now.
+ # XXX get STATUS CONSTANTS INSTEAD
+
+ icon_status = self.conductor.get_icon_name()
+ if icon_status == "connected":
+ self.connAct.setEnabled(True)
+ self.connAct.setText(
+ self.tr('Encryption ON turn o&ff'))
+ return
+ if icon_status == "disconnected":
+ self.connAct.setEnabled(True)
+ self.connAct.setText(
+ self.tr('Encryption OFF turn &on'))
+ return
+ if icon_status == "connecting":
+ self.connAct.setDisabled(True)
+ self.connAct.setText(self.tr('connecting...'))
+ return
+
+ def detailsWin(self):
+ visible = self.isVisible()
+ if visible:
+ self.hide()
+ else:
+ self.show()
+ if sys.platform == "darwin":
+ self.raise_()
+
+ def about(self):
+ # move to widget
+ flavor = BRANDING.get('short_name', None)
+ content = self.tr(
+ ("LEAP client<br>"
+ "(version <b>%s</b>)<br>" % VERSION))
+ if flavor:
+ content = content + ('<br>Flavor: <i>%s</i><br>' % flavor)
+ content = content + (
+ "<br><a href='https://leap.se/'>"
+ "https://leap.se</a>")
+ QtGui.QMessageBox.about(self, self.tr("About"), content)
+
+ def setConnWidget(self, icon_name):
+ oldlayout = self.statusIconBox.layout()
+
+ for i in range(3):
+ oldlayout.itemAt(i).widget().hide()
+ new = self.states[icon_name]
+ oldlayout.itemAt(new).widget().show()
+
+ def setIcon(self, name):
+ icon_fun = self.Icons.get(name)
+ if icon_fun and callable(icon_fun):
+ icon = icon_fun(self)
+ self.trayIcon.setIcon(icon)
+
+ def getIcon(self, icon_name):
+ return self.states.get(icon_name, None)
+
+ def setIconToolTip(self):
+ """
+ get readable status and place it on systray tooltip
+ """
+ status = self.conductor.status.get_readable_status()
+ self.trayIcon.setToolTip(status)
+
+ def iconActivated(self, reason):
+ """
+ handles left click, left double click
+ showing the trayicon menu
+ """
+ if reason in (QtGui.QSystemTrayIcon.Trigger,
+ QtGui.QSystemTrayIcon.DoubleClick):
+ context_menu = self.trayIcon.contextMenu()
+ # for some reason, context_menu.show()
+ # is failing in a way beyond my understanding.
+ # (not working the first time it's clicked).
+ # this works however.
+ # XXX in osx it shows some glitches.
+ context_menu.exec_(self.trayIcon.geometry().center())
+
+ @QtCore.pyqtSlot()
+ def onTimerTick(self):
+ self.statusUpdate()
+
+ @QtCore.pyqtSlot(object)
+ def onOpenVPNStatusChange(self, status):
+ """
+ updates icon, according to the openvpn status change.
+ """
+ icon_name = self.conductor.get_icon_name()
+ if not icon_name:
+ return
+
+ # XXX refactor. Use QStateMachine
+
+ if icon_name in ("disconnected", "connected"):
+ self.eipStatusChange.emit(icon_name)
+
+ if icon_name in ("connecting"):
+ # let's see how it matches
+ leap_status_name = self.conductor.get_leap_status()
+ self.eipStatusChange.emit(leap_status_name)
+
+ if icon_name == "connected":
+ # When we change to "connected', we launch
+ # the network checker.
+ self.initNetworkChecker.emit()
+
+ self.setIcon(icon_name)
+ # change connection pixmap widget
+ self.setConnWidget(icon_name)
+
+ @QtCore.pyqtSlot(str)
+ def onEIPConnStatusChange(self, newstatus):
+ """
+ slot for EIP status changes
+ not to be confused with onOpenVPNStatusChange.
+ this only updates the non-debug LEAP Status line
+ next to the connection icon.
+ """
+ # XXX move bold to style sheet
+ self.leapConnStatus.setText(
+ "<b>%s</b>" % newstatus)