73 proxyAuthenticationRequired, |
78 proxyAuthenticationRequired, |
74 ) |
79 ) |
75 from eric7.EricWidgets import EricErrorMessage, EricFileDialog, EricMessageBox |
80 from eric7.EricWidgets import EricErrorMessage, EricFileDialog, EricMessageBox |
76 from eric7.EricWidgets.EricApplication import ericApp |
81 from eric7.EricWidgets.EricApplication import ericApp |
77 from eric7.EricWidgets.EricClickableLabel import EricClickableLabel |
82 from eric7.EricWidgets.EricClickableLabel import EricClickableLabel |
|
83 from eric7.EricWidgets.EricListSelectionDialog import EricListSelectionDialog |
78 from eric7.EricWidgets.EricMainWindow import EricMainWindow |
84 from eric7.EricWidgets.EricMainWindow import EricMainWindow |
79 from eric7.EricWidgets.EricSingleApplication import EricSingleApplicationServer |
85 from eric7.EricWidgets.EricSingleApplication import EricSingleApplicationServer |
80 from eric7.EricWidgets.EricToolBarManager import EricToolBarManager |
86 from eric7.EricWidgets.EricToolBarManager import EricToolBarManager |
81 from eric7.EricWidgets.EricZoomWidget import EricZoomWidget |
87 from eric7.EricWidgets.EricZoomWidget import EricZoomWidget |
82 from eric7.Globals import getConfig |
88 from eric7.Globals import getConfig |
252 self.profiles = Preferences.getUI("ViewProfiles") |
258 self.profiles = Preferences.getUI("ViewProfiles") |
253 |
259 |
254 splash.showMessage(self.tr("Initializing Basic Services...")) |
260 splash.showMessage(self.tr("Initializing Basic Services...")) |
255 logging.getLogger(__name__).debug("Initializing Basic Services...") |
261 logging.getLogger(__name__).debug("Initializing Basic Services...") |
256 |
262 |
257 # Generate the redirection helpers |
|
258 self.stdout = EricStdRedirector(False, self) |
|
259 self.stdout.stdoutString.connect(self.appendToStdout) |
|
260 self.stderr = EricStdRedirector(True, self) |
|
261 self.stderr.stderrString.connect(self.appendToStderr) |
|
262 # Redirect sys.stdout and/or sys.stderr if those are None |
263 # Redirect sys.stdout and/or sys.stderr if those are None |
263 if sys.stdout is None or UserInterface.ReleaseMode: |
264 if sys.stdout is None or UserInterface.ReleaseMode: |
264 sys.stdout = self.stdout |
265 self.__stdout = EricStdRedirector(False, self) |
|
266 self.__stdout.stdoutString.connect(self.appendToStdout) |
|
267 sys.stdout = self.__stdout |
265 if sys.stderr is None or UserInterface.ReleaseMode: |
268 if sys.stderr is None or UserInterface.ReleaseMode: |
266 sys.stderr = self.stderr |
269 self.__stderr = EricStdRedirector(True, self) |
|
270 self.__stderr.stderrString.connect(self.appendToStderr) |
|
271 sys.stderr = self.__stderr |
267 |
272 |
268 # create the remote server interface |
273 # create the remote server interface |
269 logging.getLogger(__name__).debug("Creating 'eric-ide' Server Interface...") |
274 logging.getLogger(__name__).debug("Creating 'eric-ide' Server Interface...") |
270 self.__ericServerInterface = EricServerInterface(self) |
275 self.__ericServerInterface = EricServerInterface(self) |
271 # register it early because it is needed very soon |
276 # register it early because it is needed very soon |
3106 ) |
3111 ) |
3107 ) |
3112 ) |
3108 self.webBrowserAct.triggered.connect(self.__startWebBrowser) |
3113 self.webBrowserAct.triggered.connect(self.__startWebBrowser) |
3109 self.actions.append(self.webBrowserAct) |
3114 self.actions.append(self.webBrowserAct) |
3110 |
3115 |
|
3116 if importlib.util.find_spec("fido2"): |
|
3117 self.securityKeyMgmtAct = EricAction( |
|
3118 self.tr("FIDO2 Security Key Management"), |
|
3119 EricPixmapCache.getIcon("securityKey"), |
|
3120 self.tr("FIDO2 Security Key Management..."), |
|
3121 0, |
|
3122 0, |
|
3123 self, |
|
3124 "fido2_security_key_mgmt", |
|
3125 ) |
|
3126 self.securityKeyMgmtAct.setStatusTip( |
|
3127 self.tr("Start the FIDO2 Security Key Management tool") |
|
3128 ) |
|
3129 self.securityKeyMgmtAct.setWhatsThis( |
|
3130 self.tr( |
|
3131 """<b>FIDO2 Security Key Management</b>""" |
|
3132 """<p>Start a tool to manage FIDO2 securit y keys.</p>""" |
|
3133 ) |
|
3134 ) |
|
3135 self.securityKeyMgmtAct.triggered.connect(self.__startFido2SecurityKeyMgmt) |
|
3136 self.actions.append(self.securityKeyMgmtAct) |
|
3137 else: |
|
3138 self.securityKeyMgmtAct = None |
|
3139 |
3111 self.iconEditorAct = EricAction( |
3140 self.iconEditorAct = EricAction( |
3112 self.tr("Icon Editor"), |
3141 self.tr("Icon Editor"), |
3113 EricPixmapCache.getIcon("iconEditor"), |
3142 EricPixmapCache.getIcon("iconEditor"), |
3114 self.tr("&Icon Editor..."), |
3143 self.tr("&Icon Editor..."), |
3115 0, |
3144 0, |
3818 ############################################################## |
3847 ############################################################## |
3819 |
3848 |
3820 self.__menus["server"] = self.__ericServerInterface.initMenu() |
3849 self.__menus["server"] = self.__ericServerInterface.initMenu() |
3821 |
3850 |
3822 ############################################################## |
3851 ############################################################## |
|
3852 ## Sessions menu |
|
3853 ############################################################## |
|
3854 |
|
3855 self.__menus["sessions"] = QMenu(self.tr("Sessions")) |
|
3856 self.__menus["sessions"].aboutToShow.connect(self.__showSessionsMenu) |
|
3857 |
|
3858 ############################################################## |
3823 ## File menu |
3859 ## File menu |
3824 ############################################################## |
3860 ############################################################## |
3825 |
3861 |
3826 self.__menus["file"] = self.viewmanager.initFileMenu() |
3862 self.__menus["file"] = self.viewmanager.initFileMenu() |
3827 mb.addMenu(self.__menus["file"]) |
3863 mb.addMenu(self.__menus["file"]) |
3828 self.__menus["file"].addSeparator() |
3864 self.__menus["file"].addSeparator() |
3829 self.__menus["file"].addAction(self.saveSessionAct) |
3865 self.__menus["file"].addMenu(self.__menus["sessions"]) |
3830 self.__menus["file"].addAction(self.loadSessionAct) |
|
3831 self.__menus["file"].addSeparator() |
3866 self.__menus["file"].addSeparator() |
3832 self.__menus["file"].addAction(self.restartAct) |
3867 self.__menus["file"].addAction(self.restartAct) |
3833 self.__menus["file"].addAction(self.exitAct) |
3868 self.__menus["file"].addAction(self.exitAct) |
3834 act = self.__menus["file"].actions()[0] |
3869 act = self.__menus["file"].actions()[0] |
3835 sep = self.__menus["file"].insertSeparator(act) |
3870 sep = self.__menus["file"].insertSeparator(act) |
4186 toolstb.addAction(self.hexEditorAct) |
4221 toolstb.addAction(self.hexEditorAct) |
4187 toolstb.addAction(self.iconEditorAct) |
4222 toolstb.addAction(self.iconEditorAct) |
4188 if self.snapshotAct is not None: |
4223 if self.snapshotAct is not None: |
4189 toolstb.addAction(self.snapshotAct) |
4224 toolstb.addAction(self.snapshotAct) |
4190 toolstb.addAction(self.pdfViewerAct) |
4225 toolstb.addAction(self.pdfViewerAct) |
4191 if self.webBrowserAct: |
4226 toolstb.addSeparator() |
4192 toolstb.addSeparator() |
4227 toolstb.addAction(self.webBrowserAct) |
4193 toolstb.addAction(self.webBrowserAct) |
4228 if self.securityKeyMgmtAct is not None: |
|
4229 toolstb.addAction(self.securityKeyMgmtAct) |
4194 self.toolbarManager.addToolBar(toolstb, toolstb.windowTitle()) |
4230 self.toolbarManager.addToolBar(toolstb, toolstb.windowTitle()) |
4195 |
4231 |
4196 # setup the settings toolbar |
4232 # setup the settings toolbar |
4197 settingstb.addAction(self.prefAct) |
4233 settingstb.addAction(self.prefAct) |
4198 settingstb.addAction(self.configViewProfilesAct) |
4234 settingstb.addAction(self.configViewProfilesAct) |
5168 btMenu.addAction(self.hexEditorAct) |
5204 btMenu.addAction(self.hexEditorAct) |
5169 btMenu.addAction(self.iconEditorAct) |
5205 btMenu.addAction(self.iconEditorAct) |
5170 if self.snapshotAct is not None: |
5206 if self.snapshotAct is not None: |
5171 btMenu.addAction(self.snapshotAct) |
5207 btMenu.addAction(self.snapshotAct) |
5172 btMenu.addAction(self.pdfViewerAct) |
5208 btMenu.addAction(self.pdfViewerAct) |
5173 if self.webBrowserAct: |
5209 btMenu.addAction(self.webBrowserAct) |
5174 btMenu.addAction(self.webBrowserAct) |
5210 if self.securityKeyMgmtAct is not None: |
|
5211 btMenu.addAction(self.securityKeyMgmtAct) |
5175 |
5212 |
5176 ptMenu = QMenu(self.tr("&Plugin Tools"), self) |
5213 ptMenu = QMenu(self.tr("&Plugin Tools"), self) |
5177 ptMenu.aboutToShow.connect(self.__showPluginToolsMenu) |
5214 ptMenu.aboutToShow.connect(self.__showPluginToolsMenu) |
5178 |
5215 |
5179 utMenu = QMenu(self.tr("&User Tools"), self) |
5216 utMenu = QMenu(self.tr("&User Tools"), self) |
6257 def __startWebBrowser(self): |
6294 def __startWebBrowser(self): |
6258 """ |
6295 """ |
6259 Private slot to start the eric web browser. |
6296 Private slot to start the eric web browser. |
6260 """ |
6297 """ |
6261 self.launchHelpViewer("") |
6298 self.launchHelpViewer("") |
|
6299 |
|
6300 @pyqtSlot() |
|
6301 def __startFido2SecurityKeyMgmt(self): |
|
6302 """ |
|
6303 Private slot to start the FIDO2 Security Key Management. |
|
6304 """ |
|
6305 fido2Mgmt = os.path.join(os.path.dirname(__file__), "..", "eric7_fido2.py") |
|
6306 QProcess.startDetached( |
|
6307 PythonUtilities.getPythonExecutable(), [fido2Mgmt] |
|
6308 ) |
6262 |
6309 |
6263 def __customViewer(self, home=None): |
6310 def __customViewer(self, home=None): |
6264 """ |
6311 """ |
6265 Private slot to start a custom viewer. |
6312 Private slot to start a custom viewer. |
6266 |
6313 |
7693 """ |
7740 """ |
7694 fn = os.path.join(Globals.getConfigDir(), "eric7tasks.etj") |
7741 fn = os.path.join(Globals.getConfigDir(), "eric7tasks.etj") |
7695 if os.path.exists(fn): |
7742 if os.path.exists(fn): |
7696 self.__tasksFile.readFile(fn) |
7743 self.__tasksFile.readFile(fn) |
7697 |
7744 |
7698 def __writeSession(self, filename="", crashSession=False): |
7745 @pyqtSlot() |
|
7746 def __showSessionsMenu(self): |
|
7747 """ |
|
7748 Private slot to mofify the state of some session actions. |
|
7749 """ |
|
7750 crashSessionsAvailable = bool(self.__getCrashedSessions()) |
|
7751 |
|
7752 menu = self.__menus["sessions"] |
|
7753 menu.clear() |
|
7754 menu.addAction(self.saveSessionAct) |
|
7755 menu.addAction(self.loadSessionAct) |
|
7756 menu.addSeparator() |
|
7757 act = menu.addAction(self.tr("Load crash session..."), self.__loadCrashSession) |
|
7758 act.setEnabled(crashSessionsAvailable) |
|
7759 act = menu.addAction( |
|
7760 self.tr("Clean crash sessions..."), self.__cleanCrashSessions |
|
7761 ) |
|
7762 act.setEnabled(crashSessionsAvailable) |
|
7763 |
|
7764 def __writeSession(self, filename=""): |
7699 """ |
7765 """ |
7700 Private slot to write the session data to a JSON file (.esj). |
7766 Private slot to write the session data to a JSON file (.esj). |
7701 |
7767 |
7702 @param filename name of a session file to write |
7768 @param filename name of a session file to write |
7703 @type str |
7769 @type str |
7704 @param crashSession flag indicating to write a crash session file |
|
7705 @type bool |
|
7706 @return flag indicating success |
7770 @return flag indicating success |
7707 @rtype bool |
7771 @rtype bool |
7708 """ |
7772 """ |
7709 if filename: |
7773 fn = ( |
7710 fn = filename |
7774 filename |
7711 elif crashSession: |
7775 if filename |
7712 fn = os.path.join(Globals.getConfigDir(), "eric7_crash_session.esj") |
7776 else os.path.join(Globals.getConfigDir(), "eric7session.esj") |
7713 else: |
7777 ) |
7714 fn = os.path.join(Globals.getConfigDir(), "eric7session.esj") |
|
7715 |
7778 |
7716 return self.__sessionFile.writeFile(fn) |
7779 return self.__sessionFile.writeFile(fn) |
7717 |
7780 |
7718 def __readSession(self, filename=""): |
7781 def __readSession(self, filename=""): |
7719 """ |
7782 """ |
7786 if not sessionFile: |
7849 if not sessionFile: |
7787 return |
7850 return |
7788 |
7851 |
7789 self.__readSession(filename=sessionFile) |
7852 self.__readSession(filename=sessionFile) |
7790 |
7853 |
|
7854 def __crashSessionFilePath(self, globPattern=False): |
|
7855 """ |
|
7856 Private method to generate a path name for a unique crash session file. |
|
7857 |
|
7858 @param globPattern flag indicating to get the glob pattern for crash |
|
7859 session files (defaults to False) |
|
7860 @type bool (optional) |
|
7861 @return crash session file path |
|
7862 @rtype str |
|
7863 """ |
|
7864 if globPattern: |
|
7865 return os.path.join(Globals.getConfigDir(), "eric7_crash_session_*.esj") |
|
7866 else: |
|
7867 return os.path.join( |
|
7868 Globals.getConfigDir(), f"eric7_crash_session_{os.getpid()}.esj" |
|
7869 ) |
|
7870 |
|
7871 def __getCrashedSessions(self): |
|
7872 """ |
|
7873 Private method to get a list of crash session file paths of crashed sessions. |
|
7874 |
|
7875 Note: Crashed sessions are those, whose PID does not exist anymore. |
|
7876 |
|
7877 @return list of crashed session file paths |
|
7878 @rtype list of str |
|
7879 """ |
|
7880 crashedSessionsList = [] |
|
7881 |
|
7882 crashSessionPattern = self.__crashSessionFilePath(globPattern=True) |
|
7883 crashSessionPatternParts = crashSessionPattern.split("*", 1) |
|
7884 # crashSessionPatternParts is used to extract the PID from the crash session |
|
7885 # file path |
|
7886 crashSessionsList = glob.glob(crashSessionPattern) |
|
7887 if crashSessionsList: |
|
7888 for crashSession in crashSessionsList: |
|
7889 pid = crashSession.replace(crashSessionPatternParts[0], "").replace( |
|
7890 crashSessionPatternParts[1], "" |
|
7891 ) |
|
7892 if not psutil.pid_exists(int(pid)): |
|
7893 # it is a real crash session |
|
7894 crashedSessionsList.append(crashSession) |
|
7895 |
|
7896 return crashedSessionsList |
|
7897 |
|
7898 def __checkCrashSessionExists(self): |
|
7899 """ |
|
7900 Private method to check for the existence of crash session files and |
|
7901 select the one to open. |
|
7902 |
|
7903 @return file path of the crash session file to open. An empty string indicates |
|
7904 that no crash session file should be opened or exists. |
|
7905 @rtype str |
|
7906 """ |
|
7907 selectedCrashSessionFile = "" |
|
7908 crashedSessionsList = self.__getCrashedSessions() |
|
7909 if crashedSessionsList: |
|
7910 dlg = EricListSelectionDialog( |
|
7911 sorted(crashedSessionsList), |
|
7912 selectionMode=QAbstractItemView.SelectionMode.SingleSelection, |
|
7913 title=self.tr("Found Crash Sessions"), |
|
7914 message=self.tr( |
|
7915 "These crash session files were found. Select the one to" |
|
7916 " open. Select 'Cancel' to not open a crash session." |
|
7917 ), |
|
7918 doubleClickOk=True, |
|
7919 parent=self, |
|
7920 ) |
|
7921 if dlg.exec() == QDialog.DialogCode.Accepted: |
|
7922 selectedCrashSessionFile = dlg.getSelection()[0] |
|
7923 |
|
7924 return selectedCrashSessionFile |
|
7925 |
7791 def __deleteCrashSession(self): |
7926 def __deleteCrashSession(self): |
7792 """ |
7927 """ |
7793 Private slot to delete the crash session file. |
7928 Private slot to delete the crash session file. |
7794 """ |
7929 """ |
7795 fn = os.path.join(Globals.getConfigDir(), "eric7_crash_session.esj") |
7930 fn = self.__crashSessionFilePath() |
7796 if os.path.exists(fn): |
7931 if os.path.exists(fn): |
7797 with contextlib.suppress(OSError): |
7932 with contextlib.suppress(OSError): |
7798 os.remove(fn) |
7933 os.remove(fn) |
7799 |
7934 |
7800 def __writeCrashSession(self): |
7935 def __writeCrashSession(self): |
7819 if ( |
7954 if ( |
7820 not self.__disableCrashSession |
7955 not self.__disableCrashSession |
7821 and not self.__noCrashOpenAtStartup |
7956 and not self.__noCrashOpenAtStartup |
7822 and Preferences.getUI("OpenCrashSessionOnStartup") |
7957 and Preferences.getUI("OpenCrashSessionOnStartup") |
7823 ): |
7958 ): |
7824 fn = os.path.join(Globals.getConfigDir(), "eric7_crash_session.esj") |
7959 fn = self.__checkCrashSessionExists() |
7825 if os.path.exists(fn): |
7960 if fn: |
7826 yes = EricMessageBox.yesNo( |
7961 res = self.__readSession(filename=fn) |
7827 self, |
7962 if res and Preferences.getUI("DeleteLoadedCrashSession"): |
7828 self.tr("Crash Session found!"), |
7963 os.remove(fn) |
7829 self.tr( |
|
7830 """A session file of a crashed session was""" |
|
7831 """ found. Shall this session be restored?""" |
|
7832 ), |
|
7833 ) |
|
7834 if yes: |
|
7835 res = self.__readSession(filename=fn) |
|
7836 |
7964 |
7837 return res |
7965 return res |
|
7966 |
|
7967 @pyqtSlot() |
|
7968 def __loadCrashSession(self): |
|
7969 """ |
|
7970 Private slot to load a crash session. |
|
7971 """ |
|
7972 fn = self.__checkCrashSessionExists() |
|
7973 if fn: |
|
7974 self.__readSession(filename=fn) |
|
7975 |
|
7976 @pyqtSlot() |
|
7977 def __cleanCrashSessions(self): |
|
7978 """ |
|
7979 Private slot to clean all stale crash sessions. |
|
7980 """ |
|
7981 from .DeleteFilesConfirmationDialog import DeleteFilesConfirmationDialog |
|
7982 |
|
7983 crashedSessionsList = self.__getCrashedSessions() |
|
7984 if crashedSessionsList: |
|
7985 dlg = DeleteFilesConfirmationDialog( |
|
7986 parent=self, |
|
7987 caption=self.tr("Clean stale crash sessions"), |
|
7988 message=self.tr( |
|
7989 "Do you really want to delete these stale crash session files?" |
|
7990 ), |
|
7991 files=sorted(crashedSessionsList), |
|
7992 ) |
|
7993 if dlg.exec() == QDialog.DialogCode.Accepted: |
|
7994 for crashSession in crashedSessionsList: |
|
7995 os.remove(crashSession) |
7838 |
7996 |
7839 def showFindFileByNameDialog(self): |
7997 def showFindFileByNameDialog(self): |
7840 """ |
7998 """ |
7841 Public slot to show the Find File by Name dialog. |
7999 Public slot to show the Find File by Name dialog. |
7842 """ |
8000 """ |