diff options
Diffstat (limited to 'src/leap/baseapp/systray.py')
-rw-r--r-- | src/leap/baseapp/systray.py | 268 |
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) |