def __init__( self, seedNodes, reactor ) : QMainWindow.__init__( self ) self.ui = Ui_MainWindow() self.ui.setupUi( self ) self._setupIcons() self.seedNodes = seedNodes self.reactor = reactor self.session = UserSession( seedNodes, reactor ) self.sm = self.session.sm self.ev = Eventer() self.profile = None self.baseTitle = str(self.windowTitle()) self.exitAfterOffline = False self.reconnector = None self.keyInfoDialog = None self.addContactDialog = None self.sm.insertCallback( self._onStateChange ) self.connect( self.ui.createKeyButton, SIGNAL('clicked()'), self.ui.actionCreateKey.trigger ) self.connect( self.ui.createKeyButton1, SIGNAL('clicked()'), self.ui.actionCreateKey.trigger ) self.connect( self.ui.goOnlineButton, SIGNAL('clicked()'), self.ui.actionGoOnline.trigger ) self.ui.contacts.setWrapping( True ) self.connect( self.ui.contacts, SIGNAL('customContextMenuRequested(const QPoint&)'), self._onContactsContextMenuRequested ) self.connect( self.ui.contacts, SIGNAL('itemDoubleClicked(QListWidgetItem*)'), self._onContactDoubleClicked ) self.contextMenu = QMenu() self.contextMenu.addAction( self.ui.actionCheckStatus ) self.contextMenu.addAction( self.ui.actionRemoveContact ) self.contextMenu.addAction( self.ui.actionContactInfo ) self.connect( self.contextMenu, SIGNAL('triggered(QAction*)'), self._onContextMenuAction ) self.actionManager = ActionManager( self.contextMenu ) self.appletServer = AppletServer( self.session, self.actionManager, self.reactor ) self.statusChecker = StatusChecker( self.ui.contacts, self.session, self.ev, self.reactor ) self.contactInfoDialogs = {} self.trayMenu = QMenu( self ) self.trayMenu.addAction( self.ui.actionExit ) if HAS_TRAY : self.trayIcon = TrayIcon( QPixmap(':/images/cspace_offline.png'), 'CSpace', self.trayMenu, self ) self.connect( self.trayIcon, SIGNAL('clicked(const QPoint&,int)'), self._onTrayClicked ) self.trayIcon.show() else : self.trayIcon = None self.editPermissionsDialog = None self.autoUpdater = AutoUpdater( self.reactor ) self.autoUpdater.setUpdateCallback( self._onUpdateDownloaded ) self._updateUI() self._doCheckSettings()
def __init__( self, seedNodes, reactor ) : QMainWindow.__init__( self ) self.ui = Ui_MainWindow() self.ui.setupUi( self ) self._setupIcons() self.seedNodes = seedNodes self.reactor = reactor self.session = UserSession( seedNodes, reactor ) self.sm = self.session.sm self.ev = Eventer() self.profile = None self.baseTitle = str(self.windowTitle()) self.exitAfterOffline = False self.reconnector = None self.keyInfoDialog = None self.addContactDialog = None self.sm.insertCallback( self._onStateChange ) self.connect( self.ui.createKeyButton, SIGNAL('clicked()'), self.ui.actionCreateKey.trigger ) self.connect( self.ui.createKeyButton1, SIGNAL('clicked()'), self.ui.actionCreateKey.trigger ) self.connect( self.ui.goOnlineButton, SIGNAL('clicked()'), self.ui.actionGoOnline.trigger ) self.ui.contacts.setWrapping( True ) self.connect( self.ui.contacts, SIGNAL('customContextMenuRequested(const QPoint&)'), self._onContactsContextMenuRequested ) self.connect( self.ui.contacts, SIGNAL('itemDoubleClicked(QListWidgetItem*)'), self._onContactDoubleClicked ) self.contextMenu = QMenu() self.contextMenu.addAction( self.ui.actionCheckStatus ) self.contextMenu.addAction( self.ui.actionRemoveContact ) self.contextMenu.addAction( self.ui.actionContactInfo ) self.connect( self.contextMenu, SIGNAL('triggered(QAction*)'), self._onContextMenuAction ) self.actionManager = ActionManager( self.contextMenu ) self.appletServer = AppletServer( self.session, self.actionManager, self.reactor ) self.appletServer.registerServices() self.appletServer.registerActions() self.statusChecker = StatusChecker( self.ui.contacts, self.session, self.ev, self.reactor ) self.contactInfoDialogs = {} self.trayMenu = QMenu( self ) self.trayMenu.addAction( self.ui.actionExit ) if HAS_TRAY : self.trayIcon = TrayIcon( QPixmap(':/images/cspace_offline.png'), 'CSpace', self.trayMenu, self ) self.connect( self.trayIcon, SIGNAL('clicked(const QPoint&,int)'), self._onTrayClicked ) self.trayIcon.show() else : self.trayIcon = None self.editPermissionsDialog = None self.autoUpdater = AutoUpdater( self.reactor ) self.autoUpdater.setUpdateCallback( self._onUpdateDownloaded ) self._updateUI() self._doCheckSettings()
def __init__(self, seedNodes, settings=None, reactor=None): global logger logger.info("CSpaceService init") if reactor is None: reactor = SelectReactor() if settings is None: settings = localSettings() self.reactor = reactor self.settings = settings self.dispatcher = Eventer() self.seedNodes = seedNodes # Session Manager self.session = UserSession(self.seedNodes, reactor) # Our user profile self.profile = None # Session status State Machine self.sm = self.session.sm # Make sure we handle the edges/state transitions self.sm.insertCallback(self._onStateChange) # Manage actions self.actionManager = ActionManager() # Loads applets in subprocesses self.appletServer = AppletServer(self.session, self.actionManager, self.reactor) # Our contacts' statii self.statusProbe = ContactStatusChecker(self, reactor) # Our current status self.status = self.sm.current() self.statusmsg = self.STATII[self.status] self.exitAfterOffline = False self.reactor.callLater(0, self._onStarted)
class CSpaceService: OFFLINE = UserSession.OFFLINE CONNECTING = UserSession.CONNECTING ONLINE = UserSession.ONLINE DISCONNECTING = UserSession.DISCONNECTING UNKNOWN = -1 STATII = { OFFLINE: "Offline", CONNECTING: "Connecting", ONLINE: "Online", DISCONNECTING: "Disconnecting", UNKNOWN: "Unknown", } ########## ## Service initialization ## def __init__(self, seedNodes, settings=None, reactor=None): global logger logger.info("CSpaceService init") if reactor is None: reactor = SelectReactor() if settings is None: settings = localSettings() self.reactor = reactor self.settings = settings self.dispatcher = Eventer() self.seedNodes = seedNodes # Session Manager self.session = UserSession(self.seedNodes, reactor) # Our user profile self.profile = None # Session status State Machine self.sm = self.session.sm # Make sure we handle the edges/state transitions self.sm.insertCallback(self._onStateChange) # Manage actions self.actionManager = ActionManager() # Loads applets in subprocesses self.appletServer = AppletServer(self.session, self.actionManager, self.reactor) # Our contacts' statii self.statusProbe = ContactStatusChecker(self, reactor) # Our current status self.status = self.sm.current() self.statusmsg = self.STATII[self.status] self.exitAfterOffline = False self.reactor.callLater(0, self._onStarted) def _onStarted(self): self.dispatcher.trigger("service.start") def _error(self, msg): logger.error(msg) self.dispatcher.trigger("error", msg) return (False, msg) def run(self): self.reactor.run() try: self.appletServer.bridgeThread.reactor.stop() except AttributeError: pass def svcstatus(self): return self.statusmsg def stop(self): # offline first if self.status != self.OFFLINE: self.exitAfterOffline = True return self.offline() for reactor in (self.appletServer.bridgeThread.reactor, self.reactor): reactor.stop() self.dispatcher.trigger("service.stop") return True ########## ## Go online and offline, change profiles ## def switch(self, profile, password): if self.status is not self.OFFLINE: return self._error("Not currently offline.") st = self.settings logger.debug("Changing profile...") profiles = [userName for userName, keyId, entry in listProfiles()] if not st.getInt("Settings/RememberKey", 0): return self._error("No key found.") if not profile in entries: return self._error("No profile %s found.") profile = loadProfile(profile, password) if not profile: return self._error("Profile couldn't be loaded.") st.setString("Settings/SavedProfile", profile.name) st.setString("Settings/SavedPassword", password) saveLocalSettings() self.dispatcher.trigger("profile.switch", profile) return True def online(self): global logger if self.status is not self.OFFLINE: return self._error("Not currently offline.") st = self.settings logger.debug("Going online...") entries = [entry for userName, keyId, entry in listProfiles()] if not st.getInt("Settings/RememberKey", 0): return self._error("No key found.") entry = st.getString("Settings/SavedProfile") password = st.getString("Settings/SavedPassword") if not (entry and password and (entry in entries)): return self._error("No profile or password.") profile = loadProfile(entry, password) if not profile: return self._error("Profile couldn't be loaded.") self.profile = profile logger.debug("Going online as %s keyID=%s ..." % (profile.name, profile.keyId)) self.session.goOnline(self.profile) self.reconnector = Reconnector(self.profile, self._onReconnectTimer, self.reactor) return True def _onReconnectTimer(self): assert self.reconnector if self.reconnector.timeLeft > 0: return self.profile = self.reconnector.profile self.reconnector.shutdown() if self.session.sm.current() == 0: self.session.goOnline(self.profile) self.reconnector = Reconnector(self.profile, self._onReconnectTimer, self.reactor) self.dispatcher.trigger("profile.reconnecting", self.profile) def offline(self): if self.status is not self.ONLINE: return self._error("Not currently online.") self.session.goOffline() self.profile = None self.reconnector.shutdown() self.reconnector = None return True def _onStateChange(self): """ Respond to connecting/online/disconnecting/offline notices """ self.status = self.sm.current() self.statusmsg = None # No longer online if self.sm.previous() == self.ONLINE: # Stop checking contact statii self.statusProbe.stopProbing() # Clean up any open applets self.appletServer.clearConnections() # Now offline if self.sm.current() == self.OFFLINE: profile = self.profile self.profile = None if self.reconnector: if self.sm.previous() == self.CONNECTING: msg = "Connect failed." else: msg = "Disconnected." self.reconnector.startReconnectTimer(msg) self.statusmsg = msg self.dispatcher.trigger("profile.offline", profile) if self.exitAfterOffline: self.stop() # Now online elif self.sm.current() == self.ONLINE: # Restart checking contact statii self.dispatcher.trigger("profile.online", self.profile) self.statusProbe.startProbing() # Now connecting elif self.sm.current() == self.CONNECTING: self.dispatcher.trigger("profile.connecting", self.profile) elif self.sm.current() == self.DISCONNECTING: self.dispatcher.trigger("profile.disconnecting", self.profile) if not self.statusmsg: self.statusmsg = self.STATII[self.status] logger.info("STATUS: " + self.statusmsg) ########## ## List, add and remove contacts ## def list(self): if self.status != self.ONLINE: return self._error("Not connected.") contactsByName = self.profile.contactNames statii = [] STATII = CSpaceService.STATII for key in contactsByName: contact = contactsByName[key] if hasattr(contact, "status"): buddystatus = (key, contact.status, STATII[contact.status]) else: buddystatus = (key, -1, STATII[-1]) statii.append(buddystatus) statii.sort() return statii def add(self, cname, keyid): # Lookup the contact info and call _doAddContact if self.status != self.ONLINE: return self._error("Can't lookup buddy while not online.") httpOp = HttpRequest(self.reactor).get( # 'http://cspace.in/pubkey/%s' % keyid, "http://identity.datahaven.net/cspacekeys/%s" % keyid, self._onLookupResponse, ) self._addOp = AsyncOp(self._onAddContact, httpOp.cancel) self._addOp.cname = cname return True def addKey(self, cname, key): if cname and not isValidUserName(cname): return self._error("Bad username.") k = RSAKey() try: k.fromPEM_PublicKey(pemPublicKey) except RSAError: return self._error("Bad PEM-encoded key.") contact = Contact(k, cname) self._onAddContact(contact) def _onLookupResponse(self, responseCode, data): if responseCode != 200: self._addOp.notify(None) return inp = StringIO.StringIO(data) name = inp.readline().strip() pemPublicKey = inp.read() if name and not isValidUserName(name): self._addOp.notify(None) return k = RSAKey() try: k.fromPEM_PublicKey(pemPublicKey) except RSAError: self._addOp.notify(None) contact = Contact(k, self._addOp.cname) self._addOp.notify(contact) def _onAddContact(self, contact): del self._addOp if contact == None: # Contact lookup failed. return self.profile.addContact(contact) saveProfileContacts(self.profile) self.dispatcher.trigger("contact.add", contact) def remove(self, cname=None): if self.status != self.ONLINE: return self._error("Can't modify contact list while not online.") # Lookup the contact in profile and remove it contact = self.profile.getContactByName(cname) if contact is None: return self._error("No such contact.") self.profile.removeContact(contact) saveProfileContacts(self.profile) self.dispatcher.trigger("contact.remove", contact) return True ######### ## Perform contact action ## def action(self, buddy, action): if self.status != self.ONLINE: return False actionManager = self.actionManager if buddy not in self.profile.contactNames: return self._error("Bad buddy name: %s" % buddy) for actionItem in actionManager.actions: if actionItem[4] == action: logger.info("Running action %s on %s" % (action, buddy)) retVal = actionManager.execAction(action, buddy) self.dispatcher.trigger("contact.action", self.profile.contactNames[buddy], action, retVal) return retVal else: logger.info("Not action %s" % actionItem[4]) return self._error("Bad action name: %s" % action) def probe(self, buddy): if self.status != self.ONLINE: return self._error("Not online.") if buddy not in self.profile.contactNames: return self._error("Bad buddy name: %s" % buddy) self.statusProbe.probeContactStatus(buddy) return True def contactinfo(self, buddy): if self.status != self.ONLINE: return self._error("Not online.") if buddy not in self.profile.contactNames: return self._error("Bad buddy name: %s" % buddy) contact = self.profile.contactNames[buddy] contactKey = hexEncode(contact.publicKeyData) if hasattr(contact, "status"): contactStatus = contact.status else: contactStatus = -1 return [contact.name, contactKey, self.STATII[contactStatus]]
class MainWindow( QMainWindow ) : OFFLINE = UserSession.OFFLINE CONNECTING = UserSession.CONNECTING ONLINE = UserSession.ONLINE DISCONNECTING = UserSession.DISCONNECTING def __init__( self, seedNodes, reactor ) : QMainWindow.__init__( self ) self.ui = Ui_MainWindow() self.ui.setupUi( self ) self._setupIcons() self.seedNodes = seedNodes self.reactor = reactor self.session = UserSession( seedNodes, reactor ) self.sm = self.session.sm self.ev = Eventer() self.profile = None self.baseTitle = str(self.windowTitle()) self.exitAfterOffline = False self.reconnector = None self.keyInfoDialog = None self.addContactDialog = None self.sm.insertCallback( self._onStateChange ) self.connect( self.ui.createKeyButton, SIGNAL('clicked()'), self.ui.actionCreateKey.trigger ) self.connect( self.ui.createKeyButton1, SIGNAL('clicked()'), self.ui.actionCreateKey.trigger ) self.connect( self.ui.goOnlineButton, SIGNAL('clicked()'), self.ui.actionGoOnline.trigger ) self.ui.contacts.setWrapping( True ) self.connect( self.ui.contacts, SIGNAL('customContextMenuRequested(const QPoint&)'), self._onContactsContextMenuRequested ) self.connect( self.ui.contacts, SIGNAL('itemDoubleClicked(QListWidgetItem*)'), self._onContactDoubleClicked ) self.contextMenu = QMenu() self.contextMenu.addAction( self.ui.actionCheckStatus ) self.contextMenu.addAction( self.ui.actionRemoveContact ) self.contextMenu.addAction( self.ui.actionContactInfo ) self.connect( self.contextMenu, SIGNAL('triggered(QAction*)'), self._onContextMenuAction ) self.actionManager = ActionManager( self.contextMenu ) self.appletServer = AppletServer( self.session, self.actionManager, self.reactor ) self.appletServer.registerServices() self.appletServer.registerActions() self.statusChecker = StatusChecker( self.ui.contacts, self.session, self.ev, self.reactor ) self.contactInfoDialogs = {} self.trayMenu = QMenu( self ) self.trayMenu.addAction( self.ui.actionExit ) if HAS_TRAY : self.trayIcon = TrayIcon( QPixmap(':/images/cspace_offline.png'), 'CSpace', self.trayMenu, self ) self.connect( self.trayIcon, SIGNAL('clicked(const QPoint&,int)'), self._onTrayClicked ) self.trayIcon.show() else : self.trayIcon = None self.editPermissionsDialog = None self.autoUpdater = AutoUpdater( self.reactor ) self.autoUpdater.setUpdateCallback( self._onUpdateDownloaded ) self._updateUI() self._doCheckSettings() def _setupIcons( self ) : def makeIcon( name ) : icon = QIcon() for res in '16 22 32'.split() : icon.addFile( ':/images/%s%s.png' % (name,res) ) return icon actionIcons = ( 'CreateKey register', 'GoOnline connect', 'GoOffline disconnect', 'Exit exit', 'AddContact user_add', 'RefreshStatus refresh', 'CheckStatus refresh', 'ContactInfo contact_info', 'RemoveContact user_remove', 'EditPermissions edit_permissions', 'KeyInfo key_info' ) for s in actionIcons : name,icon = s.split() action = getattr( self.ui, 'action%s'%name ) action.setIcon( makeIcon(icon) ) def _updateUI( self ) : state = self.sm.current() online = (state == self.ONLINE) offline = (state == self.OFFLINE) connecting = (state == self.CONNECTING) disconnecting = (state == self.DISCONNECTING) self.ui.actionCreateKey.setEnabled( offline ) self.ui.actionGoOnline.setEnabled( offline ) self.ui.actionGoOffline.setEnabled( online ) self.ui.actionKeyInfo.setEnabled( online ) self.ui.actionExit.setEnabled( True ) self.ui.actionAddContact.setEnabled( online ) self.ui.actionRefreshStatus.setEnabled( online ) self.ui.actionCheckStatus.setEnabled( online ) self.ui.actionContactInfo.setEnabled( online ) self.ui.actionRemoveContact.setEnabled( online ) self.ui.actionEditPermissions.setEnabled( online ) self.ui.actionAboutCSpace.setEnabled( True ) if online : msg = 'Online' if offline : msg = 'Offline' if connecting : msg = 'Going Online...' if disconnecting : msg = 'Going Offline...' if not offline : msg = '%s - %s' % (self.profile.name,msg) self.setWindowTitle( ' - '.join([msg,self.baseTitle]) ) self.ui.createKeyButton.setEnabled( offline ) self.ui.createKeyButton1.setEnabled( offline ) self.ui.goOnlineButton.setEnabled( offline ) self.ui.connectCancelButton.setEnabled( connecting or disconnecting ) if online : self.ui.stack.setCurrentWidget( self.ui.contactsPage ) elif offline : if self.reconnector : assert self.reconnector.reconnecting self.ui.connectCancelButton.setEnabled( True ) self.ui.stack.setCurrentWidget( self.ui.connectingPage ) self.ui.connectStatus.setText('<b>%s<br/>Reconnecting in %d second(s)...</b>' % (self.reconnector.errorMsg, self.reconnector.timeLeft) ) elif listProfiles() : self.ui.stack.setCurrentWidget( self.ui.offlinePage ) else : self.ui.stack.setCurrentWidget( self.ui.offlineNoUsersPage ) elif connecting : self.ui.stack.setCurrentWidget( self.ui.connectingPage ) self.ui.connectStatus.setText( '<b>Connecting...</b>' ) elif disconnecting : self.ui.stack.setCurrentWidget( self.ui.connectingPage ) self.ui.connectStatus.setText( '<b>Disconnecting...</b>' ) def _onStateChange( self ) : if self.sm.previous() == self.ONLINE : self.statusChecker.shutdown() self.appletServer.clearConnections() for dlg in self.contactInfoDialogs.values() : dlg.close() assert not self.contactInfoDialogs if self.editPermissionsDialog : self.editPermissionsDialog.close() assert not self.editPermissionsDialog self.ui.contacts.clear() if self.trayIcon is not None : self.trayIcon.setIcon( QPixmap(':/images/cspace_offline.png') ) self.trayIcon.setToolTip( 'CSpace' ) if self.sm.current() == self.OFFLINE : self.profile = None if self.reconnector : if self.sm.previous() == self.CONNECTING : msg = 'Connect failed.' else : msg = 'Disconnected.' self.reconnector.startReconnectTimer( msg ) if self.exitAfterOffline : self.reactor.callLater( 0, self.ui.actionExit.trigger ) elif self.sm.current() == self.ONLINE : self.ui.contacts.clear() for contactName in self.profile.getContactNames() : contact = self.profile.getContactByName( contactName ) self._addContactItem( contact ) self.statusChecker.initialize() if self.trayIcon is not None : self.trayIcon.setIcon( QPixmap(':/images/cspace_online.png') ) self.trayIcon.setToolTip( 'CSpace - %s' % self.profile.name ) self._updateUI() def _doCheckSettings( self ) : st = localSettings() windowWidth = st.getInt( 'Settings/WindowWidth', 0 ) windowHeight = st.getInt( 'Settings/WindowHeight', 0 ) if (windowWidth > 0) and (windowHeight > 0) : windowWidth = max( windowWidth, 100 ) windowHeight = max( windowHeight, 100 ) self.resize( QSize(windowWidth,windowHeight).expandedTo(self.minimumSizeHint()) ) profiles = listProfiles() entries = [entry for userName,keyId,entry in profiles] if st.getInt('Settings/RememberKey',0) : entry = st.getString('Settings/SavedProfile') password = st.getString('Settings/SavedPassword') if entry and password and (entry in entries) : profile = loadProfile( entry, password ) if profile : self._doGoOnline( profile ) def _sortContacts( self ) : self.ui.contacts.sortItems() def _addContactItem( self, contact ) : contactItem = QListWidgetItem( QIcon(':/images/user_offline.png'), contact.name, self.ui.contacts ) self._sortContacts() self.statusChecker.check( contactItem ) def _cancelReconnect( self ) : if self.reconnector : self.reconnector.shutdown() self.reconnector = None self._updateUI() @pyqtSignature( '' ) def on_connectCancelButton_clicked( self ) : self._cancelReconnect() self.exitAfterOffline = False self.session.shutdown() @pyqtSignature( '' ) def on_actionCreateKey_triggered( self ) : self._cancelReconnect() dlg = CreateKeyDialog( self, self.reactor ) result = dlg.exec_() if result == QDialog.Rejected : return userName = dlg.userName password = dlg.password rsaKey = dlg.rsaKey keyId = dlg.keyId profile = createProfile( rsaKey, password, userName, keyId ) dlg = CreateKeyDoneDialog( self, keyId ) result = dlg.exec_() if result == QDialog.Rejected : return st = localSettings() if dlg.rememberKey : st.setInt( 'Settings/RememberKey', 1 ) st.setString( 'Settings/SavedProfile', profile.storeEntry ) st.setString( 'Settings/SavedPassword', password ) else : st.setInt( 'Settings/RememberKey', 0 ) st.remove( 'Settings/SavedProfile' ) st.remove( 'Settings/SavedPassword' ) self._updateUI() self._doGoOnline( profile ) @pyqtSignature( '' ) def on_actionGoOnline_triggered( self ) : self._cancelReconnect() dlg = GoOnlineDialog( self ) if not dlg.profiles : QMessageBox.warning( self, 'No Private Keys', 'No Private Keys have been created. Please create your Private Key first.' ) self.ui.actionCreateKey.trigger() return result = dlg.exec_() if result == QDialog.Rejected : return self._doGoOnline( dlg.profile ) def _doGoOnline( self, profile ) : self.profile = profile self.session.goOnline( self.profile ) self.reconnector = Reconnector( self.profile, self._onReconnectTimer, self.reactor ) def _onReconnectTimer( self ) : assert self.reconnector if self.reconnector.timeLeft > 0 : self._updateUI() return self.profile = self.reconnector.profile self._cancelReconnect() self.session.goOnline( self.profile ) self.reconnector = Reconnector( self.profile, self._onReconnectTimer, self.reactor ) @pyqtSignature( '' ) def on_actionGoOffline_triggered( self ) : self._cancelReconnect() self.session.goOffline() @pyqtSignature( '' ) def on_actionKeyInfo_triggered( self ) : assert self.sm.current() == self.ONLINE if self.keyInfoDialog : self.keyInfoDialog.show() self.keyInfoDialog.activateWindow() return def onDialogClose() : self.keyInfoDialog = None self.sm.removeCallback( callbackId ) def onStateChange() : self.keyInfoDialog.close() assert not self.keyInfoDialog dlg = KeyInfoDialog( self, self.profile, onDialogClose ) dlg.show() self.keyInfoDialog = dlg callbackId = self.sm.insertCallback( onStateChange, src=self.ONLINE ) @pyqtSignature( '' ) def on_actionExit_triggered( self ) : self._cancelReconnect() if self.sm.current() == self.ONLINE : self.exitAfterOffline = True self.session.goOffline() else : self.exitAfterOffline = False self.appletServer.shutdown() self.session.shutdown() if self.trayIcon is not None : self.trayIcon.hide() st = localSettings() st.setInt( 'Settings/WindowWidth', self.width() ) st.setInt( 'Settings/WindowHeight', self.height() ) qApp.exit( 0 ) @pyqtSignature( '' ) def on_actionAddContact_triggered( self ) : assert self.sm.current() == self.ONLINE if self.addContactDialog : self.addContactDialog.show() self.addContactDialog.activateWindow() return def onDialogClose() : self.addContactDialog = None self.sm.removeCallback( callbackId ) def onAddContact( contact ) : self.profile.addContact( contact ) self._addContactItem( contact ) saveProfileContacts( self.profile ) def onStateChange() : self.addContactDialog.close() assert not self.addContactDialog dlg = AddContactDialog( self, self.reactor, self.profile, onAddContact, onDialogClose ) dlg.show() self.addContactDialog = dlg callbackId = self.sm.insertCallback( onStateChange, src=self.ONLINE ) @pyqtSignature( '' ) def on_actionRefreshStatus_triggered( self ) : assert self.sm.current() == self.ONLINE for i in range(self.ui.contacts.count()) : item = self.ui.contacts.item( i ) self.statusChecker.check( item ) def _onContactsContextMenuRequested( self, pos ) : item = self.ui.contacts.itemAt( pos ) if item is None : return curItem = self.ui.contacts.currentItem() if item is not curItem : return self.contextMenu.popup( self.ui.contacts.mapToGlobal(pos) ) def _onContactDoubleClicked( self, item ) : assert self.sm.current() == self.ONLINE item = self.ui.contacts.currentItem() if item is None : return self.actionManager.execDefaultAction( str(item.text()) ) def _onContextMenuAction( self, action ) : assert self.sm.current() == self.ONLINE item = self.ui.contacts.currentItem() if item is None : return self.actionManager.execAction( action, str(item.text()) ) @pyqtSignature( '' ) def on_actionCheckStatus_triggered( self ) : assert self.sm.current() == self.ONLINE item = self.ui.contacts.currentItem() if item is None : return self.statusChecker.check( item ) @pyqtSignature( '' ) def on_actionRemoveContact_triggered( self ) : assert self.sm.current() == self.ONLINE item = self.ui.contacts.currentItem() if item is None : return contact = self.profile.getContactByName( str(item.text()) ) self.ev.trigger( 'contact.remove', item, contact ) self.ui.contacts.takeItem( self.ui.contacts.row(item) ) self.profile.removeContact( contact ) saveProfileContacts( self.profile ) permissions = self.session.getPermissions() permissions.removeContact( contact.name ) if permissions.isModified() : permissions.savePermissions() @pyqtSignature( '' ) def on_actionContactInfo_triggered( self ) : assert self.sm.current() == self.ONLINE item = self.ui.contacts.currentItem() if item is None : return dlg = self.contactInfoDialogs.get( item ) if dlg is None : contact = self.profile.getContactByName( str(item.text()) ) # FIXME: why is obj needed? obj = Dummy() def onUpdateName( newName ) : oldName = contact.name self.profile.changeContactName( oldName, newName ) saveProfileContacts( self.profile ) item.setText( newName ) self._sortContacts() permissions = self.session.getPermissions() permissions.changeContactName( oldName, newName ) if permissions.isModified() : permissions.savePermissions() def onClose() : if obj.callbackId is not None : self.ev.remove( obj.callbackId ) del self.contactInfoDialogs[item] def onContactRemoved( removedItem, contact ) : if removedItem is item : dlg.close() dlg = ContactInfoDialog( self, contact, self.profile, onUpdateName, onClose ) self.contactInfoDialogs[item] = dlg obj.callbackId = self.ev.register( 'contact.remove', onContactRemoved ) dlg.show() dlg.activateWindow() @pyqtSignature( '' ) def on_actionEditPermissions_triggered( self ) : assert self.sm.current() == self.ONLINE if self.editPermissionsDialog : self.editPermissionsDialog.show() self.editPermissionsDialog.activateWindow() return def onClose() : self.editPermissionsDialog = None dlg = PermissionsDialog( self, self.session.getPermissions(), onClose ) dlg.show() self.editPermissionsDialog = dlg @pyqtSignature( '' ) def on_actionAboutCSpace_triggered( self ) : buildNumber = currentBuildNumber() listenPortMsg = '' if self.sm.current() == self.ONLINE : listenPortMsg = 'Applet Server Port: %d\n' % \ self.appletServer.getListenPort() QMessageBox.information( self, 'About CSpace', 'CSpace Peer-to-Peer Communication Platform\n'+ 'Build %d\n' % buildNumber + listenPortMsg + '(c) Tachyon Technologies 2006', QMessageBox.Ok ) def _onTrayClicked( self, pos, button ) : if (self.isMinimized()) or (not self.isVisible()) : self.showNormal() self.activateWindow() def _onUpdateDownloaded( self, updateFileName, updateFileData ) : def onInstall() : installerFile = self.autoUpdater.saveInstaller( updateFileName, updateFileData ) global execFileAfterExit execFileAfterExit = installerFile self.ui.actionExit.trigger() self.notifyWindow.close() self.notifyWindow = UpdateNotifyWindow( self.reactor, onInstall ) def closeEvent( self, ev ) : self.showMinimized() if self.trayIcon is not None : self.hide() ev.ignore()
class MainWindow( QMainWindow ) : OFFLINE = UserSession.OFFLINE CONNECTING = UserSession.CONNECTING ONLINE = UserSession.ONLINE DISCONNECTING = UserSession.DISCONNECTING def __init__( self, seedNodes, reactor ) : QMainWindow.__init__( self ) self.ui = Ui_MainWindow() self.ui.setupUi( self ) self._setupIcons() self.seedNodes = seedNodes self.reactor = reactor self.session = UserSession( seedNodes, reactor ) self.sm = self.session.sm self.ev = Eventer() self.profile = None self.baseTitle = str(self.windowTitle()) self.exitAfterOffline = False self.reconnector = None self.keyInfoDialog = None self.addContactDialog = None self.sm.insertCallback( self._onStateChange ) self.connect( self.ui.createKeyButton, SIGNAL('clicked()'), self.ui.actionCreateKey.trigger ) self.connect( self.ui.createKeyButton1, SIGNAL('clicked()'), self.ui.actionCreateKey.trigger ) self.connect( self.ui.goOnlineButton, SIGNAL('clicked()'), self.ui.actionGoOnline.trigger ) self.ui.contacts.setWrapping( True ) self.connect( self.ui.contacts, SIGNAL('customContextMenuRequested(const QPoint&)'), self._onContactsContextMenuRequested ) self.connect( self.ui.contacts, SIGNAL('itemDoubleClicked(QListWidgetItem*)'), self._onContactDoubleClicked ) self.contextMenu = QMenu() self.contextMenu.addAction( self.ui.actionCheckStatus ) self.contextMenu.addAction( self.ui.actionRemoveContact ) self.contextMenu.addAction( self.ui.actionContactInfo ) self.connect( self.contextMenu, SIGNAL('triggered(QAction*)'), self._onContextMenuAction ) self.actionManager = ActionManager( self.contextMenu ) self.appletServer = AppletServer( self.session, self.actionManager, self.reactor ) self.statusChecker = StatusChecker( self.ui.contacts, self.session, self.ev, self.reactor ) self.contactInfoDialogs = {} self.trayMenu = QMenu( self ) self.trayMenu.addAction( self.ui.actionExit ) if HAS_TRAY : self.trayIcon = TrayIcon( QPixmap(':/images/cspace_offline.png'), 'CSpace', self.trayMenu, self ) self.connect( self.trayIcon, SIGNAL('clicked(const QPoint&,int)'), self._onTrayClicked ) self.trayIcon.show() else : self.trayIcon = None self.editPermissionsDialog = None self.autoUpdater = AutoUpdater( self.reactor ) self.autoUpdater.setUpdateCallback( self._onUpdateDownloaded ) self._updateUI() self._doCheckSettings() def _setupIcons( self ) : def makeIcon( name ) : icon = QIcon() for res in '16 22 32'.split() : icon.addFile( ':/images/%s%s.png' % (name,res) ) return icon actionIcons = ( 'CreateKey register', 'GoOnline connect', 'GoOffline disconnect', 'Exit exit', 'AddContact user_add', 'RefreshStatus refresh', 'CheckStatus refresh', 'ContactInfo contact_info', 'RemoveContact user_remove', 'EditPermissions edit_permissions', 'KeyInfo key_info' ) for s in actionIcons : name,icon = s.split() action = getattr( self.ui, 'action%s'%name ) action.setIcon( makeIcon(icon) ) def _updateUI( self ) : state = self.sm.current() online = (state == self.ONLINE) offline = (state == self.OFFLINE) connecting = (state == self.CONNECTING) disconnecting = (state == self.DISCONNECTING) self.ui.actionCreateKey.setEnabled( offline ) self.ui.actionGoOnline.setEnabled( offline ) self.ui.actionGoOffline.setEnabled( online ) self.ui.actionKeyInfo.setEnabled( online ) self.ui.actionExit.setEnabled( True ) self.ui.actionAddContact.setEnabled( online ) self.ui.actionRefreshStatus.setEnabled( online ) self.ui.actionCheckStatus.setEnabled( online ) self.ui.actionContactInfo.setEnabled( online ) self.ui.actionRemoveContact.setEnabled( online ) self.ui.actionEditPermissions.setEnabled( online ) self.ui.actionAboutCSpace.setEnabled( True ) if online : msg = 'Online' if offline : msg = 'Offline' if connecting : msg = 'Going Online...' if disconnecting : msg = 'Going Offline...' if not offline : msg = '%s - %s' % (self.profile.name,msg) self.setWindowTitle( ' - '.join([msg,self.baseTitle]) ) self.ui.createKeyButton.setEnabled( offline ) self.ui.createKeyButton1.setEnabled( offline ) self.ui.goOnlineButton.setEnabled( offline ) self.ui.connectCancelButton.setEnabled( connecting or disconnecting ) if online : self.ui.stack.setCurrentWidget( self.ui.contactsPage ) elif offline : if self.reconnector : assert self.reconnector.reconnecting self.ui.connectCancelButton.setEnabled( True ) self.ui.stack.setCurrentWidget( self.ui.connectingPage ) self.ui.connectStatus.setText('<b>%s<br/>Reconnecting in %d second(s)...</b>' % (self.reconnector.errorMsg, self.reconnector.timeLeft) ) elif listProfiles() : self.ui.stack.setCurrentWidget( self.ui.offlinePage ) else : self.ui.stack.setCurrentWidget( self.ui.offlineNoUsersPage ) elif connecting : self.ui.stack.setCurrentWidget( self.ui.connectingPage ) self.ui.connectStatus.setText( '<b>Connecting...</b>' ) elif disconnecting : self.ui.stack.setCurrentWidget( self.ui.connectingPage ) self.ui.connectStatus.setText( '<b>Disconnecting...</b>' ) def _onStateChange( self ) : if self.sm.previous() == self.ONLINE : self.statusChecker.shutdown() self.appletServer.clearConnections() for dlg in self.contactInfoDialogs.values() : dlg.close() assert not self.contactInfoDialogs if self.editPermissionsDialog : self.editPermissionsDialog.close() assert not self.editPermissionsDialog self.ui.contacts.clear() if self.trayIcon is not None : self.trayIcon.setIcon( QPixmap(':/images/cspace_offline.png') ) self.trayIcon.setToolTip( 'CSpace' ) if self.sm.current() == self.OFFLINE : self.profile = None if self.reconnector : if self.sm.previous() == self.CONNECTING : msg = 'Connect failed.' else : msg = 'Disconnected.' self.reconnector.startReconnectTimer( msg ) if self.exitAfterOffline : self.reactor.callLater( 0, self.ui.actionExit.trigger ) elif self.sm.current() == self.ONLINE : self.ui.contacts.clear() for contactName in self.profile.getContactNames() : contact = self.profile.getContactByName( contactName ) self._addContactItem( contact ) self.statusChecker.initialize() if self.trayIcon is not None : self.trayIcon.setIcon( QPixmap(':/images/cspace_online.png') ) self.trayIcon.setToolTip( 'CSpace - %s' % self.profile.name ) self._updateUI() def _doCheckSettings( self ) : st = localSettings() windowWidth = st.getInt( 'Settings/WindowWidth', 0 ) windowHeight = st.getInt( 'Settings/WindowHeight', 0 ) if (windowWidth > 0) and (windowHeight > 0) : windowWidth = max( windowWidth, 100 ) windowHeight = max( windowHeight, 100 ) self.resize( QSize(windowWidth,windowHeight).expandedTo(self.minimumSizeHint()) ) profiles = listProfiles() entries = [entry for userName,keyId,entry in profiles] if st.getInt('Settings/RememberKey',0) : entry = st.getString('Settings/SavedProfile') password = st.getString('Settings/SavedPassword') if entry and password and (entry in entries) : profile = loadProfile( entry, password ) if profile : self._doGoOnline( profile ) def _sortContacts( self ) : self.ui.contacts.sortItems() def _addContactItem( self, contact ) : contactItem = QListWidgetItem( QIcon(':/images/user_offline.png'), contact.name, self.ui.contacts ) self._sortContacts() self.statusChecker.check( contactItem ) def _cancelReconnect( self ) : if self.reconnector : self.reconnector.shutdown() self.reconnector = None self._updateUI() @pyqtSignature( '' ) def on_connectCancelButton_clicked( self ) : self._cancelReconnect() self.exitAfterOffline = False self.session.shutdown() @pyqtSignature( '' ) def on_actionCreateKey_triggered( self ) : self._cancelReconnect() dlg = CreateKeyDialog( self, self.reactor ) result = dlg.exec_() if result == QDialog.Rejected : return userName = dlg.userName password = dlg.password rsaKey = dlg.rsaKey keyId = dlg.keyId profile = createProfile( rsaKey, password, userName, keyId ) dlg = CreateKeyDoneDialog( self, keyId ) result = dlg.exec_() if result == QDialog.Rejected : return st = localSettings() if dlg.rememberKey : st.setInt( 'Settings/RememberKey', 1 ) st.setString( 'Settings/SavedProfile', profile.storeEntry ) st.setString( 'Settings/SavedPassword', password ) else : st.setInt( 'Settings/RememberKey', 0 ) st.remove( 'Settings/SavedProfile' ) st.remove( 'Settings/SavedPassword' ) self._updateUI() self._doGoOnline( profile ) @pyqtSignature( '' ) def on_actionGoOnline_triggered( self ) : self._cancelReconnect() dlg = GoOnlineDialog( self ) if not dlg.profiles : QMessageBox.warning( self, 'No Private Keys', 'No Private Keys have been created. Please create your Private Key first.' ) self.ui.actionCreateKey.trigger() return result = dlg.exec_() if result == QDialog.Rejected : return self._doGoOnline( dlg.profile ) def _doGoOnline( self, profile ) : self.profile = profile self.session.goOnline( self.profile ) self.reconnector = Reconnector( self.profile, self._onReconnectTimer, self.reactor ) def _onReconnectTimer( self ) : assert self.reconnector if self.reconnector.timeLeft > 0 : self._updateUI() return self.profile = self.reconnector.profile self._cancelReconnect() self.session.goOnline( self.profile ) self.reconnector = Reconnector( self.profile, self._onReconnectTimer, self.reactor ) @pyqtSignature( '' ) def on_actionGoOffline_triggered( self ) : self._cancelReconnect() self.session.goOffline() @pyqtSignature( '' ) def on_actionKeyInfo_triggered( self ) : assert self.sm.current() == self.ONLINE if self.keyInfoDialog : self.keyInfoDialog.show() self.keyInfoDialog.activateWindow() return def onDialogClose() : self.keyInfoDialog = None self.sm.removeCallback( callbackId ) def onStateChange() : self.keyInfoDialog.close() assert not self.keyInfoDialog dlg = KeyInfoDialog( self, self.profile, onDialogClose ) dlg.show() self.keyInfoDialog = dlg callbackId = self.sm.insertCallback( onStateChange, src=self.ONLINE ) @pyqtSignature( '' ) def on_actionExit_triggered( self ) : self._cancelReconnect() if self.sm.current() == self.ONLINE : self.exitAfterOffline = True self.session.goOffline() else : self.exitAfterOffline = False self.appletServer.shutdown() self.session.shutdown() if self.trayIcon is not None : self.trayIcon.hide() st = localSettings() st.setInt( 'Settings/WindowWidth', self.width() ) st.setInt( 'Settings/WindowHeight', self.height() ) qApp.exit( 0 ) @pyqtSignature( '' ) def on_actionAddContact_triggered( self ) : assert self.sm.current() == self.ONLINE if self.addContactDialog : self.addContactDialog.show() self.addContactDialog.activateWindow() return def onDialogClose() : self.addContactDialog = None self.sm.removeCallback( callbackId ) def onAddContact( contact ) : self.profile.addContact( contact ) self._addContactItem( contact ) saveProfileContacts( self.profile ) def onStateChange() : self.addContactDialog.close() assert not self.addContactDialog dlg = AddContactDialog( self, self.reactor, self.profile, onAddContact, onDialogClose ) dlg.show() self.addContactDialog = dlg callbackId = self.sm.insertCallback( onStateChange, src=self.ONLINE ) @pyqtSignature( '' ) def on_actionRefreshStatus_triggered( self ) : assert self.sm.current() == self.ONLINE for i in range(self.ui.contacts.count()) : item = self.ui.contacts.item( i ) self.statusChecker.check( item ) def _onContactsContextMenuRequested( self, pos ) : item = self.ui.contacts.itemAt( pos ) if item is None : return curItem = self.ui.contacts.currentItem() if item is not curItem : return self.contextMenu.popup( self.ui.contacts.mapToGlobal(pos) ) def _onContactDoubleClicked( self, item ) : assert self.sm.current() == self.ONLINE item = self.ui.contacts.currentItem() if item is None : return self.actionManager.execDefaultAction( str(item.text()) ) def _onContextMenuAction( self, action ) : assert self.sm.current() == self.ONLINE item = self.ui.contacts.currentItem() if item is None : return self.actionManager.execAction( action, str(item.text()) ) @pyqtSignature( '' ) def on_actionCheckStatus_triggered( self ) : assert self.sm.current() == self.ONLINE item = self.ui.contacts.currentItem() if item is None : return self.statusChecker.check( item ) @pyqtSignature( '' ) def on_actionRemoveContact_triggered( self ) : assert self.sm.current() == self.ONLINE item = self.ui.contacts.currentItem() if item is None : return contact = self.profile.getContactByName( str(item.text()) ) self.ev.trigger( 'contact.remove', item, contact ) self.ui.contacts.takeItem( self.ui.contacts.row(item) ) self.profile.removeContact( contact ) saveProfileContacts( self.profile ) permissions = self.session.getPermissions() permissions.removeContact( contact.name ) if permissions.isModified() : permissions.savePermissions() @pyqtSignature( '' ) def on_actionContactInfo_triggered( self ) : assert self.sm.current() == self.ONLINE item = self.ui.contacts.currentItem() if item is None : return dlg = self.contactInfoDialogs.get( item ) if dlg is None : contact = self.profile.getContactByName( str(item.text()) ) # FIXME: why is obj needed? obj = Dummy() def onUpdateName( newName ) : oldName = contact.name self.profile.changeContactName( oldName, newName ) saveProfileContacts( self.profile ) item.setText( newName ) self._sortContacts() permissions = self.session.getPermissions() permissions.changeContactName( oldName, newName ) if permissions.isModified() : permissions.savePermissions() def onClose() : if obj.callbackId is not None : self.ev.remove( obj.callbackId ) del self.contactInfoDialogs[item] def onContactRemoved( removedItem, contact ) : if removedItem is item : dlg.close() dlg = ContactInfoDialog( self, contact, self.profile, onUpdateName, onClose ) self.contactInfoDialogs[item] = dlg obj.callbackId = self.ev.register( 'contact.remove', onContactRemoved ) dlg.show() dlg.activateWindow() @pyqtSignature( '' ) def on_actionEditPermissions_triggered( self ) : assert self.sm.current() == self.ONLINE if self.editPermissionsDialog : self.editPermissionsDialog.show() self.editPermissionsDialog.activateWindow() return def onClose() : self.editPermissionsDialog = None dlg = PermissionsDialog( self, self.session.getPermissions(), onClose ) dlg.show() self.editPermissionsDialog = dlg @pyqtSignature( '' ) def on_actionAboutCSpace_triggered( self ) : buildNumber = currentBuildNumber() listenPortMsg = '' if self.sm.current() == self.ONLINE : listenPortMsg = 'Applet Server Port: %d\n' % \ self.appletServer.getListenPort() QMessageBox.information( self, 'About CSpace', 'CSpace Peer-to-Peer Communication Platform\n'+ 'Build %d\n' % buildNumber + listenPortMsg + '(c) Tachyon Technologies 2006', QMessageBox.Ok ) def _onTrayClicked( self, pos, button ) : if (self.isMinimized()) or (not self.isVisible()) : self.showNormal() self.activateWindow() def _onUpdateDownloaded( self, updateFileName, updateFileData ) : def onInstall() : installerFile = self.autoUpdater.saveInstaller( updateFileName, updateFileData ) global execFileAfterExit execFileAfterExit = installerFile self.ui.actionExit.trigger() self.notifyWindow.close() self.notifyWindow = UpdateNotifyWindow( self.reactor, onInstall ) def closeEvent( self, ev ) : self.showMinimized() if self.trayIcon is not None : self.hide() ev.ignore()