1# -*- coding: utf-8 -*-
2
3# Copyright (c) 2002 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
4#
5
6"""
7Module implementing the main user interface.
8"""
9
10import os
11import sys
12import logging
13import shutil
14import json
15import datetime
16import getpass
17import functools
18import contextlib
19
20from PyQt5.QtCore import (
21    pyqtSlot, QTimer, QFile, QFileInfo, pyqtSignal, PYQT_VERSION_STR, QDate,
22    QIODevice, qVersion, QProcess, QSize, QUrl, QObject, Qt, QUuid, QThread,
23    QUrlQuery
24)
25from PyQt5.QtGui import QKeySequence, QDesktopServices, QGuiApplication
26from PyQt5.QtWidgets import (
27    QSizePolicy, QWidget, QWhatsThis, QToolBar, QDialog, QSplitter,
28    QApplication, QMenu, QVBoxLayout, QDockWidget, QAction, QLabel
29)
30from PyQt5.Qsci import QSCINTILLA_VERSION_STR
31from PyQt5.QtNetwork import (
32    QNetworkProxyFactory, QNetworkAccessManager, QNetworkRequest, QNetworkReply
33)
34
35from .Info import Version, VersionOnly, BugAddress, Program, FeatureAddress
36from . import Config
37from .NotificationWidget import NotificationTypes
38
39from E5Gui.E5SingleApplication import E5SingleApplicationServer
40from E5Gui.E5Action import E5Action, createActionGroup
41from E5Gui.E5ToolBarManager import E5ToolBarManager
42from E5Gui import E5MessageBox, E5FileDialog, E5ErrorMessage
43from E5Gui.E5Application import e5App
44from E5Gui.E5MainWindow import E5MainWindow
45from E5Gui.E5ZoomWidget import E5ZoomWidget
46from E5Gui.E5ProgressDialog import E5ProgressDialog
47from E5Gui.E5ClickableLabel import E5ClickableLabel
48
49import Preferences
50import Utilities
51import Globals
52
53import UI.PixmapCache
54
55from Sessions.SessionFile import SessionFile
56
57from Tasks.TasksFile import TasksFile
58
59from E5Network.E5NetworkProxyFactory import (
60    E5NetworkProxyFactory, proxyAuthenticationRequired
61)
62try:
63    from E5Network.E5SslErrorHandler import E5SslErrorHandler, E5SslErrorState
64    SSL_AVAILABLE = True
65except ImportError:
66    SSL_AVAILABLE = False
67
68from eric6config import getConfig
69
70
71class Redirector(QObject):
72    """
73    Helper class used to redirect stdout and stderr to the log window.
74
75    @signal appendStderr(str) emitted to write data to stderr logger
76    @signal appendStdout(str) emitted to write data to stdout logger
77    """
78    appendStderr = pyqtSignal(str)
79    appendStdout = pyqtSignal(str)
80
81    def __init__(self, stderr, parent=None):
82        """
83        Constructor
84
85        @param stderr flag indicating stderr is being redirected
86        @type bool
87        @param parent reference to the parent object
88        @type QObject
89        """
90        super().__init__(parent)
91        self.stderr = stderr
92        self.buffer = ''
93
94    def __nWrite(self, n):
95        """
96        Private method used to write data.
97
98        @param n max number of bytes to write
99        """
100        if n:
101            line = self.buffer[:n]
102            if self.stderr:
103                self.appendStderr.emit(line)
104            else:
105                self.appendStdout.emit(line)
106            self.buffer = self.buffer[n:]
107
108    def __bufferedWrite(self):
109        """
110        Private method returning number of characters to write.
111
112        @return number of characters buffered or length of buffered line
113            (integer)
114        """
115        return self.buffer.rfind('\n') + 1
116
117    def flush(self):
118        """
119        Public method used to flush the buffered data.
120        """
121        self.__nWrite(len(self.buffer))
122
123    def write(self, s):
124        """
125        Public method used to write data.
126
127        @param s data to be written (it must support the str-method)
128        """
129        self.buffer += str(s)
130        self.__nWrite(self.__bufferedWrite())
131
132
133class UserInterface(E5MainWindow):
134    """
135    Class implementing the main user interface.
136
137    @signal appendStderr(str) emitted to write data to stderr logger
138    @signal appendStdout(str) emitted to write data to stdout logger
139    @signal preferencesChanged() emitted after the preferences were changed
140    @signal reloadAPIs() emitted to reload the api information
141    @signal showMenu(str, QMenu) emitted when a menu is about to be shown. The
142        name of the menu and a reference to the menu are given.
143    @signal masterPasswordChanged(str, str) emitted after the master
144        password has been changed with the old and the new password
145    """
146    appendStderr = pyqtSignal(str)
147    appendStdout = pyqtSignal(str)
148    preferencesChanged = pyqtSignal()
149    reloadAPIs = pyqtSignal()
150    showMenu = pyqtSignal(str, QMenu)
151    masterPasswordChanged = pyqtSignal(str, str)
152
153    maxFilePathLen = 100
154    maxMenuFilePathLen = 75
155
156    LeftSide = 1
157    BottomSide = 2
158    RightSide = 3
159
160    ErrorLogFileName = "eric6_error.log"
161
162    def __init__(self, app, locale, splash, plugin, disabledPlugins,
163                 noOpenAtStartup, noCrashOpenAtStartup, disableCrashSession,
164                 restartArguments, originalPathString):
165        """
166        Constructor
167
168        @param app reference to the application object
169        @type E5Application
170        @param locale locale to be used by the UI
171        @type str
172        @param splash reference to the splashscreen
173        @type UI.SplashScreen.SplashScreen
174        @param plugin filename of a plug-in to be loaded (used for plugin
175            development)
176        @type str
177        @param disabledPlugins list of plug-ins that have been disabled via
178            the command line parameters '--disable-plugin='
179        @type list of str
180        @param noOpenAtStartup flag indicating that the open at startup option
181            should not be executed
182        @type bool
183        @param noCrashOpenAtStartup flag indicating to ignore any crash session
184            file found at statup
185        @type bool
186        @param disableCrashSession flag indicating to disable the crash session
187            support
188        @type bool
189        @param restartArguments list of command line parameters to be used for
190            a restart
191        @type list of str
192        @param originalPathString original PATH environment variable
193        @type str
194        """
195        super().__init__()
196
197        self.__restartArgs = restartArguments[:]
198
199        self.setStyle(Preferences.getUI("Style"),
200                      Preferences.getUI("StyleSheet"))
201
202        self.maxEditorPathLen = Preferences.getUI("CaptionFilenameLength")
203        self.locale = locale
204        self.__openAtStartup = not noOpenAtStartup
205        self.__noCrashOpenAtStartup = noCrashOpenAtStartup
206        self.__disableCrashSession = disableCrashSession
207        self.__disabledPlugins = disabledPlugins[:]
208
209        self.__originalPathString = originalPathString
210
211        self.__layoutType = Preferences.getUI("LayoutType")
212
213        self.passiveMode = Preferences.getDebugger("PassiveDbgEnabled")
214
215        g = Preferences.getGeometry("MainGeometry")
216        if g.isEmpty():
217            s = QSize(1280, 1024)
218            self.resize(s)
219        else:
220            self.restoreGeometry(g)
221        self.__startup = True
222
223        if Preferences.getUI("UseSystemProxy"):
224            QNetworkProxyFactory.setUseSystemConfiguration(True)
225        else:
226            self.__proxyFactory = E5NetworkProxyFactory()
227            QNetworkProxyFactory.setApplicationProxyFactory(
228                self.__proxyFactory)
229            QNetworkProxyFactory.setUseSystemConfiguration(False)
230
231        self.capProject = ""
232        self.capEditor = ""
233        self.captionShowsFilename = Preferences.getUI("CaptionShowsFilename")
234
235        QApplication.setWindowIcon(UI.PixmapCache.getIcon("eric"))
236        self.setWindowIcon(UI.PixmapCache.getIcon("eric"))
237        self.__setWindowCaption()
238
239        # load the view profiles
240        self.profiles = Preferences.getUI("ViewProfiles2")
241
242        # Generate the conda interface
243        from CondaInterface.Conda import Conda
244        self.condaInterface = Conda(self)
245        e5App().registerObject("Conda", self.condaInterface)
246
247        # Generate the pip interface
248        from PipInterface.Pip import Pip
249        self.pipInterface = Pip(self)
250        e5App().registerObject("Pip", self.pipInterface)
251
252        # Generate the virtual environment manager
253        from VirtualEnv.VirtualenvManager import VirtualenvManager
254        self.virtualenvManager = VirtualenvManager(self)
255        # register it early because it is needed very soon
256        e5App().registerObject("VirtualEnvManager", self.virtualenvManager)
257
258        # Generate an empty project object and multi project object
259        from Project.Project import Project
260        self.project = Project(self)
261        e5App().registerObject("Project", self.project)
262
263        from MultiProject.MultiProject import MultiProject
264        self.multiProject = MultiProject(self.project, self)
265
266        # Generate the debug server object
267        from Debugger.DebugServer import DebugServer
268        debugServer = DebugServer(self.__originalPathString,
269                                  project=self.project, parent=self)
270
271        # Create the background service object
272        from Utilities.BackgroundService import BackgroundService
273        self.backgroundService = BackgroundService(self)
274
275        splash.showMessage(self.tr("Initializing Plugin Manager..."))
276
277        # Initialize the Plugin Manager (Plugins are initialized later
278        from PluginManager.PluginManager import PluginManager
279        self.pluginManager = PluginManager(self, self.__disabledPlugins,
280                                           develPlugin=plugin)
281
282        splash.showMessage(self.tr("Generating Main User Interface..."))
283
284        self.codeDocumentationViewer = None
285        self.cooperation = None
286        self.irc = None
287        self.symbolsViewer = None
288        self.browser = None
289        self.templateViewer = None
290        self.numbersViewer = None
291        self.pipWidget = None
292        self.condaWidget = None
293        self.microPythonWidget = None
294
295        self.__webBrowserProcess = None
296        self.__webBrowserClient = None
297        self.__webBrowserSAName = QUuid.createUuid().toString()[1:-1]
298
299        # Create the main window now so that we can connect QActions to it.
300        logging.debug("Creating Layout...")
301        self.__createLayout(debugServer)
302        self.__currentRightWidget = None
303        self.__currentBottomWidget = None
304
305        # Generate the debugger part of the ui
306        logging.debug("Creating Debugger UI...")
307        from Debugger.DebugUI import DebugUI
308        self.debuggerUI = DebugUI(self, self.viewmanager, debugServer,
309                                  self.debugViewer, self.project)
310        self.debugViewer.setDebugger(self.debuggerUI)
311        self.shell.setDebuggerUI(self.debuggerUI)
312
313        # Generate the redirection helpers
314        self.stdout = Redirector(False, self)
315        self.stderr = Redirector(True, self)
316
317        # set a few dialog members for non-modal dialogs created on demand
318        self.programsDialog = None
319        self.shortcutsDialog = None
320        self.unittestDialog = None
321        self.findFileNameDialog = None
322        self.diffDlg = None
323        self.compareDlg = None
324        self.findFilesDialog = None
325        self.replaceFilesDialog = None
326        self.__notification = None
327        self.__readingSession = False
328        self.__versionsDialog = None
329        self.__configurationDialog = None
330
331        # now setup the connections
332        splash.showMessage(self.tr("Setting up connections..."))
333
334        self.debugViewer.exceptionLogger.sourceFile.connect(
335            self.viewmanager.openSourceFile)
336
337        self.debugViewer.sourceFile.connect(self.viewmanager.showDebugSource)
338
339        self.taskViewer.displayFile.connect(self.viewmanager.openSourceFile)
340
341        self.projectBrowser.psBrowser.sourceFile[str].connect(
342            self.viewmanager.openSourceFile)
343        self.projectBrowser.psBrowser.sourceFile[str, int].connect(
344            self.viewmanager.openSourceFile)
345        self.projectBrowser.psBrowser.sourceFile[str, list].connect(
346            self.viewmanager.openSourceFile)
347        self.projectBrowser.psBrowser.sourceFile[str, int, str].connect(
348            self.viewmanager.openSourceFile)
349        self.projectBrowser.psBrowser.closeSourceWindow.connect(
350            self.viewmanager.closeWindow)
351        self.projectBrowser.psBrowser.unittestOpen.connect(
352            self.__unittestScript)
353
354        self.projectBrowser.pfBrowser.designerFile.connect(self.__designer)
355        self.projectBrowser.pfBrowser.sourceFile.connect(
356            self.viewmanager.openSourceFile)
357        self.projectBrowser.pfBrowser.uipreview.connect(self.__UIPreviewer)
358        self.projectBrowser.pfBrowser.trpreview.connect(self.__TRPreviewer)
359        self.projectBrowser.pfBrowser.closeSourceWindow.connect(
360            self.viewmanager.closeWindow)
361        self.projectBrowser.pfBrowser.appendStderr.connect(self.appendToStderr)
362
363        self.projectBrowser.prBrowser.sourceFile.connect(
364            self.viewmanager.openSourceFile)
365        self.projectBrowser.prBrowser.closeSourceWindow.connect(
366            self.viewmanager.closeWindow)
367        self.projectBrowser.prBrowser.appendStderr.connect(self.appendToStderr)
368
369        self.projectBrowser.ptBrowser.linguistFile.connect(self.__linguist)
370        self.projectBrowser.ptBrowser.sourceFile.connect(
371            self.viewmanager.openSourceFile)
372        self.projectBrowser.ptBrowser.trpreview[list].connect(
373            self.__TRPreviewer)
374        self.projectBrowser.ptBrowser.trpreview[list, bool].connect(
375            self.__TRPreviewer)
376        self.projectBrowser.ptBrowser.closeSourceWindow.connect(
377            self.viewmanager.closeWindow)
378        self.projectBrowser.ptBrowser.appendStdout.connect(self.appendToStdout)
379        self.projectBrowser.ptBrowser.appendStderr.connect(self.appendToStderr)
380
381        self.projectBrowser.piBrowser.sourceFile[str].connect(
382            self.viewmanager.openSourceFile)
383        self.projectBrowser.piBrowser.sourceFile[str, int].connect(
384            self.viewmanager.openSourceFile)
385        self.projectBrowser.piBrowser.closeSourceWindow.connect(
386            self.viewmanager.closeWindow)
387        self.projectBrowser.piBrowser.appendStdout.connect(self.appendToStdout)
388        self.projectBrowser.piBrowser.appendStderr.connect(self.appendToStderr)
389
390        self.projectBrowser.ppBrowser.sourceFile[str].connect(
391            self.viewmanager.openSourceFile)
392        self.projectBrowser.ppBrowser.sourceFile[str, int].connect(
393            self.viewmanager.openSourceFile)
394        self.projectBrowser.ppBrowser.closeSourceWindow.connect(
395            self.viewmanager.closeWindow)
396        self.projectBrowser.ppBrowser.appendStdout.connect(self.appendToStdout)
397        self.projectBrowser.ppBrowser.appendStderr.connect(self.appendToStderr)
398
399        self.projectBrowser.poBrowser.sourceFile.connect(
400            self.viewmanager.openSourceFile)
401        self.projectBrowser.poBrowser.closeSourceWindow.connect(
402            self.viewmanager.closeWindow)
403        self.projectBrowser.poBrowser.pixmapEditFile.connect(self.__editPixmap)
404        self.projectBrowser.poBrowser.pixmapFile.connect(self.__showPixmap)
405        self.projectBrowser.poBrowser.svgFile.connect(self.__showSvg)
406        self.projectBrowser.poBrowser.umlFile.connect(self.__showUml)
407        self.projectBrowser.poBrowser.binaryFile.connect(self.__openHexEditor)
408
409        self.project.sourceFile.connect(self.viewmanager.openSourceFile)
410        self.project.designerFile.connect(self.__designer)
411        self.project.linguistFile.connect(self.__linguist)
412        self.project.projectOpened.connect(self.viewmanager.projectOpened)
413        self.project.projectClosed.connect(self.viewmanager.projectClosed)
414        self.project.projectFileRenamed.connect(
415            self.viewmanager.projectFileRenamed)
416        self.project.lexerAssociationsChanged.connect(
417            self.viewmanager.projectLexerAssociationsChanged)
418        self.project.newProject.connect(self.__newProject)
419        self.project.projectOpened.connect(self.__projectOpened)
420        self.project.projectOpened.connect(self.__activateProjectBrowser)
421        self.project.projectClosed.connect(self.__projectClosed)
422        self.project.projectClosed.connect(
423            self.backgroundService.preferencesOrProjectChanged)
424        self.project.projectOpened.connect(self.__writeCrashSession)
425        self.project.projectClosed.connect(self.__writeCrashSession)
426        self.project.appendStdout.connect(self.appendToStdout)
427        self.project.appendStderr.connect(self.appendToStderr)
428
429        self.multiProject.multiProjectOpened.connect(
430            self.__activateMultiProjectBrowser)
431        self.multiProject.multiProjectOpened.connect(
432            self.__writeCrashSession)
433        self.multiProject.multiProjectClosed.connect(
434            self.__writeCrashSession)
435
436        self.debuggerUI.resetUI.connect(self.viewmanager.handleResetUI)
437        self.debuggerUI.resetUI.connect(self.debugViewer.handleResetUI)
438        self.debuggerUI.resetUI.connect(self.__debuggingDone)
439        self.debuggerUI.debuggingStarted.connect(self.__programChange)
440        self.debuggerUI.debuggingStarted.connect(self.__debuggingStarted)
441        self.debuggerUI.compileForms.connect(
442            self.projectBrowser.pfBrowser.compileChangedForms)
443        self.debuggerUI.compileResources.connect(
444            self.projectBrowser.prBrowser.compileChangedResources)
445        self.debuggerUI.executeMake.connect(self.project.executeMake)
446        self.debuggerUI.appendStdout.connect(self.appendToStdout)
447
448        debugServer.clientDisassembly.connect(
449            self.debugViewer.disassemblyViewer.showDisassembly)
450        debugServer.clientProcessStdout.connect(self.appendToStdout)
451        debugServer.clientProcessStderr.connect(self.appendToStderr)
452        debugServer.appendStdout.connect(self.appendToStdout)
453
454        self.stdout.appendStdout.connect(self.appendToStdout)
455        self.stderr.appendStderr.connect(self.appendToStderr)
456
457        self.preferencesChanged.connect(self.viewmanager.preferencesChanged)
458        self.reloadAPIs.connect(self.viewmanager.getAPIsManager().reloadAPIs)
459        self.preferencesChanged.connect(self.logViewer.preferencesChanged)
460        self.appendStdout.connect(self.logViewer.appendToStdout)
461        self.appendStderr.connect(self.logViewer.appendToStderr)
462        self.preferencesChanged.connect(self.shell.handlePreferencesChanged)
463        self.preferencesChanged.connect(self.project.handlePreferencesChanged)
464        self.preferencesChanged.connect(
465            self.projectBrowser.handlePreferencesChanged)
466        self.preferencesChanged.connect(
467            self.projectBrowser.psBrowser.handlePreferencesChanged)
468        self.preferencesChanged.connect(
469            self.projectBrowser.pfBrowser.handlePreferencesChanged)
470        self.preferencesChanged.connect(
471            self.projectBrowser.prBrowser.handlePreferencesChanged)
472        self.preferencesChanged.connect(
473            self.projectBrowser.ptBrowser.handlePreferencesChanged)
474        self.preferencesChanged.connect(
475            self.projectBrowser.piBrowser.handlePreferencesChanged)
476        self.preferencesChanged.connect(
477            self.projectBrowser.ppBrowser.handlePreferencesChanged)
478        self.preferencesChanged.connect(
479            self.projectBrowser.poBrowser.handlePreferencesChanged)
480        self.preferencesChanged.connect(
481            self.taskViewer.handlePreferencesChanged)
482        self.preferencesChanged.connect(self.pluginManager.preferencesChanged)
483        self.preferencesChanged.connect(debugServer.preferencesChanged)
484        self.preferencesChanged.connect(self.debugViewer.preferencesChanged)
485        self.preferencesChanged.connect(
486            self.backgroundService.preferencesOrProjectChanged)
487        self.preferencesChanged.connect(self.__previewer.preferencesChanged)
488        self.preferencesChanged.connect(self.__astViewer.preferencesChanged)
489        self.preferencesChanged.connect(self.__disViewer.preferencesChanged)
490
491        if self.browser is not None:
492            self.browser.sourceFile[str].connect(
493                self.viewmanager.openSourceFile)
494            self.browser.sourceFile[str, int].connect(
495                self.viewmanager.openSourceFile)
496            self.browser.sourceFile[str, list].connect(
497                self.viewmanager.openSourceFile)
498            self.browser.sourceFile[str, int, str].connect(
499                self.viewmanager.openSourceFile)
500            self.browser.designerFile.connect(self.__designer)
501            self.browser.linguistFile.connect(self.__linguist)
502            self.browser.projectFile.connect(self.project.openProject)
503            self.browser.multiProjectFile.connect(
504                self.multiProject.openMultiProject)
505            self.browser.pixmapEditFile.connect(self.__editPixmap)
506            self.browser.pixmapFile.connect(self.__showPixmap)
507            self.browser.svgFile.connect(self.__showSvg)
508            self.browser.umlFile.connect(self.__showUml)
509            self.browser.binaryFile.connect(self.__openHexEditor)
510            self.browser.unittestOpen.connect(self.__unittestScript)
511            self.browser.trpreview.connect(self.__TRPreviewer)
512
513            self.debuggerUI.debuggingStarted.connect(
514                self.browser.handleProgramChange)
515
516            debugServer.clientInterpreterChanged.connect(
517                self.browser.handleInterpreterChanged)
518
519            self.preferencesChanged.connect(
520                self.browser.handlePreferencesChanged)
521
522        if self.codeDocumentationViewer is not None:
523            self.preferencesChanged.connect(
524                self.codeDocumentationViewer.preferencesChanged)
525
526        self.viewmanager.editorSaved.connect(self.project.repopulateItem)
527        self.viewmanager.lastEditorClosed.connect(self.__lastEditorClosed)
528        self.viewmanager.editorOpened.connect(self.__editorOpened)
529        self.viewmanager.changeCaption.connect(self.__setWindowCaption)
530        self.viewmanager.checkActions.connect(self.__checkActions)
531        self.viewmanager.editorChanged.connect(
532            self.projectBrowser.handleEditorChanged)
533        self.viewmanager.editorLineChanged.connect(
534            self.projectBrowser.handleEditorLineChanged)
535        self.viewmanager.editorOpened.connect(self.__writeCrashSession)
536        self.viewmanager.editorClosed.connect(self.__writeCrashSession)
537        self.viewmanager.editorRenamed.connect(self.__writeCrashSession)
538        self.viewmanager.editorChanged.connect(self.__writeCrashSession)
539
540        self.shell.zoomValueChanged.connect(
541            lambda v: self.viewmanager.zoomValueChanged(v, self.shell))
542
543        if self.cooperation is not None:
544            self.viewmanager.checkActions.connect(
545                self.cooperation.checkEditorActions)
546            self.preferencesChanged.connect(
547                self.cooperation.preferencesChanged)
548            self.cooperation.shareEditor.connect(
549                self.viewmanager.shareEditor)
550            self.cooperation.startEdit.connect(
551                self.viewmanager.startSharedEdit)
552            self.cooperation.sendEdit.connect(
553                self.viewmanager.sendSharedEdit)
554            self.cooperation.cancelEdit.connect(
555                self.viewmanager.cancelSharedEdit)
556            self.cooperation.connected.connect(
557                self.viewmanager.shareConnected)
558            self.cooperation.editorCommand.connect(
559                self.viewmanager.receive)
560            self.viewmanager.setCooperationClient(
561                self.cooperation.getClient())
562
563        if self.symbolsViewer is not None:
564            self.symbolsViewer.insertSymbol.connect(
565                self.viewmanager.insertSymbol)
566
567        if self.numbersViewer is not None:
568            self.numbersViewer.insertNumber.connect(
569                self.viewmanager.insertNumber)
570
571        if self.irc is not None:
572            self.irc.autoConnected.connect(self.__ircAutoConnected)
573
574        # create the toolbar manager object
575        self.toolbarManager = E5ToolBarManager(self, self)
576        self.toolbarManager.setMainWindow(self)
577
578        # Initialize the tool groups and list of started tools
579        splash.showMessage(self.tr("Initializing Tools..."))
580        self.toolGroups, self.currentToolGroup = Preferences.readToolGroups()
581        self.toolProcs = []
582        self.__initExternalToolsActions()
583
584        # redirect handling of http and https URLs to ourselves
585        QDesktopServices.setUrlHandler("http", self.handleUrl)
586        QDesktopServices.setUrlHandler("https", self.handleUrl)
587
588        # register all relevant objects
589        splash.showMessage(self.tr("Registering Objects..."))
590        e5App().registerObject("UserInterface", self)
591        e5App().registerObject("DebugUI", self.debuggerUI)
592        e5App().registerObject("DebugServer", debugServer)
593        e5App().registerObject("BackgroundService", self.backgroundService)
594        e5App().registerObject("ViewManager", self.viewmanager)
595        e5App().registerObject("ProjectBrowser", self.projectBrowser)
596        e5App().registerObject("MultiProject", self.multiProject)
597        e5App().registerObject("TaskViewer", self.taskViewer)
598        if self.templateViewer is not None:
599            e5App().registerObject("TemplateViewer", self.templateViewer)
600        e5App().registerObject("Shell", self.shell)
601        e5App().registerObject("PluginManager", self.pluginManager)
602        e5App().registerObject("ToolbarManager", self.toolbarManager)
603        if self.cooperation is not None:
604            e5App().registerObject("Cooperation", self.cooperation)
605        if self.irc is not None:
606            e5App().registerObject("IRC", self.irc)
607        if self.symbolsViewer is not None:
608            e5App().registerObject("Symbols", self.symbolsViewer)
609        if self.numbersViewer is not None:
610            e5App().registerObject("Numbers", self.numbersViewer)
611        if self.codeDocumentationViewer is not None:
612            e5App().registerObject("DocuViewer", self.codeDocumentationViewer)
613        if self.microPythonWidget is not None:
614            e5App().registerObject("MicroPython", self.microPythonWidget)
615
616        # list of web addresses serving the versions file
617        self.__httpAlternatives = Preferences.getUI("VersionsUrls6")
618        self.__inVersionCheck = False
619        self.__versionCheckProgress = None
620
621        # create the various JSON file interfaces
622        self.__sessionFile = SessionFile(True)
623        self.__tasksFile = TasksFile(True)
624
625        # Initialize the actions, menus, toolbars and statusbar
626        splash.showMessage(self.tr("Initializing Actions..."))
627        self.__initActions()
628        splash.showMessage(self.tr("Initializing Menus..."))
629        self.__initMenus()
630        splash.showMessage(self.tr("Initializing Toolbars..."))
631        self.__initToolbars()
632        splash.showMessage(self.tr("Initializing Statusbar..."))
633        self.__initStatusbar()
634
635        # connect the appFocusChanged signal after all actions are ready
636        app.focusChanged.connect(self.viewmanager.appFocusChanged)
637
638        # Initialize the instance variables.
639        self.currentProg = None
640        self.isProg = False
641        self.utEditorOpen = False
642        self.utProjectOpen = False
643
644        self.inDragDrop = False
645        self.setAcceptDrops(True)
646
647        self.currentProfile = None
648
649        self.shutdownCalled = False
650        self.inCloseEvent = False
651
652        # now redirect stdout and stderr
653        #
654        sys.stdout = self.stdout
655        sys.stderr = self.stderr
656
657        # now fire up the single application server
658        if Preferences.getUI("SingleApplicationMode"):
659            splash.showMessage(
660                self.tr("Initializing Single Application Server..."))
661            self.SAServer = E5SingleApplicationServer()
662        else:
663            self.SAServer = None
664
665        # now finalize the plugin manager setup
666        splash.showMessage(self.tr("Initializing Plugins..."))
667        self.pluginManager.finalizeSetup()
668        # now activate plugins having autoload set to True
669        splash.showMessage(self.tr("Activating Plugins..."))
670        self.pluginManager.activatePlugins()
671        splash.showMessage(self.tr("Generating Plugins Toolbars..."))
672        self.pluginManager.initPluginToolbars(self.toolbarManager)
673        if Preferences.getPluginManager("StartupCleanup"):
674            splash.showMessage(self.tr("Cleaning Plugins Download Area..."))
675            from PluginManager.PluginRepositoryDialog import (
676                PluginRepositoryDownloadCleanup
677            )
678            PluginRepositoryDownloadCleanup(quiet=True)
679
680        # now read the keyboard shortcuts for all the actions
681        from Preferences import Shortcuts
682        Shortcuts.readShortcuts()
683
684        # restore toolbar manager state
685        splash.showMessage(self.tr("Restoring Toolbarmanager..."))
686        self.toolbarManager.restoreState(
687            Preferences.getUI("ToolbarManagerState"))
688
689        if self.codeDocumentationViewer is not None:
690            # finalize the initialization of the code documentation viewer
691            self.codeDocumentationViewer.finalizeSetup()
692
693        # now activate the initial view profile
694        splash.showMessage(self.tr("Setting View Profile..."))
695        self.__setEditProfile()
696
697        # special treatment for the VCS toolbars
698        for tb in self.getToolbarsByCategory("vcs"):
699            tb.setVisible(False)
700            tb.setEnabled(False)
701        tb = self.getToolbar("vcs")[1]
702        tb.setEnabled(True)
703        if Preferences.getVCS("ShowVcsToolbar"):
704            tb.setVisible(True)
705
706        # now read the saved tasks
707        splash.showMessage(self.tr("Reading Tasks..."))
708        self.__readTasks()
709
710        if self.templateViewer is not None:
711            # now read the saved templates
712            splash.showMessage(self.tr("Reading Templates..."))
713            self.templateViewer.readTemplates()
714
715        # now start the debug client with the most recently used virtual
716        # environment
717        splash.showMessage(self.tr("Starting Debugger..."))
718        if Preferences.getShell("StartWithMostRecentlyUsedEnvironment"):
719            debugServer.startClient(
720                False, venvName=Preferences.getShell("LastVirtualEnvironment")
721            )
722        else:
723            debugServer.startClient(False)
724
725        # attributes for the network objects
726        self.__networkManager = QNetworkAccessManager(self)
727        self.__networkManager.proxyAuthenticationRequired.connect(
728            proxyAuthenticationRequired)
729        if SSL_AVAILABLE:
730            self.__sslErrorHandler = E5SslErrorHandler(self)
731            self.__networkManager.sslErrors.connect(self.__sslErrors)
732        self.__replies = []
733
734        # set spellchecker defaults
735        from QScintilla.SpellChecker import SpellChecker
736        SpellChecker.setDefaultLanguage(
737            Preferences.getEditor("SpellCheckingDefaultLanguage"))
738
739        # attributes for the last shown configuration page and the
740        # extended configuration entries
741        self.__lastConfigurationPageName = ""
742        self.__expandedConfigurationEntries = []
743
744        # set the keyboard input interval
745        interval = Preferences.getUI("KeyboardInputInterval")
746        if interval > 0:
747            QApplication.setKeyboardInputInterval(interval)
748
749        # connect to the desktop environment session manager
750        QGuiApplication.setFallbackSessionManagementEnabled(False)
751        app.commitDataRequest.connect(self.__commitData,
752                                      Qt.ConnectionType.DirectConnection)
753
754    def networkAccessManager(self):
755        """
756        Public method to get a reference to the network access manager object.
757
758        @return reference to the network access manager object
759        @rtype QNetworkAccessManager
760        """
761        return self.__networkManager
762
763    def __createLayout(self, debugServer):
764        """
765        Private method to create the layout of the various windows.
766
767        @param debugServer reference to the debug server object
768        @exception ValueError raised to indicate an invalid layout type
769        """
770        # Create the view manager depending on the configuration setting
771        logging.debug("Creating Viewmanager...")
772        import ViewManager
773        self.viewmanager = ViewManager.factory(
774            self, self, debugServer, self.pluginManager)
775        leftWidget = QWidget()
776        layout = QVBoxLayout()
777        layout.setContentsMargins(1, 1, 1, 1)
778        layout.setSpacing(1)
779        layout.addWidget(self.viewmanager.mainWidget())
780        layout.addWidget(self.viewmanager.searchWidget())
781        layout.addWidget(self.viewmanager.replaceWidget())
782        self.viewmanager.mainWidget().setSizePolicy(
783            QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Expanding)
784        leftWidget.setLayout(layout)
785        self.viewmanager.searchWidget().hide()
786        self.viewmanager.replaceWidget().hide()
787
788        splitter = QSplitter(Qt.Orientation.Horizontal)
789        splitter.addWidget(leftWidget)
790        self.setCentralWidget(splitter)
791
792        # Create previewer
793        logging.debug("Creating Previewer...")
794        from .Previewer import Previewer
795        self.__previewer = Previewer(self.viewmanager, splitter)
796        splitter.addWidget(self.__previewer)
797
798        # Create AST viewer
799        logging.debug("Creating Python AST Viewer")
800        from .PythonAstViewer import PythonAstViewer
801        self.__astViewer = PythonAstViewer(self.viewmanager, splitter)
802        splitter.addWidget(self.__astViewer)
803
804        # Create DIS viewer
805        logging.debug("Creating Python Disassembly Viewer")
806        from .PythonDisViewer import PythonDisViewer
807        self.__disViewer = PythonDisViewer(self.viewmanager, parent=splitter)
808        splitter.addWidget(self.__disViewer)
809
810        # Create layout with toolbox windows embedded in dock windows
811        if self.__layoutType == "Toolboxes":
812            logging.debug("Creating toolboxes...")
813            self.__createToolboxesLayout(debugServer)
814
815        # Create layout with sidebar windows embedded in dock windows
816        elif self.__layoutType == "Sidebars":
817            logging.debug("Creating sidebars...")
818            self.__createSidebarsLayout(debugServer)
819
820        else:
821            raise ValueError("Wrong layout type given ({0})".format(
822                self.__layoutType))
823        logging.debug("Created Layout")
824
825    def __createToolboxesLayout(self, debugServer):
826        """
827        Private method to create the Toolboxes layout.
828
829        @param debugServer reference to the debug server object
830        """
831        from E5Gui.E5ToolBox import E5VerticalToolBox, E5HorizontalToolBox
832
833        logging.debug("Creating Toolboxes Layout...")
834
835        # Create the left toolbox
836        self.lToolboxDock = self.__createDockWindow("lToolboxDock")
837        self.lToolbox = E5VerticalToolBox(self.lToolboxDock)
838        self.__setupDockWindow(self.lToolboxDock,
839                               Qt.DockWidgetArea.LeftDockWidgetArea,
840                               self.lToolbox,
841                               self.tr("Left Toolbox"))
842
843        # Create the horizontal toolbox
844        self.hToolboxDock = self.__createDockWindow("hToolboxDock")
845        self.hToolbox = E5HorizontalToolBox(self.hToolboxDock)
846        self.__setupDockWindow(self.hToolboxDock,
847                               Qt.DockWidgetArea.BottomDockWidgetArea,
848                               self.hToolbox,
849                               self.tr("Horizontal Toolbox"))
850
851        # Create the right toolbox
852        self.rToolboxDock = self.__createDockWindow("rToolboxDock")
853        self.rToolbox = E5VerticalToolBox(self.rToolboxDock)
854        self.__setupDockWindow(self.rToolboxDock,
855                               Qt.DockWidgetArea.RightDockWidgetArea,
856                               self.rToolbox,
857                               self.tr("Right Toolbox"))
858
859        ####################################################
860        ## Populate the left toolbox
861        ####################################################
862
863        # Create the project browser
864        logging.debug("Creating Project Browser...")
865        from Project.ProjectBrowser import ProjectBrowser
866        self.projectBrowser = ProjectBrowser(self.project)
867        self.lToolbox.addItem(self.projectBrowser,
868                              UI.PixmapCache.getIcon("projectViewer"),
869                              self.tr("Project-Viewer"))
870
871        # Create the multi project browser
872        logging.debug("Creating Multiproject Browser...")
873        from MultiProject.MultiProjectBrowser import MultiProjectBrowser
874        self.multiProjectBrowser = MultiProjectBrowser(self.multiProject,
875                                                       self.project)
876        self.lToolbox.addItem(self.multiProjectBrowser,
877                              UI.PixmapCache.getIcon("multiProjectViewer"),
878                              self.tr("Multiproject-Viewer"))
879
880        if Preferences.getUI("ShowTemplateViewer"):
881            # Create the template viewer part of the user interface
882            logging.debug("Creating Template Viewer...")
883            from Templates.TemplateViewer import TemplateViewer
884            self.templateViewer = TemplateViewer(None,
885                                                 self.viewmanager)
886            self.lToolbox.addItem(self.templateViewer,
887                                  UI.PixmapCache.getIcon("templateViewer"),
888                                  self.tr("Template-Viewer"))
889
890        ####################################################
891        ## Populate the right toolbox
892        ####################################################
893
894        if Preferences.getUI("ShowCodeDocumentationViewer"):
895            # Create the code documentation viewer
896            logging.debug("Creating Code Documentation Viewer...")
897            from .CodeDocumentationViewer import CodeDocumentationViewer
898            self.codeDocumentationViewer = CodeDocumentationViewer(self)
899            self.rToolbox.addItem(self.codeDocumentationViewer,
900                                  UI.PixmapCache.getIcon("codeDocuViewer"),
901                                  self.tr("Code Documentation Viewer"))
902
903        # Create the debug viewer
904        logging.debug("Creating Debug Viewer...")
905        from Debugger.DebugViewer import DebugViewer
906        self.debugViewer = DebugViewer(debugServer)
907        self.rToolbox.addItem(self.debugViewer,
908                              UI.PixmapCache.getIcon("debugViewer"),
909                              self.tr("Debug-Viewer"))
910
911        if Preferences.getUI("ShowPyPIPackageManager"):
912            # Create the PyPI package manager
913            logging.debug("Creating PyPI Package Manager...")
914            from PipInterface.PipPackagesWidget import PipPackagesWidget
915            self.pipWidget = PipPackagesWidget(self.pipInterface)
916            self.rToolbox.addItem(self.pipWidget,
917                                  UI.PixmapCache.getIcon("pypi"),
918                                  self.tr("PyPI"))
919
920        if Preferences.getUI("ShowCondaPackageManager"):
921            # Create the conda package manager
922            logging.debug("Creating Conda Package Manager...")
923            from CondaInterface.CondaPackagesWidget import CondaPackagesWidget
924            self.condaWidget = CondaPackagesWidget(self.condaInterface)
925            self.rToolbox.addItem(self.condaWidget,
926                                  UI.PixmapCache.getIcon("miniconda"),
927                                  self.tr("Conda"))
928
929        if Preferences.getUI("ShowCooperation"):
930            # Create the chat part of the user interface
931            logging.debug("Creating Chat Widget...")
932            from Cooperation.ChatWidget import ChatWidget
933            self.cooperation = ChatWidget(self)
934            self.rToolbox.addItem(self.cooperation,
935                                  UI.PixmapCache.getIcon("cooperation"),
936                                  self.tr("Cooperation"))
937
938        if Preferences.getUI("ShowIrc"):
939            # Create the IRC part of the user interface
940            logging.debug("Creating IRC Widget...")
941            from Network.IRC.IrcWidget import IrcWidget
942            self.irc = IrcWidget(self)
943            self.rToolbox.addItem(self.irc,
944                                  UI.PixmapCache.getIcon("irc"),
945                                  self.tr("IRC"))
946
947        if Preferences.getUI("ShowMicroPython"):
948            # Create the MicroPython part of the user interface
949            logging.debug("Creating MicroPython Widget...")
950            from MicroPython.MicroPythonWidget import MicroPythonWidget
951            self.microPythonWidget = MicroPythonWidget(self)
952            self.rToolbox.addItem(self.microPythonWidget,
953                                  UI.PixmapCache.getIcon("micropython"),
954                                  self.tr("MicroPython"))
955
956        ####################################################
957        ## Populate the bottom toolbox
958        ####################################################
959
960        # Create the task viewer part of the user interface
961        logging.debug("Creating Task Viewer...")
962        from Tasks.TaskViewer import TaskViewer
963        self.taskViewer = TaskViewer(None, self.project)
964        self.hToolbox.addItem(self.taskViewer,
965                              UI.PixmapCache.getIcon("task"),
966                              self.tr("Task-Viewer"))
967
968        # Create the log viewer part of the user interface
969        logging.debug("Creating Log Viewer...")
970        from .LogView import LogViewer
971        self.logViewer = LogViewer(self)
972        self.hToolbox.addItem(self.logViewer,
973                              UI.PixmapCache.getIcon("logViewer"),
974                              self.tr("Log-Viewer"))
975
976        if Preferences.getUI("ShowFileBrowser"):
977            # Create the file browser
978            logging.debug("Creating File Browser...")
979            from .Browser import Browser
980            self.browser = Browser()
981            self.lToolbox.addItem(self.browser,
982                                  UI.PixmapCache.getIcon("browser"),
983                                  self.tr("File-Browser"))
984
985        if Preferences.getUI("ShowSymbolsViewer"):
986            # Create the symbols viewer
987            logging.debug("Creating Symbols Viewer...")
988            from .SymbolsWidget import SymbolsWidget
989            self.symbolsViewer = SymbolsWidget()
990            self.lToolbox.addItem(self.symbolsViewer,
991                                  UI.PixmapCache.getIcon("symbols"),
992                                  self.tr("Symbols"))
993
994        if Preferences.getUI("ShowNumbersViewer"):
995            # Create the numbers viewer
996            logging.debug("Creating Numbers Viewer...")
997            from .NumbersWidget import NumbersWidget
998            self.numbersViewer = NumbersWidget()
999            self.hToolbox.addItem(self.numbersViewer,
1000                                  UI.PixmapCache.getIcon("numbers"),
1001                                  self.tr("Numbers"))
1002
1003        ####################################################
1004        ## Populate the configurable widgets
1005        ####################################################
1006
1007        # Create the shell
1008        logging.debug("Creating Shell...")
1009        self.__shellPosition = Preferences.getUI("ShellPosition")
1010        if self.__shellPosition == "left":
1011            self.__shellParent = self.lToolboxDock
1012        elif self.__shellPosition == "right":
1013            self.__shellParent = self.rToolboxDock
1014        else:
1015            self.__shellParent = self.hToolboxDock
1016        from QScintilla.Shell import ShellAssembly
1017        self.shellAssembly = ShellAssembly(
1018            debugServer, self.viewmanager, self.project, True)
1019        self.shell = self.shellAssembly.shell()
1020        self.__shellParent.widget().insertItem(
1021            0, self.shellAssembly, UI.PixmapCache.getIcon("shell"),
1022            self.tr("Shell"))
1023
1024        ####################################################
1025        ## Set the start index of each toolbox
1026        ####################################################
1027
1028        self.lToolbox.setCurrentIndex(0)
1029        self.rToolbox.setCurrentIndex(0)
1030        self.hToolbox.setCurrentIndex(0)
1031
1032    def __createSidebarsLayout(self, debugServer):
1033        """
1034        Private method to create the Sidebars layout.
1035
1036        @param debugServer reference to the debug server object
1037        """
1038        from E5Gui.E5SideBar import E5SideBar, E5SideBarSide
1039
1040        logging.debug("Creating Sidebars Layout...")
1041
1042        delay = Preferences.getUI("SidebarDelay")
1043        # Create the left sidebar
1044        self.leftSidebar = E5SideBar(E5SideBarSide.WEST, delay)
1045
1046        # Create the bottom sidebar
1047        self.bottomSidebar = E5SideBar(E5SideBarSide.SOUTH, delay)
1048
1049        # Create the right sidebar
1050        self.rightSidebar = E5SideBar(E5SideBarSide.EAST, delay)
1051
1052        ####################################################
1053        ## Populate the left side bar
1054        ####################################################
1055
1056        # Create the project browser
1057        logging.debug("Creating Project Browser...")
1058        from Project.ProjectBrowser import ProjectBrowser
1059        self.projectBrowser = ProjectBrowser(self.project)
1060        self.leftSidebar.addTab(
1061            self.projectBrowser,
1062            UI.PixmapCache.getIcon("projectViewer"),
1063            self.tr("Project-Viewer"))
1064
1065        # Create the multi project browser
1066        logging.debug("Creating Multiproject Browser...")
1067        from MultiProject.MultiProjectBrowser import MultiProjectBrowser
1068        self.multiProjectBrowser = MultiProjectBrowser(self.multiProject,
1069                                                       self.project)
1070        self.leftSidebar.addTab(
1071            self.multiProjectBrowser,
1072            UI.PixmapCache.getIcon("multiProjectViewer"),
1073            self.tr("Multiproject-Viewer"))
1074
1075        if Preferences.getUI("ShowTemplateViewer"):
1076            # Create the template viewer part of the user interface
1077            logging.debug("Creating Template Viewer...")
1078            from Templates.TemplateViewer import TemplateViewer
1079            self.templateViewer = TemplateViewer(None,
1080                                                 self.viewmanager)
1081            self.leftSidebar.addTab(
1082                self.templateViewer,
1083                UI.PixmapCache.getIcon("templateViewer"),
1084                self.tr("Template-Viewer"))
1085
1086        ####################################################
1087        ## Populate the right side bar
1088        ####################################################
1089
1090        if Preferences.getUI("ShowCodeDocumentationViewer"):
1091            # Create the code documentation viewer
1092            logging.debug("Creating Code Documentation Viewer...")
1093            from .CodeDocumentationViewer import CodeDocumentationViewer
1094            self.codeDocumentationViewer = CodeDocumentationViewer(self)
1095            self.rightSidebar.addTab(
1096                self.codeDocumentationViewer,
1097                UI.PixmapCache.getIcon("codeDocuViewer"),
1098                self.tr("Code Documentation Viewer"))
1099
1100        # Create the debug viewer
1101        logging.debug("Creating Debug Viewer...")
1102        from Debugger.DebugViewer import DebugViewer
1103        self.debugViewer = DebugViewer(debugServer)
1104        self.rightSidebar.addTab(
1105            self.debugViewer, UI.PixmapCache.getIcon("debugViewer"),
1106            self.tr("Debug-Viewer"))
1107
1108        if Preferences.getUI("ShowPyPIPackageManager"):
1109            # Create the PyPI package manager
1110            logging.debug("Creating PyPI Package Manager...")
1111            from PipInterface.PipPackagesWidget import PipPackagesWidget
1112            self.pipWidget = PipPackagesWidget(self.pipInterface)
1113            self.rightSidebar.addTab(
1114                self.pipWidget, UI.PixmapCache.getIcon("pypi"),
1115                self.tr("PyPI"))
1116
1117        if Preferences.getUI("ShowCondaPackageManager"):
1118            # Create the conda package manager
1119            logging.debug("Creating Conda Package Manager...")
1120            from CondaInterface.CondaPackagesWidget import CondaPackagesWidget
1121            self.condaWidget = CondaPackagesWidget(self.condaInterface)
1122            self.rightSidebar.addTab(
1123                self.condaWidget, UI.PixmapCache.getIcon("miniconda"),
1124                self.tr("Conda"))
1125
1126        if Preferences.getUI("ShowCooperation"):
1127            # Create the chat part of the user interface
1128            logging.debug("Creating Chat Widget...")
1129            from Cooperation.ChatWidget import ChatWidget
1130            self.cooperation = ChatWidget(self)
1131            self.rightSidebar.addTab(
1132                self.cooperation, UI.PixmapCache.getIcon("cooperation"),
1133                self.tr("Cooperation"))
1134
1135        if Preferences.getUI("ShowIrc"):
1136            # Create the IRC part of the user interface
1137            logging.debug("Creating IRC Widget...")
1138            from Network.IRC.IrcWidget import IrcWidget
1139            self.irc = IrcWidget(self)
1140            self.rightSidebar.addTab(
1141                self.irc, UI.PixmapCache.getIcon("irc"),
1142                self.tr("IRC"))
1143
1144        if Preferences.getUI("ShowMicroPython"):
1145            # Create the MicroPython part of the user interface
1146            logging.debug("Creating MicroPython Widget...")
1147            from MicroPython.MicroPythonWidget import MicroPythonWidget
1148            self.microPythonWidget = MicroPythonWidget(self)
1149            self.rightSidebar.addTab(
1150                self.microPythonWidget, UI.PixmapCache.getIcon("micropython"),
1151                self.tr("MicroPython"))
1152
1153        ####################################################
1154        ## Populate the bottom side bar
1155        ####################################################
1156
1157        # Create the task viewer part of the user interface
1158        logging.debug("Creating Task Viewer...")
1159        from Tasks.TaskViewer import TaskViewer
1160        self.taskViewer = TaskViewer(None, self.project)
1161        self.bottomSidebar.addTab(self.taskViewer,
1162                                  UI.PixmapCache.getIcon("task"),
1163                                  self.tr("Task-Viewer"))
1164
1165        # Create the log viewer part of the user interface
1166        logging.debug("Creating Log Viewer...")
1167        from .LogView import LogViewer
1168        self.logViewer = LogViewer(self)
1169        self.bottomSidebar.addTab(self.logViewer,
1170                                  UI.PixmapCache.getIcon("logViewer"),
1171                                  self.tr("Log-Viewer"))
1172
1173        if Preferences.getUI("ShowFileBrowser"):
1174            # Create the file browser
1175            logging.debug("Creating File Browser...")
1176            from .Browser import Browser
1177            self.browser = Browser()
1178            self.leftSidebar.addTab(self.browser,
1179                                    UI.PixmapCache.getIcon("browser"),
1180                                    self.tr("File-Browser"))
1181
1182        if Preferences.getUI("ShowSymbolsViewer"):
1183            # Create the symbols viewer
1184            logging.debug("Creating Symbols Viewer...")
1185            from .SymbolsWidget import SymbolsWidget
1186            self.symbolsViewer = SymbolsWidget()
1187            self.leftSidebar.addTab(self.symbolsViewer,
1188                                    UI.PixmapCache.getIcon("symbols"),
1189                                    self.tr("Symbols"))
1190
1191        if Preferences.getUI("ShowNumbersViewer"):
1192            # Create the numbers viewer
1193            logging.debug("Creating Numbers Viewer...")
1194            from .NumbersWidget import NumbersWidget
1195            self.numbersViewer = NumbersWidget()
1196            self.bottomSidebar.addTab(self.numbersViewer,
1197                                      UI.PixmapCache.getIcon("numbers"),
1198                                      self.tr("Numbers"))
1199
1200        ####################################################
1201        ## Populate the configurable widgets
1202        ####################################################
1203
1204        # Create the shell
1205        logging.debug("Creating Shell...")
1206        self.__shellPosition = Preferences.getUI("ShellPosition")
1207        if self.__shellPosition == "left":
1208            self.__shellParent = self.leftSidebar
1209        elif self.__shellPosition == "right":
1210            self.__shellParent = self.rightSidebar
1211        else:
1212            self.__shellParent = self.bottomSidebar
1213        from QScintilla.Shell import ShellAssembly
1214        self.shellAssembly = ShellAssembly(
1215            debugServer, self.viewmanager, self.project, True)
1216        self.shell = self.shellAssembly.shell()
1217        self.__shellParent.insertTab(0, self.shellAssembly,
1218                                     UI.PixmapCache.getIcon("shell"),
1219                                     self.tr("Shell"))
1220
1221        ####################################################
1222        ## Set the start index of each side bar
1223        ####################################################
1224
1225        self.leftSidebar.setCurrentIndex(0)
1226        self.rightSidebar.setCurrentIndex(0)
1227        self.bottomSidebar.setCurrentIndex(0)
1228
1229        # create the central widget
1230        logging.debug("Creating central widget...")
1231        cw = self.centralWidget()   # save the current central widget
1232        self.leftSplitter = QSplitter(Qt.Orientation.Horizontal)
1233        self.rightSplitter = QSplitter(Qt.Orientation.Horizontal)
1234        self.verticalSplitter = QSplitter(Qt.Orientation.Vertical)
1235        self.verticalSplitter.addWidget(cw)
1236        self.verticalSplitter.addWidget(self.bottomSidebar)
1237        self.rightSplitter.addWidget(self.verticalSplitter)
1238        self.rightSplitter.addWidget(self.rightSidebar)
1239        self.leftSplitter.addWidget(self.leftSidebar)
1240        self.leftSplitter.addWidget(self.rightSplitter)
1241        self.setCentralWidget(self.leftSplitter)
1242
1243        self.leftSidebar.setSplitter(self.leftSplitter)
1244        self.rightSidebar.setSplitter(self.rightSplitter)
1245        self.bottomSidebar.setSplitter(self.verticalSplitter)
1246
1247    def addSideWidget(self, side, widget, icon, label):
1248        """
1249        Public method to add a widget to the sides.
1250
1251        @param side side to add the widget to
1252        @type int (one of UserInterface.LeftSide, UserInterface.BottomSide,
1253            UserInterface.RightSide)
1254        @param widget reference to the widget to add
1255        @type QWidget
1256        @param icon icon to be used
1257        @type QIcon
1258        @param label label text to be shown
1259        @type str
1260        """
1261        if side in [UserInterface.LeftSide, UserInterface.BottomSide,
1262                    UserInterface.RightSide]:
1263            if self.__layoutType == "Toolboxes":
1264                if side == UserInterface.LeftSide:
1265                    self.lToolbox.addItem(widget, icon, label)
1266                elif side == UserInterface.BottomSide:
1267                    self.hToolbox.addItem(widget, icon, label)
1268                elif side == UserInterface.RightSide:
1269                    self.rToolbox.addItem(widget, icon, label)
1270            elif self.__layoutType == "Sidebars":
1271                if side == UserInterface.LeftSide:
1272                    self.leftSidebar.addTab(widget, icon, label)
1273                elif side == UserInterface.BottomSide:
1274                    self.bottomSidebar.addTab(widget, icon, label)
1275                elif side == UserInterface.RightSide:
1276                    self.rightSidebar.addTab(widget, icon, label)
1277
1278    def removeSideWidget(self, widget):
1279        """
1280        Public method to remove a widget added using addSideWidget().
1281
1282        @param widget reference to the widget to remove
1283        @type QWidget
1284        """
1285        if self.__layoutType == "Toolboxes":
1286            for container in [self.lToolbox, self.hToolbox, self.rToolbox]:
1287                index = container.indexOf(widget)
1288                if index != -1:
1289                    container.removeItem(index)
1290        elif self.__layoutType == "Sidebars":
1291            for container in [self.leftSidebar, self.bottomSidebar,
1292                              self.rightSidebar]:
1293                index = container.indexOf(widget)
1294                if index != -1:
1295                    container.removeTab(index)
1296
1297    def showSideWidget(self, widget):
1298        """
1299        Public method to show a specific widget placed in the side widgets.
1300
1301        @param widget reference to the widget to be shown
1302        @type QWidget
1303        """
1304        if self.__layoutType == "Toolboxes":
1305            for dock in [self.lToolboxDock, self.hToolboxDock,
1306                         self.rToolboxDock]:
1307                container = dock.widget()
1308                index = container.indexOf(widget)
1309                if index != -1:
1310                    dock.show()
1311                    container.setCurrentIndex(index)
1312                    dock.raise_()
1313        elif self.__layoutType == "Sidebars":
1314            for container in [self.leftSidebar, self.bottomSidebar,
1315                              self.rightSidebar]:
1316                index = container.indexOf(widget)
1317                if index != -1:
1318                    container.show()
1319                    container.setCurrentIndex(index)
1320                    container.raise_()
1321                    if container.isAutoHiding():
1322                        container.setFocus()
1323
1324    def showLogViewer(self):
1325        """
1326        Public method to show the Log-Viewer.
1327        """
1328        if Preferences.getUI("LogViewerAutoRaise"):
1329            if self.__layoutType == "Toolboxes":
1330                self.hToolboxDock.show()
1331                self.hToolbox.setCurrentWidget(self.logViewer)
1332                self.hToolboxDock.raise_()
1333            elif self.__layoutType == "Sidebars":
1334                self.bottomSidebar.show()
1335                self.bottomSidebar.setCurrentWidget(self.logViewer)
1336                self.bottomSidebar.raise_()
1337                if self.bottomSidebar.isAutoHiding():
1338                    self.bottomSidebar.setFocus()
1339
1340    def __openOnStartup(self, startupType=None):
1341        """
1342        Private method to open the last file, project or multiproject.
1343
1344        @param startupType type of startup requested (string, one of
1345            "Nothing", "File", "Project", "MultiProject" or "Session")
1346        """
1347        startupTypeMapping = {
1348            "Nothing": 0,
1349            "File": 1,
1350            "Project": 2,
1351            "MultiProject": 3,
1352            "Session": 4,
1353        }
1354
1355        if startupType is None:
1356            startup = Preferences.getUI("OpenOnStartup")
1357        else:
1358            try:
1359                startup = startupTypeMapping[startupType]
1360            except KeyError:
1361                startup = Preferences.getUI("OpenOnStartup")
1362
1363        if startup == 0:
1364            # open nothing
1365            pass
1366        elif startup == 1:
1367            # open last file
1368            recent = self.viewmanager.getMostRecent()
1369            if recent is not None:
1370                self.viewmanager.openFiles(recent)
1371        elif startup == 2:
1372            # open last project
1373            recent = self.project.getMostRecent()
1374            if recent is not None:
1375                self.project.openProject(recent)
1376        elif startup == 3:
1377            # open last multiproject
1378            recent = self.multiProject.getMostRecent()
1379            if recent is not None:
1380                self.multiProject.openMultiProject(recent)
1381        elif startup == 4:
1382            # open from session file
1383            self.__readSession()
1384
1385    def processArgs(self, args):
1386        """
1387        Public method to process the command line args passed to the UI.
1388
1389        @param args list of files to open<br />
1390            The args are processed one at a time. All arguments after a
1391            '--' option are considered debug arguments to the program
1392            for the debugger. All files named before the '--' option
1393            are opened in a text editor, unless the argument ends in
1394            .epj or .e4p, then it is opened as a project file. If it
1395            ends in .emj, .e4m or .e5m, it is opened as a multi project.
1396        """
1397        # check and optionally read a crash session and ignore any arguments
1398        if self.__readCrashSession():
1399            return
1400
1401        # no args, return
1402        if args is None:
1403            if self.__openAtStartup:
1404                self.__openOnStartup()
1405            return
1406
1407        opens = 0
1408
1409        # holds space delimited list of command args, if any
1410        argsStr = None
1411        # flag indicating '--' options was found
1412        ddseen = False
1413
1414        argChars = ['-', '/'] if Utilities.isWindowsPlatform() else ['-']
1415
1416        for arg in args:
1417            # handle a request to start with last session
1418            if arg == '--start-file':
1419                self.__openOnStartup("File")
1420                # ignore all further arguments
1421                return
1422            elif arg == '--start-multi':
1423                self.__openOnStartup("MultiProject")
1424                # ignore all further arguments
1425                return
1426            elif arg == '--start-project':
1427                self.__openOnStartup("Project")
1428                # ignore all further arguments
1429                return
1430            elif arg == '--start-session':
1431                self.__openOnStartup("Session")
1432                # ignore all further arguments
1433                return
1434
1435            if arg == '--' and not ddseen:
1436                ddseen = True
1437                continue
1438
1439            if arg[0] in argChars or ddseen:
1440                if argsStr is None:
1441                    argsStr = arg
1442                else:
1443                    argsStr = "{0} {1}".format(argsStr, arg)
1444                continue
1445
1446            try:
1447                ext = os.path.splitext(arg)[1]
1448                ext = os.path.normcase(ext)
1449            except IndexError:
1450                ext = ""
1451
1452            if ext in ('.epj', '.e4p'):
1453                self.project.openProject(arg)
1454                opens += 1
1455            elif ext in ('.emj', '.e4m', '.e5m'):
1456                self.multiProject.openMultiProject(arg)
1457                opens += 1
1458            else:
1459                self.viewmanager.openFiles(arg)
1460                opens += 1
1461
1462        # store away any args we had
1463        if argsStr is not None:
1464            self.debuggerUI.setArgvHistory(argsStr)
1465
1466        if opens == 0 and self.__openAtStartup:
1467            # no files, project or multiproject was given
1468            self.__openOnStartup()
1469
1470    def processInstallInfoFile(self):
1471        """
1472        Public method to process the file containing installation information.
1473        """
1474        import Globals
1475
1476        installInfoFile = Globals.getInstallInfoFilePath()
1477        if not os.path.exists(installInfoFile):
1478            filename = os.path.join(getConfig("ericDir"), "eric6install.json")
1479            if os.path.exists(filename):
1480                # eric was installed via the install.py script
1481                shutil.copy2(filename, installInfoFile)
1482            else:
1483                filename = os.path.join(getConfig("ericDir"),
1484                                        "eric6installpip.json")
1485                if os.path.exists(filename):
1486                    # eric was installed via pip (i.e. eric-ide)
1487                    with contextlib.suppress(OSError):
1488                        installDateTime = datetime.datetime.now(tz=None)
1489                        with open(filename, "r") as infoFile:
1490                            installInfo = json.load(infoFile)
1491                        installInfo["guessed"] = True
1492                        installInfo["eric"] = getConfig("ericDir")
1493                        installInfo["virtualenv"] = (
1494                            installInfo["eric"].startswith(
1495                                os.path.expanduser("~"))
1496                        )
1497                        if installInfo["virtualenv"]:
1498                            installInfo["user"] = getpass.getuser()
1499                            installInfo["exe"] = sys.executable
1500                        installInfo["installed"] = True
1501                        installInfo["installed_on"] = installDateTime.strftime(
1502                            "%Y-%m-%d %H:%M:%S")
1503                        installInfo["sudo"] = not os.access(
1504                            installInfo["eric"], os.W_OK)
1505                        with open(installInfoFile, "w") as infoFile:
1506                            json.dump(installInfo, infoFile, indent=2)
1507        else:
1508            changed = False
1509            with open(installInfoFile, "r") as infoFile:
1510                installInfo = json.load(infoFile)
1511
1512            # 1. adapt stored file to latest format
1513            if "install_cwd" not in installInfo:
1514                installInfo["install_cwd"] = ""
1515                installInfo["install_cwd_edited"] = False
1516                changed = True
1517            if "installed_on" not in installInfo:
1518                installInfo["installed_on"] = ""
1519                changed = True
1520
1521            # 2. merge new data into stored file
1522            filename = os.path.join(getConfig("ericDir"), "eric6install.json")
1523            if os.path.exists(filename):
1524                # eric was updated via the install.py script
1525                if (
1526                    os.path.getmtime(filename) >
1527                    os.path.getmtime(installInfoFile)
1528                ):
1529                    if not installInfo["edited"]:
1530                        shutil.copy2(filename, installInfoFile)
1531                    else:
1532                        with open(filename, "r") as infoFile:
1533                            installInfo2 = json.load(infoFile)
1534                        if not installInfo["install_cwd_edited"]:
1535                            installInfo2["install_cwd"] = installInfo[
1536                                "install_cwd"]
1537                        if not installInfo["exe_edited"]:
1538                            installInfo2["exe"] = installInfo["exe"]
1539                        if not installInfo["argv_edited"]:
1540                            installInfo2["argv"] = installInfo["argv"]
1541                        if not installInfo["eric_edited"]:
1542                            installInfo2["eric"] = installInfo["eric"]
1543                        installInfo = installInfo2
1544                        changed = True
1545            else:
1546                filename = os.path.join(getConfig("ericDir"),
1547                                        "eric6installpip.json")
1548                if os.path.exists(filename):
1549                    # eric was updated via pip (i.e. eric-ide)
1550                    # just update the installation date and time
1551                    installDateTime = datetime.datetime.now(tz=None)
1552                    installInfo["installed_on"] = installDateTime.strftime(
1553                        "%Y-%m-%d %H:%M:%S")
1554                    changed = True
1555
1556            if changed:
1557                with open(installInfoFile, "w") as infoFile:
1558                    json.dump(installInfo, infoFile, indent=2)
1559
1560    def __createDockWindow(self, name):
1561        """
1562        Private method to create a dock window with common properties.
1563
1564        @param name object name of the new dock window (string)
1565        @return the generated dock window (QDockWindow)
1566        """
1567        dock = QDockWidget()
1568        dock.setObjectName(name)
1569        dock.setFeatures(
1570            QDockWidget.DockWidgetFeatures(
1571                QDockWidget.DockWidgetFeature.DockWidgetClosable |
1572                QDockWidget.DockWidgetFeature.DockWidgetMovable |
1573                QDockWidget.DockWidgetFeature.DockWidgetFloatable
1574            )
1575        )
1576        return dock
1577
1578    def __setupDockWindow(self, dock, where, widget, caption):
1579        """
1580        Private method to configure the dock window created with
1581        __createDockWindow().
1582
1583        @param dock the dock window (QDockWindow)
1584        @param where dock area to be docked to (Qt.DockWidgetArea)
1585        @param widget widget to be shown in the dock window (QWidget)
1586        @param caption caption of the dock window (string)
1587        """
1588        if caption is None:
1589            caption = ""
1590        self.addDockWidget(where, dock)
1591        dock.setWidget(widget)
1592        dock.setWindowTitle(caption)
1593        dock.show()
1594
1595    def __setWindowCaption(self, editor=None, project=None):
1596        """
1597        Private method to set the caption of the Main Window.
1598
1599        @param editor filename to be displayed (string)
1600        @param project project name to be displayed (string)
1601        """
1602        if editor is not None and self.captionShowsFilename:
1603            self.capEditor = Utilities.compactPath(editor, self.maxFilePathLen)
1604        if project is not None:
1605            self.capProject = project
1606
1607        if self.passiveMode:
1608            if not self.capProject and not self.capEditor:
1609                self.setWindowTitle(
1610                    self.tr("{0} - Passive Mode").format(Program))
1611            elif self.capProject and not self.capEditor:
1612                self.setWindowTitle(
1613                    self.tr("{0} - {1} - Passive Mode")
1614                        .format(self.capProject, Program))
1615            elif not self.capProject and self.capEditor:
1616                self.setWindowTitle(
1617                    self.tr("{0} - {1} - Passive Mode")
1618                        .format(self.capEditor, Program))
1619            else:
1620                self.setWindowTitle(
1621                    self.tr("{0} - {1} - {2} - Passive Mode")
1622                    .format(self.capProject, self.capEditor, Program))
1623        else:
1624            if not self.capProject and not self.capEditor:
1625                self.setWindowTitle(Program)
1626            elif self.capProject and not self.capEditor:
1627                self.setWindowTitle(
1628                    "{0} - {1}".format(self.capProject, Program))
1629            elif not self.capProject and self.capEditor:
1630                self.setWindowTitle(
1631                    "{0} - {1}".format(self.capEditor, Program))
1632            else:
1633                self.setWindowTitle("{0} - {1} - {2}".format(
1634                    self.capProject, self.capEditor, Program))
1635
1636    def __initActions(self):
1637        """
1638        Private method to define the user interface actions.
1639        """
1640        self.actions = []
1641        self.wizardsActions = []
1642
1643        self.exitAct = E5Action(
1644            self.tr('Quit'),
1645            UI.PixmapCache.getIcon("exit"),
1646            self.tr('&Quit'),
1647            QKeySequence(self.tr("Ctrl+Q", "File|Quit")),
1648            0, self, 'quit')
1649        self.exitAct.setStatusTip(self.tr('Quit the IDE'))
1650        self.exitAct.setWhatsThis(self.tr(
1651            """<b>Quit the IDE</b>"""
1652            """<p>This quits the IDE. Any unsaved changes may be saved"""
1653            """ first. Any Python program being debugged will be stopped"""
1654            """ and the preferences will be written to disc.</p>"""
1655        ))
1656        self.exitAct.triggered.connect(self.__quit)
1657        self.exitAct.setMenuRole(QAction.MenuRole.QuitRole)
1658        self.actions.append(self.exitAct)
1659
1660        self.restartAct = E5Action(
1661            self.tr('Restart'),
1662            UI.PixmapCache.getIcon("restart"),
1663            self.tr('Restart'),
1664            QKeySequence(self.tr("Ctrl+Shift+Q", "File|Quit")),
1665            0, self, 'restart_eric')
1666        self.restartAct.setStatusTip(self.tr('Restart the IDE'))
1667        self.restartAct.setWhatsThis(self.tr(
1668            """<b>Restart the IDE</b>"""
1669            """<p>This restarts the IDE. Any unsaved changes may be saved"""
1670            """ first. Any Python program being debugged will be stopped"""
1671            """ and the preferences will be written to disc.</p>"""
1672        ))
1673        self.restartAct.triggered.connect(self.__restart)
1674        self.actions.append(self.restartAct)
1675
1676        self.saveSessionAct = E5Action(
1677            self.tr('Save session'),
1678            self.tr('Save session...'),
1679            0, 0, self, 'save_session_to_file')
1680        self.saveSessionAct.setStatusTip(self.tr('Save session'))
1681        self.saveSessionAct.setWhatsThis(self.tr(
1682            """<b>Save session...</b>"""
1683            """<p>This saves the current session to disk. A dialog is"""
1684            """ opened to select the file name.</p>"""
1685        ))
1686        self.saveSessionAct.triggered.connect(self.__saveSessionToFile)
1687        self.actions.append(self.saveSessionAct)
1688
1689        self.loadSessionAct = E5Action(
1690            self.tr('Load session'),
1691            self.tr('Load session...'),
1692            0, 0, self, 'load_session_from_file')
1693        self.loadSessionAct.setStatusTip(self.tr('Load session'))
1694        self.loadSessionAct.setWhatsThis(self.tr(
1695            """<b>Load session...</b>"""
1696            """<p>This loads a session saved to disk previously. A dialog is"""
1697            """ opened to select the file name.</p>"""
1698        ))
1699        self.loadSessionAct.triggered.connect(self.__loadSessionFromFile)
1700        self.actions.append(self.loadSessionAct)
1701
1702        self.newWindowAct = E5Action(
1703            self.tr('New Window'),
1704            UI.PixmapCache.getIcon("newWindow"),
1705            self.tr('New &Window'),
1706            QKeySequence(self.tr("Ctrl+Shift+N", "File|New Window")),
1707            0, self, 'new_window')
1708        self.newWindowAct.setStatusTip(self.tr(
1709            'Open a new eric instance'))
1710        self.newWindowAct.setWhatsThis(self.tr(
1711            """<b>New Window</b>"""
1712            """<p>This opens a new instance of the eric IDE.</p>"""
1713        ))
1714        self.newWindowAct.triggered.connect(self.__newWindow)
1715        self.actions.append(self.newWindowAct)
1716        self.newWindowAct.setEnabled(
1717            not Preferences.getUI("SingleApplicationMode"))
1718
1719        self.viewProfileActGrp = createActionGroup(self, "viewprofiles", True)
1720
1721        self.setEditProfileAct = E5Action(
1722            self.tr('Edit Profile'),
1723            UI.PixmapCache.getIcon("viewProfileEdit"),
1724            self.tr('Edit Profile'),
1725            0, 0,
1726            self.viewProfileActGrp, 'edit_profile', True)
1727        self.setEditProfileAct.setStatusTip(self.tr(
1728            'Activate the edit view profile'))
1729        self.setEditProfileAct.setWhatsThis(self.tr(
1730            """<b>Edit Profile</b>"""
1731            """<p>Activate the "Edit View Profile". Windows being shown,"""
1732            """ if this profile is active, may be configured with the"""
1733            """ "View Profile Configuration" dialog.</p>"""
1734        ))
1735        self.setEditProfileAct.triggered.connect(self.__setEditProfile)
1736        self.actions.append(self.setEditProfileAct)
1737
1738        self.setDebugProfileAct = E5Action(
1739            self.tr('Debug Profile'),
1740            UI.PixmapCache.getIcon("viewProfileDebug"),
1741            self.tr('Debug Profile'),
1742            0, 0,
1743            self.viewProfileActGrp, 'debug_profile', True)
1744        self.setDebugProfileAct.setStatusTip(
1745            self.tr('Activate the debug view profile'))
1746        self.setDebugProfileAct.setWhatsThis(self.tr(
1747            """<b>Debug Profile</b>"""
1748            """<p>Activate the "Debug View Profile". Windows being shown,"""
1749            """ if this profile is active, may be configured with the"""
1750            """ "View Profile Configuration" dialog.</p>"""
1751        ))
1752        self.setDebugProfileAct.triggered.connect(self.setDebugProfile)
1753        self.actions.append(self.setDebugProfileAct)
1754
1755        self.pbActivateAct = E5Action(
1756            self.tr('Project-Viewer'),
1757            self.tr('&Project-Viewer'),
1758            QKeySequence(self.tr("Alt+Shift+P")),
1759            0, self,
1760            'project_viewer_activate')
1761        self.pbActivateAct.setStatusTip(self.tr(
1762            "Switch the input focus to the Project-Viewer window."))
1763        self.pbActivateAct.setWhatsThis(self.tr(
1764            """<b>Activate Project-Viewer</b>"""
1765            """<p>This switches the input focus to the Project-Viewer"""
1766            """ window.</p>"""
1767        ))
1768        self.pbActivateAct.triggered.connect(self.__activateProjectBrowser)
1769        self.actions.append(self.pbActivateAct)
1770        self.addAction(self.pbActivateAct)
1771
1772        self.mpbActivateAct = E5Action(
1773            self.tr('Multiproject-Viewer'),
1774            self.tr('&Multiproject-Viewer'),
1775            QKeySequence(self.tr("Alt+Shift+M")),
1776            0, self,
1777            'multi_project_viewer_activate')
1778        self.mpbActivateAct.setStatusTip(self.tr(
1779            "Switch the input focus to the Multiproject-Viewer window."))
1780        self.mpbActivateAct.setWhatsThis(self.tr(
1781            """<b>Activate Multiproject-Viewer</b>"""
1782            """<p>This switches the input focus to the Multiproject-Viewer"""
1783            """ window.</p>"""
1784        ))
1785        self.mpbActivateAct.triggered.connect(
1786            self.__activateMultiProjectBrowser)
1787        self.actions.append(self.mpbActivateAct)
1788        self.addAction(self.mpbActivateAct)
1789
1790        self.debugViewerActivateAct = E5Action(
1791            self.tr('Debug-Viewer'),
1792            self.tr('&Debug-Viewer'),
1793            QKeySequence(self.tr("Alt+Shift+D")),
1794            0, self,
1795            'debug_viewer_activate')
1796        self.debugViewerActivateAct.setStatusTip(self.tr(
1797            "Switch the input focus to the Debug-Viewer window."))
1798        self.debugViewerActivateAct.setWhatsThis(self.tr(
1799            """<b>Activate Debug-Viewer</b>"""
1800            """<p>This switches the input focus to the Debug-Viewer"""
1801            """ window.</p>"""
1802        ))
1803        self.debugViewerActivateAct.triggered.connect(
1804            self.activateDebugViewer)
1805        self.actions.append(self.debugViewerActivateAct)
1806        self.addAction(self.debugViewerActivateAct)
1807
1808        self.shellActivateAct = E5Action(
1809            self.tr('Shell'),
1810            self.tr('&Shell'),
1811            QKeySequence(self.tr("Alt+Shift+S")),
1812            0, self,
1813            'interpreter_shell_activate')
1814        self.shellActivateAct.setStatusTip(self.tr(
1815            "Switch the input focus to the Shell window."))
1816        self.shellActivateAct.setWhatsThis(self.tr(
1817            """<b>Activate Shell</b>"""
1818            """<p>This switches the input focus to the Shell window.</p>"""
1819        ))
1820        self.shellActivateAct.triggered.connect(self.__activateShell)
1821        self.actions.append(self.shellActivateAct)
1822        self.addAction(self.shellActivateAct)
1823
1824        if self.browser is not None:
1825            self.browserActivateAct = E5Action(
1826                self.tr('File-Browser'),
1827                self.tr('&File-Browser'),
1828                QKeySequence(self.tr("Alt+Shift+F")),
1829                0, self,
1830                'file_browser_activate')
1831            self.browserActivateAct.setStatusTip(self.tr(
1832                "Switch the input focus to the File-Browser window."))
1833            self.browserActivateAct.setWhatsThis(self.tr(
1834                """<b>Activate File-Browser</b>"""
1835                """<p>This switches the input focus to the File-Browser"""
1836                """ window.</p>"""
1837            ))
1838            self.browserActivateAct.triggered.connect(self.__activateBrowser)
1839            self.actions.append(self.browserActivateAct)
1840            self.addAction(self.browserActivateAct)
1841
1842        self.logViewerActivateAct = E5Action(
1843            self.tr('Log-Viewer'),
1844            self.tr('Lo&g-Viewer'),
1845            QKeySequence(self.tr("Alt+Shift+G")),
1846            0, self,
1847            'log_viewer_activate')
1848        self.logViewerActivateAct.setStatusTip(self.tr(
1849            "Switch the input focus to the Log-Viewer window."))
1850        self.logViewerActivateAct.setWhatsThis(self.tr(
1851            """<b>Activate Log-Viewer</b>"""
1852            """<p>This switches the input focus to the Log-Viewer"""
1853            """ window.</p>"""
1854        ))
1855        self.logViewerActivateAct.triggered.connect(
1856            self.__activateLogViewer)
1857        self.actions.append(self.logViewerActivateAct)
1858        self.addAction(self.logViewerActivateAct)
1859
1860        self.taskViewerActivateAct = E5Action(
1861            self.tr('Task-Viewer'),
1862            self.tr('&Task-Viewer'),
1863            QKeySequence(self.tr("Alt+Shift+T")),
1864            0, self,
1865            'task_viewer_activate')
1866        self.taskViewerActivateAct.setStatusTip(self.tr(
1867            "Switch the input focus to the Task-Viewer window."))
1868        self.taskViewerActivateAct.setWhatsThis(self.tr(
1869            """<b>Activate Task-Viewer</b>"""
1870            """<p>This switches the input focus to the Task-Viewer"""
1871            """ window.</p>"""
1872        ))
1873        self.taskViewerActivateAct.triggered.connect(
1874            self.__activateTaskViewer)
1875        self.actions.append(self.taskViewerActivateAct)
1876        self.addAction(self.taskViewerActivateAct)
1877
1878        if self.templateViewer is not None:
1879            self.templateViewerActivateAct = E5Action(
1880                self.tr('Template-Viewer'),
1881                self.tr('Templ&ate-Viewer'),
1882                QKeySequence(self.tr("Alt+Shift+A")),
1883                0, self,
1884                'template_viewer_activate')
1885            self.templateViewerActivateAct.setStatusTip(self.tr(
1886                "Switch the input focus to the Template-Viewer window."))
1887            self.templateViewerActivateAct.setWhatsThis(self.tr(
1888                """<b>Activate Template-Viewer</b>"""
1889                """<p>This switches the input focus to the Template-Viewer"""
1890                """ window.</p>"""
1891            ))
1892            self.templateViewerActivateAct.triggered.connect(
1893                self.__activateTemplateViewer)
1894            self.actions.append(self.templateViewerActivateAct)
1895            self.addAction(self.templateViewerActivateAct)
1896
1897        self.ltAct = E5Action(
1898            self.tr('Left Toolbox'),
1899            self.tr('&Left Toolbox'), 0, 0, self, 'vertical_toolbox', True)
1900        self.ltAct.setStatusTip(self.tr('Toggle the Left Toolbox window'))
1901        self.ltAct.setWhatsThis(self.tr(
1902            """<b>Toggle the Left Toolbox window</b>"""
1903            """<p>If the Left Toolbox window is hidden then display it."""
1904            """ If it is displayed then close it.</p>"""
1905        ))
1906        self.ltAct.triggered.connect(self.__toggleLeftToolbox)
1907        self.actions.append(self.ltAct)
1908
1909        self.rtAct = E5Action(
1910            self.tr('Right Toolbox'),
1911            self.tr('&Right Toolbox'),
1912            0, 0, self, 'vertical_toolbox', True)
1913        self.rtAct.setStatusTip(self.tr('Toggle the Right Toolbox window'))
1914        self.rtAct.setWhatsThis(self.tr(
1915            """<b>Toggle the Right Toolbox window</b>"""
1916            """<p>If the Right Toolbox window is hidden then display it."""
1917            """ If it is displayed then close it.</p>"""
1918        ))
1919        self.rtAct.triggered.connect(self.__toggleRightToolbox)
1920        self.actions.append(self.rtAct)
1921
1922        self.htAct = E5Action(
1923            self.tr('Horizontal Toolbox'),
1924            self.tr('&Horizontal Toolbox'), 0, 0, self,
1925            'horizontal_toolbox', True)
1926        self.htAct.setStatusTip(self.tr(
1927            'Toggle the Horizontal Toolbox window'))
1928        self.htAct.setWhatsThis(self.tr(
1929            """<b>Toggle the Horizontal Toolbox window</b>"""
1930            """<p>If the Horizontal Toolbox window is hidden then display"""
1931            """ it. If it is displayed then close it.</p>"""
1932        ))
1933        self.htAct.triggered.connect(self.__toggleHorizontalToolbox)
1934        self.actions.append(self.htAct)
1935
1936        self.lsbAct = E5Action(
1937            self.tr('Left Sidebar'),
1938            self.tr('&Left Sidebar'),
1939            0, 0, self, 'left_sidebar', True)
1940        self.lsbAct.setStatusTip(self.tr('Toggle the left sidebar window'))
1941        self.lsbAct.setWhatsThis(self.tr(
1942            """<b>Toggle the left sidebar window</b>"""
1943            """<p>If the left sidebar window is hidden then display it."""
1944            """ If it is displayed then close it.</p>"""
1945        ))
1946        self.lsbAct.triggered.connect(self.__toggleLeftSidebar)
1947        self.actions.append(self.lsbAct)
1948
1949        self.rsbAct = E5Action(
1950            self.tr('Right Sidebar'),
1951            self.tr('&Right Sidebar'),
1952            0, 0, self, 'right_sidebar', True)
1953        self.rsbAct.setStatusTip(self.tr(
1954            'Toggle the right sidebar window'))
1955        self.rsbAct.setWhatsThis(self.tr(
1956            """<b>Toggle the right sidebar window</b>"""
1957            """<p>If the right sidebar window is hidden then display it."""
1958            """ If it is displayed then close it.</p>"""
1959        ))
1960        self.rsbAct.triggered.connect(self.__toggleRightSidebar)
1961        self.actions.append(self.rsbAct)
1962
1963        self.bsbAct = E5Action(
1964            self.tr('Bottom Sidebar'),
1965            self.tr('&Bottom Sidebar'), 0, 0, self,
1966            'bottom_sidebar', True)
1967        self.bsbAct.setStatusTip(self.tr(
1968            'Toggle the bottom sidebar window'))
1969        self.bsbAct.setWhatsThis(self.tr(
1970            """<b>Toggle the bottom sidebar window</b>"""
1971            """<p>If the bottom sidebar window is hidden then display it."""
1972            """ If it is displayed then close it.</p>"""
1973        ))
1974        self.bsbAct.triggered.connect(self.__toggleBottomSidebar)
1975        self.actions.append(self.bsbAct)
1976
1977        if self.cooperation is not None:
1978            self.cooperationViewerActivateAct = E5Action(
1979                self.tr('Cooperation-Viewer'),
1980                self.tr('Co&operation-Viewer'),
1981                QKeySequence(self.tr("Alt+Shift+O")),
1982                0, self,
1983                'cooperation_viewer_activate')
1984            self.cooperationViewerActivateAct.setStatusTip(self.tr(
1985                "Switch the input focus to the Cooperation-Viewer window."))
1986            self.cooperationViewerActivateAct.setWhatsThis(self.tr(
1987                """<b>Activate Cooperation-Viewer</b>"""
1988                """<p>This switches the input focus to the"""
1989                """ Cooperation-Viewer window.</p>"""
1990            ))
1991            self.cooperationViewerActivateAct.triggered.connect(
1992                self.activateCooperationViewer)
1993            self.actions.append(self.cooperationViewerActivateAct)
1994            self.addAction(self.cooperationViewerActivateAct)
1995
1996        if self.irc is not None:
1997            self.ircActivateAct = E5Action(
1998                self.tr('IRC'),
1999                self.tr('&IRC'),
2000                QKeySequence(self.tr("Ctrl+Alt+Shift+I")),
2001                0, self,
2002                'irc_widget_activate')
2003            self.ircActivateAct.setStatusTip(self.tr(
2004                "Switch the input focus to the IRC window."))
2005            self.ircActivateAct.setWhatsThis(self.tr(
2006                """<b>Activate IRC</b>"""
2007                """<p>This switches the input focus to the IRC window.</p>"""
2008            ))
2009            self.ircActivateAct.triggered.connect(
2010                self.__activateIRC)
2011            self.actions.append(self.ircActivateAct)
2012            self.addAction(self.ircActivateAct)
2013
2014        if self.symbolsViewer is not None:
2015            self.symbolsViewerActivateAct = E5Action(
2016                self.tr('Symbols-Viewer'),
2017                self.tr('S&ymbols-Viewer'),
2018                QKeySequence(self.tr("Alt+Shift+Y")),
2019                0, self,
2020                'symbols_viewer_activate')
2021            self.symbolsViewerActivateAct.setStatusTip(self.tr(
2022                "Switch the input focus to the Symbols-Viewer window."))
2023            self.symbolsViewerActivateAct.setWhatsThis(self.tr(
2024                """<b>Activate Symbols-Viewer</b>"""
2025                """<p>This switches the input focus to the Symbols-Viewer"""
2026                """ window.</p>"""
2027            ))
2028            self.symbolsViewerActivateAct.triggered.connect(
2029                self.__activateSymbolsViewer)
2030            self.actions.append(self.symbolsViewerActivateAct)
2031            self.addAction(self.symbolsViewerActivateAct)
2032
2033        if self.numbersViewer is not None:
2034            self.numbersViewerActivateAct = E5Action(
2035                self.tr('Numbers-Viewer'),
2036                self.tr('Num&bers-Viewer'),
2037                QKeySequence(self.tr("Alt+Shift+B")),
2038                0, self,
2039                'numbers_viewer_activate')
2040            self.numbersViewerActivateAct.setStatusTip(self.tr(
2041                "Switch the input focus to the Numbers-Viewer window."))
2042            self.numbersViewerActivateAct.setWhatsThis(self.tr(
2043                """<b>Activate Numbers-Viewer</b>"""
2044                """<p>This switches the input focus to the Numbers-Viewer"""
2045                """ window.</p>"""
2046            ))
2047            self.numbersViewerActivateAct.triggered.connect(
2048                self.__activateNumbersViewer)
2049            self.actions.append(self.numbersViewerActivateAct)
2050            self.addAction(self.numbersViewerActivateAct)
2051
2052        if self.codeDocumentationViewer is not None:
2053            self.codeDocumentationViewerActivateAct = E5Action(
2054                self.tr('Code Documentation Viewer'),
2055                self.tr('Code Documentation Viewer'),
2056                QKeySequence(self.tr("Ctrl+Alt+Shift+D")),
2057                0, self,
2058                'code_documentation_viewer_activate')
2059            self.codeDocumentationViewerActivateAct.setStatusTip(self.tr(
2060                "Switch the input focus to the Code Documentation Viewer"
2061                " window."))
2062            self.codeDocumentationViewerActivateAct.setWhatsThis(self.tr(
2063                """<b>Code Documentation Viewer</b>"""
2064                """<p>This switches the input focus to the Code"""
2065                """ Documentation Viewer window.</p>"""
2066            ))
2067            self.codeDocumentationViewerActivateAct.triggered.connect(
2068                self.activateCodeDocumentationViewer)
2069            self.actions.append(self.codeDocumentationViewerActivateAct)
2070            self.addAction(self.codeDocumentationViewerActivateAct)
2071
2072        if self.pipWidget is not None:
2073            self.pipWidgetActivateAct = E5Action(
2074                self.tr('PyPI'),
2075                self.tr('PyPI'),
2076                QKeySequence(self.tr("Ctrl+Alt+Shift+P")),
2077                0, self,
2078                'pip_widget_activate')
2079            self.pipWidgetActivateAct.setStatusTip(self.tr(
2080                "Switch the input focus to the PyPI window."))
2081            self.pipWidgetActivateAct.setWhatsThis(self.tr(
2082                """<b>PyPI</b>"""
2083                """<p>This switches the input focus to the PyPI window.</p>"""
2084            ))
2085            self.pipWidgetActivateAct.triggered.connect(
2086                self.__activatePipWidget)
2087            self.actions.append(self.pipWidgetActivateAct)
2088            self.addAction(self.pipWidgetActivateAct)
2089
2090        if self.condaWidget is not None:
2091            self.condaWidgetActivateAct = E5Action(
2092                self.tr('Conda'),
2093                self.tr('Conda'),
2094                QKeySequence(self.tr("Ctrl+Alt+Shift+C")),
2095                0, self,
2096                'conda_widget_activate')
2097            self.condaWidgetActivateAct.setStatusTip(self.tr(
2098                "Switch the input focus to the Conda window."))
2099            self.condaWidgetActivateAct.setWhatsThis(self.tr(
2100                """<b>Conda</b>"""
2101                """<p>This switches the input focus to the Conda window.</p>"""
2102            ))
2103            self.condaWidgetActivateAct.triggered.connect(
2104                self.__activateCondaWidget)
2105            self.actions.append(self.condaWidgetActivateAct)
2106            self.addAction(self.condaWidgetActivateAct)
2107
2108        if self.microPythonWidget is not None:
2109            self.microPythonWidgetActivateAct = E5Action(
2110                self.tr('MicroPython'),
2111                self.tr('MicroPython'),
2112                QKeySequence(self.tr("Ctrl+Alt+Shift+M")),
2113                0, self,
2114                'micropython_widget_activate')
2115            self.microPythonWidgetActivateAct.setStatusTip(self.tr(
2116                "Switch the input focus to the MicroPython window."))
2117            self.microPythonWidgetActivateAct.setWhatsThis(self.tr(
2118                """<b>MicroPython</b>"""
2119                """<p>This switches the input focus to the MicroPython"""
2120                """ window.</p>"""
2121            ))
2122            self.microPythonWidgetActivateAct.triggered.connect(
2123                self.__activateMicroPython)
2124            self.actions.append(self.microPythonWidgetActivateAct)
2125            self.addAction(self.microPythonWidgetActivateAct)
2126
2127        self.whatsThisAct = E5Action(
2128            self.tr('What\'s This?'),
2129            UI.PixmapCache.getIcon("whatsThis"),
2130            self.tr('&What\'s This?'),
2131            QKeySequence(self.tr("Shift+F1")),
2132            0, self, 'whatsThis')
2133        self.whatsThisAct.setStatusTip(self.tr('Context sensitive help'))
2134        self.whatsThisAct.setWhatsThis(self.tr(
2135            """<b>Display context sensitive help</b>"""
2136            """<p>In What's This? mode, the mouse cursor shows an arrow with"""
2137            """ a question mark, and you can click on the interface elements"""
2138            """ to get a short description of what they do and how to use"""
2139            """ them. In dialogs, this feature can be accessed using the"""
2140            """ context help button in the titlebar.</p>"""
2141        ))
2142        self.whatsThisAct.triggered.connect(self.__whatsThis)
2143        self.actions.append(self.whatsThisAct)
2144
2145        self.helpviewerAct = E5Action(
2146            self.tr('Helpviewer'),
2147            UI.PixmapCache.getIcon("help"),
2148            self.tr('&Helpviewer...'),
2149            QKeySequence(self.tr("F1")),
2150            0, self, 'helpviewer')
2151        self.helpviewerAct.setStatusTip(self.tr(
2152            'Open the helpviewer window'))
2153        self.helpviewerAct.setWhatsThis(self.tr(
2154            """<b>Helpviewer</b>"""
2155            """<p>Display the eric web browser. This window will show"""
2156            """ HTML help files and help from Qt help collections. It"""
2157            """ has the capability to navigate to links, set bookmarks,"""
2158            """ print the displayed help and some more features. You may"""
2159            """ use it to browse the internet as well</p><p>If called"""
2160            """ with a word selected, this word is search in the Qt help"""
2161            """ collection.</p>"""
2162        ))
2163        self.helpviewerAct.triggered.connect(self.__helpViewer)
2164        self.actions.append(self.helpviewerAct)
2165
2166        self.__initQtDocActions()
2167        self.__initPythonDocActions()
2168        self.__initEricDocAction()
2169        self.__initPySideDocActions()
2170
2171        self.versionAct = E5Action(
2172            self.tr('Show Versions'),
2173            self.tr('Show &Versions'),
2174            0, 0, self, 'show_versions')
2175        self.versionAct.setStatusTip(self.tr(
2176            'Display version information'))
2177        self.versionAct.setWhatsThis(self.tr(
2178            """<b>Show Versions</b>"""
2179            """<p>Display version information.</p>"""
2180        ))
2181        self.versionAct.triggered.connect(self.__showVersions)
2182        self.actions.append(self.versionAct)
2183
2184        self.checkUpdateAct = E5Action(
2185            self.tr('Check for Updates'),
2186            self.tr('Check for &Updates...'), 0, 0, self, 'check_updates')
2187        self.checkUpdateAct.setStatusTip(self.tr('Check for Updates'))
2188        self.checkUpdateAct.setWhatsThis(self.tr(
2189            """<b>Check for Updates...</b>"""
2190            """<p>Checks the internet for updates of eric.</p>"""
2191        ))
2192        self.checkUpdateAct.triggered.connect(self.performVersionCheck)
2193        self.actions.append(self.checkUpdateAct)
2194
2195        self.showVersionsAct = E5Action(
2196            self.tr('Show downloadable versions'),
2197            self.tr('Show &downloadable versions...'),
2198            0, 0, self, 'show_downloadable_versions')
2199        self.showVersionsAct.setStatusTip(
2200            self.tr('Show the versions available for download'))
2201        self.showVersionsAct.setWhatsThis(self.tr(
2202            """<b>Show downloadable versions...</b>"""
2203            """<p>Shows the eric versions available for download """
2204            """from the internet.</p>"""
2205        ))
2206        self.showVersionsAct.triggered.connect(
2207            self.showAvailableVersionsInfo)
2208        self.actions.append(self.showVersionsAct)
2209
2210        self.showErrorLogAct = E5Action(
2211            self.tr('Show Error Log'),
2212            self.tr('Show Error &Log...'),
2213            0, 0, self, 'show_error_log')
2214        self.showErrorLogAct.setStatusTip(self.tr('Show Error Log'))
2215        self.showErrorLogAct.setWhatsThis(self.tr(
2216            """<b>Show Error Log...</b>"""
2217            """<p>Opens a dialog showing the most recent error log.</p>"""
2218        ))
2219        self.showErrorLogAct.triggered.connect(self.__showErrorLog)
2220        self.actions.append(self.showErrorLogAct)
2221
2222        self.showInstallInfoAct = E5Action(
2223            self.tr('Show Install Info'),
2224            self.tr('Show Install &Info...'),
2225            0, 0, self, 'show_install_info')
2226        self.showInstallInfoAct.setStatusTip(self.tr(
2227            'Show Installation Information'))
2228        self.showInstallInfoAct.setWhatsThis(self.tr(
2229            """<b>Show Install Info...</b>"""
2230            """<p>Opens a dialog showing some information about the"""
2231            """ installation process.</p>"""
2232        ))
2233        self.showInstallInfoAct.triggered.connect(self.__showInstallInfo)
2234        self.actions.append(self.showInstallInfoAct)
2235
2236        self.reportBugAct = E5Action(
2237            self.tr('Report Bug'),
2238            self.tr('Report &Bug...'),
2239            0, 0, self, 'report_bug')
2240        self.reportBugAct.setStatusTip(self.tr('Report a bug'))
2241        self.reportBugAct.setWhatsThis(self.tr(
2242            """<b>Report Bug...</b>"""
2243            """<p>Opens a dialog to report a bug.</p>"""
2244        ))
2245        self.reportBugAct.triggered.connect(self.__reportBug)
2246        self.actions.append(self.reportBugAct)
2247
2248        self.requestFeatureAct = E5Action(
2249            self.tr('Request Feature'),
2250            self.tr('Request &Feature...'),
2251            0, 0, self, 'request_feature')
2252        self.requestFeatureAct.setStatusTip(self.tr(
2253            'Send a feature request'))
2254        self.requestFeatureAct.setWhatsThis(self.tr(
2255            """<b>Request Feature...</b>"""
2256            """<p>Opens a dialog to send a feature request.</p>"""
2257        ))
2258        self.requestFeatureAct.triggered.connect(self.__requestFeature)
2259        self.actions.append(self.requestFeatureAct)
2260
2261        self.utActGrp = createActionGroup(self)
2262
2263        self.utDialogAct = E5Action(
2264            self.tr('Unittest'),
2265            UI.PixmapCache.getIcon("unittest"),
2266            self.tr('&Unittest...'),
2267            0, 0, self.utActGrp, 'unittest')
2268        self.utDialogAct.setStatusTip(self.tr('Start unittest dialog'))
2269        self.utDialogAct.setWhatsThis(self.tr(
2270            """<b>Unittest</b>"""
2271            """<p>Perform unit tests. The dialog gives you the"""
2272            """ ability to select and run a unittest suite.</p>"""
2273        ))
2274        self.utDialogAct.triggered.connect(self.__unittest)
2275        self.actions.append(self.utDialogAct)
2276
2277        self.utRestartAct = E5Action(
2278            self.tr('Unittest Restart'),
2279            UI.PixmapCache.getIcon("unittestRestart"),
2280            self.tr('&Restart Unittest...'),
2281            0, 0, self.utActGrp, 'unittest_restart')
2282        self.utRestartAct.setStatusTip(self.tr('Restart last unittest'))
2283        self.utRestartAct.setWhatsThis(self.tr(
2284            """<b>Restart Unittest</b>"""
2285            """<p>Restart the unittest performed last.</p>"""
2286        ))
2287        self.utRestartAct.triggered.connect(self.__unittestRestart)
2288        self.utRestartAct.setEnabled(False)
2289        self.actions.append(self.utRestartAct)
2290
2291        self.utRerunFailedAct = E5Action(
2292            self.tr('Unittest Rerun Failed'),
2293            UI.PixmapCache.getIcon("unittestRerunFailed"),
2294            self.tr('Rerun Failed Tests...'),
2295            0, 0, self.utActGrp, 'unittest_rerun_failed')
2296        self.utRerunFailedAct.setStatusTip(self.tr(
2297            'Rerun failed tests of the last run'))
2298        self.utRerunFailedAct.setWhatsThis(self.tr(
2299            """<b>Rerun Failed Tests</b>"""
2300            """<p>Rerun all tests that failed during the last unittest"""
2301            """ run.</p>"""
2302        ))
2303        self.utRerunFailedAct.triggered.connect(self.__unittestRerunFailed)
2304        self.utRerunFailedAct.setEnabled(False)
2305        self.actions.append(self.utRerunFailedAct)
2306
2307        self.utScriptAct = E5Action(
2308            self.tr('Unittest Script'),
2309            UI.PixmapCache.getIcon("unittestScript"),
2310            self.tr('Unittest &Script...'),
2311            0, 0, self.utActGrp, 'unittest_script')
2312        self.utScriptAct.setStatusTip(self.tr(
2313            'Run unittest with current script'))
2314        self.utScriptAct.setWhatsThis(self.tr(
2315            """<b>Unittest Script</b>"""
2316            """<p>Run unittest with current script.</p>"""
2317        ))
2318        self.utScriptAct.triggered.connect(self.__unittestScript)
2319        self.utScriptAct.setEnabled(False)
2320        self.actions.append(self.utScriptAct)
2321
2322        self.utProjectAct = E5Action(
2323            self.tr('Unittest Project'),
2324            UI.PixmapCache.getIcon("unittestProject"),
2325            self.tr('Unittest &Project...'),
2326            0, 0, self.utActGrp, 'unittest_project')
2327        self.utProjectAct.setStatusTip(self.tr(
2328            'Run unittest with current project'))
2329        self.utProjectAct.setWhatsThis(self.tr(
2330            """<b>Unittest Project</b>"""
2331            """<p>Run unittest with current project.</p>"""
2332        ))
2333        self.utProjectAct.triggered.connect(self.__unittestProject)
2334        self.utProjectAct.setEnabled(False)
2335        self.actions.append(self.utProjectAct)
2336
2337        # check for Qt5 designer and linguist
2338        if Utilities.isWindowsPlatform():
2339            designerExe = os.path.join(
2340                Utilities.getQtBinariesPath(),
2341                "{0}.exe".format(Utilities.generateQtToolName("designer")))
2342        elif Utilities.isMacPlatform():
2343            designerExe = Utilities.getQtMacBundle("designer")
2344        else:
2345            designerExe = os.path.join(
2346                Utilities.getQtBinariesPath(),
2347                Utilities.generateQtToolName("designer"))
2348        if os.path.exists(designerExe):
2349            self.designer4Act = E5Action(
2350                self.tr('Qt-Designer'),
2351                UI.PixmapCache.getIcon("designer4"),
2352                self.tr('Qt-&Designer...'),
2353                0, 0, self, 'qt_designer4')
2354            self.designer4Act.setStatusTip(self.tr('Start Qt-Designer'))
2355            self.designer4Act.setWhatsThis(self.tr(
2356                """<b>Qt-Designer</b>"""
2357                """<p>Start Qt-Designer.</p>"""
2358            ))
2359            self.designer4Act.triggered.connect(self.__designer)
2360            self.actions.append(self.designer4Act)
2361        else:
2362            self.designer4Act = None
2363
2364        if Utilities.isWindowsPlatform():
2365            linguistExe = os.path.join(
2366                Utilities.getQtBinariesPath(),
2367                "{0}.exe".format(Utilities.generateQtToolName("linguist")))
2368        elif Utilities.isMacPlatform():
2369            linguistExe = Utilities.getQtMacBundle("linguist")
2370        else:
2371            linguistExe = os.path.join(
2372                Utilities.getQtBinariesPath(),
2373                Utilities.generateQtToolName("linguist"))
2374        if os.path.exists(linguistExe):
2375            self.linguist4Act = E5Action(
2376                self.tr('Qt-Linguist'),
2377                UI.PixmapCache.getIcon("linguist4"),
2378                self.tr('Qt-&Linguist...'),
2379                0, 0, self, 'qt_linguist4')
2380            self.linguist4Act.setStatusTip(self.tr('Start Qt-Linguist'))
2381            self.linguist4Act.setWhatsThis(self.tr(
2382                """<b>Qt-Linguist</b>"""
2383                """<p>Start Qt-Linguist.</p>"""
2384            ))
2385            self.linguist4Act.triggered.connect(self.__linguist)
2386            self.actions.append(self.linguist4Act)
2387        else:
2388            self.linguist4Act = None
2389
2390        self.uipreviewerAct = E5Action(
2391            self.tr('UI Previewer'),
2392            UI.PixmapCache.getIcon("uiPreviewer"),
2393            self.tr('&UI Previewer...'),
2394            0, 0, self, 'ui_previewer')
2395        self.uipreviewerAct.setStatusTip(self.tr('Start the UI Previewer'))
2396        self.uipreviewerAct.setWhatsThis(self.tr(
2397            """<b>UI Previewer</b>"""
2398            """<p>Start the UI Previewer.</p>"""
2399        ))
2400        self.uipreviewerAct.triggered.connect(self.__UIPreviewer)
2401        self.actions.append(self.uipreviewerAct)
2402
2403        self.trpreviewerAct = E5Action(
2404            self.tr('Translations Previewer'),
2405            UI.PixmapCache.getIcon("trPreviewer"),
2406            self.tr('&Translations Previewer...'),
2407            0, 0, self, 'tr_previewer')
2408        self.trpreviewerAct.setStatusTip(self.tr(
2409            'Start the Translations Previewer'))
2410        self.trpreviewerAct.setWhatsThis(self.tr(
2411            """<b>Translations Previewer</b>"""
2412            """<p>Start the Translations Previewer.</p>"""
2413        ))
2414        self.trpreviewerAct.triggered.connect(self.__TRPreviewer)
2415        self.actions.append(self.trpreviewerAct)
2416
2417        self.diffAct = E5Action(
2418            self.tr('Compare Files'),
2419            UI.PixmapCache.getIcon("diffFiles"),
2420            self.tr('&Compare Files...'),
2421            0, 0, self, 'diff_files')
2422        self.diffAct.setStatusTip(self.tr('Compare two files'))
2423        self.diffAct.setWhatsThis(self.tr(
2424            """<b>Compare Files</b>"""
2425            """<p>Open a dialog to compare two files.</p>"""
2426        ))
2427        self.diffAct.triggered.connect(self.__compareFiles)
2428        self.actions.append(self.diffAct)
2429
2430        self.compareAct = E5Action(
2431            self.tr('Compare Files side by side'),
2432            UI.PixmapCache.getIcon("compareFiles"),
2433            self.tr('Compare &Files side by side...'),
2434            0, 0, self, 'compare_files')
2435        self.compareAct.setStatusTip(self.tr('Compare two files'))
2436        self.compareAct.setWhatsThis(self.tr(
2437            """<b>Compare Files side by side</b>"""
2438            """<p>Open a dialog to compare two files and show the result"""
2439            """ side by side.</p>"""
2440        ))
2441        self.compareAct.triggered.connect(self.__compareFilesSbs)
2442        self.actions.append(self.compareAct)
2443
2444        self.sqlBrowserAct = E5Action(
2445            self.tr('SQL Browser'),
2446            UI.PixmapCache.getIcon("sqlBrowser"),
2447            self.tr('SQL &Browser...'),
2448            0, 0, self, 'sql_browser')
2449        self.sqlBrowserAct.setStatusTip(self.tr('Browse a SQL database'))
2450        self.sqlBrowserAct.setWhatsThis(self.tr(
2451            """<b>SQL Browser</b>"""
2452            """<p>Browse a SQL database.</p>"""
2453        ))
2454        self.sqlBrowserAct.triggered.connect(self.__sqlBrowser)
2455        self.actions.append(self.sqlBrowserAct)
2456
2457        self.miniEditorAct = E5Action(
2458            self.tr('Mini Editor'),
2459            UI.PixmapCache.getIcon("editor"),
2460            self.tr('Mini &Editor...'),
2461            0, 0, self, 'mini_editor')
2462        self.miniEditorAct.setStatusTip(self.tr('Mini Editor'))
2463        self.miniEditorAct.setWhatsThis(self.tr(
2464            """<b>Mini Editor</b>"""
2465            """<p>Open a dialog with a simplified editor.</p>"""
2466        ))
2467        self.miniEditorAct.triggered.connect(self.__openMiniEditor)
2468        self.actions.append(self.miniEditorAct)
2469
2470        self.hexEditorAct = E5Action(
2471            self.tr('Hex Editor'),
2472            UI.PixmapCache.getIcon("hexEditor"),
2473            self.tr('&Hex Editor...'),
2474            0, 0, self, 'hex_editor')
2475        self.hexEditorAct.setStatusTip(self.tr(
2476            'Start the eric Hex Editor'))
2477        self.hexEditorAct.setWhatsThis(self.tr(
2478            """<b>Hex Editor</b>"""
2479            """<p>Starts the eric Hex Editor for viewing or editing"""
2480            """ binary files.</p>"""
2481        ))
2482        self.hexEditorAct.triggered.connect(self.__openHexEditor)
2483        self.actions.append(self.hexEditorAct)
2484
2485        self.webBrowserAct = E5Action(
2486            self.tr('eric Web Browser'),
2487            UI.PixmapCache.getIcon("ericWeb"),
2488            self.tr('eric &Web Browser...'),
2489            0, 0, self, 'web_browser')
2490        self.webBrowserAct.setStatusTip(self.tr(
2491            'Start the eric Web Browser'))
2492        self.webBrowserAct.setWhatsThis(self.tr(
2493            """<b>eric Web Browser</b>"""
2494            """<p>Browse the Internet with the eric Web Browser.</p>"""
2495        ))
2496        self.webBrowserAct.triggered.connect(self.__startWebBrowser)
2497        self.actions.append(self.webBrowserAct)
2498
2499        self.iconEditorAct = E5Action(
2500            self.tr('Icon Editor'),
2501            UI.PixmapCache.getIcon("iconEditor"),
2502            self.tr('&Icon Editor...'),
2503            0, 0, self, 'icon_editor')
2504        self.iconEditorAct.setStatusTip(self.tr(
2505            'Start the eric Icon Editor'))
2506        self.iconEditorAct.setWhatsThis(self.tr(
2507            """<b>Icon Editor</b>"""
2508            """<p>Starts the eric Icon Editor for editing simple icons.</p>"""
2509        ))
2510        self.iconEditorAct.triggered.connect(self.__editPixmap)
2511        self.actions.append(self.iconEditorAct)
2512
2513        self.snapshotAct = E5Action(
2514            self.tr('Snapshot'),
2515            UI.PixmapCache.getIcon("ericSnap"),
2516            self.tr('&Snapshot...'),
2517            0, 0, self, 'snapshot')
2518        self.snapshotAct.setStatusTip(self.tr(
2519            'Take snapshots of a screen region'))
2520        self.snapshotAct.setWhatsThis(self.tr(
2521            """<b>Snapshot</b>"""
2522            """<p>This opens a dialog to take snapshots of a screen"""
2523            """ region.</p>"""
2524        ))
2525        self.snapshotAct.triggered.connect(self.__snapshot)
2526        self.actions.append(self.snapshotAct)
2527
2528        self.prefAct = E5Action(
2529            self.tr('Preferences'),
2530            UI.PixmapCache.getIcon("configure"),
2531            self.tr('&Preferences...'),
2532            0, 0, self, 'preferences')
2533        self.prefAct.setStatusTip(self.tr(
2534            'Set the prefered configuration'))
2535        self.prefAct.setWhatsThis(self.tr(
2536            """<b>Preferences</b>"""
2537            """<p>Set the configuration items of the application"""
2538            """ with your prefered values.</p>"""
2539        ))
2540        self.prefAct.triggered.connect(self.showPreferences)
2541        self.prefAct.setMenuRole(QAction.MenuRole.PreferencesRole)
2542        self.actions.append(self.prefAct)
2543
2544        self.prefExportAct = E5Action(
2545            self.tr('Export Preferences'),
2546            UI.PixmapCache.getIcon("configureExport"),
2547            self.tr('E&xport Preferences...'),
2548            0, 0, self, 'export_preferences')
2549        self.prefExportAct.setStatusTip(self.tr(
2550            'Export the current configuration'))
2551        self.prefExportAct.setWhatsThis(self.tr(
2552            """<b>Export Preferences</b>"""
2553            """<p>Export the current configuration to a file.</p>"""
2554        ))
2555        self.prefExportAct.triggered.connect(self.__exportPreferences)
2556        self.actions.append(self.prefExportAct)
2557
2558        self.prefImportAct = E5Action(
2559            self.tr('Import Preferences'),
2560            UI.PixmapCache.getIcon("configureImport"),
2561            self.tr('I&mport Preferences...'),
2562            0, 0, self, 'import_preferences')
2563        self.prefImportAct.setStatusTip(self.tr(
2564            'Import a previously exported configuration'))
2565        self.prefImportAct.setWhatsThis(self.tr(
2566            """<b>Import Preferences</b>"""
2567            """<p>Import a previously exported configuration.</p>"""
2568        ))
2569        self.prefImportAct.triggered.connect(self.__importPreferences)
2570        self.actions.append(self.prefImportAct)
2571
2572        self.reloadAPIsAct = E5Action(
2573            self.tr('Reload APIs'),
2574            self.tr('Reload &APIs'),
2575            0, 0, self, 'reload_apis')
2576        self.reloadAPIsAct.setStatusTip(self.tr(
2577            'Reload the API information'))
2578        self.reloadAPIsAct.setWhatsThis(self.tr(
2579            """<b>Reload APIs</b>"""
2580            """<p>Reload the API information.</p>"""
2581        ))
2582        self.reloadAPIsAct.triggered.connect(self.__reloadAPIs)
2583        self.actions.append(self.reloadAPIsAct)
2584
2585        self.showExternalToolsAct = E5Action(
2586            self.tr('Show external tools'),
2587            UI.PixmapCache.getIcon("showPrograms"),
2588            self.tr('Show external &tools'),
2589            0, 0, self, 'show_external_tools')
2590        self.showExternalToolsAct.setStatusTip(self.tr(
2591            'Show external tools'))
2592        self.showExternalToolsAct.setWhatsThis(self.tr(
2593            """<b>Show external tools</b>"""
2594            """<p>Opens a dialog to show the path and versions of all"""
2595            """ extenal tools used by eric.</p>"""
2596        ))
2597        self.showExternalToolsAct.triggered.connect(
2598            self.__showExternalTools)
2599        self.actions.append(self.showExternalToolsAct)
2600
2601        self.configViewProfilesAct = E5Action(
2602            self.tr('View Profiles'),
2603            UI.PixmapCache.getIcon("configureViewProfiles"),
2604            self.tr('&View Profiles...'),
2605            0, 0, self, 'view_profiles')
2606        self.configViewProfilesAct.setStatusTip(self.tr(
2607            'Configure view profiles'))
2608        self.configViewProfilesAct.setWhatsThis(self.tr(
2609            """<b>View Profiles</b>"""
2610            """<p>Configure the view profiles. With this dialog you may"""
2611            """ set the visibility of the various windows for the"""
2612            """ predetermined view profiles.</p>"""
2613        ))
2614        self.configViewProfilesAct.triggered.connect(
2615            self.__configViewProfiles)
2616        self.actions.append(self.configViewProfilesAct)
2617
2618        self.configToolBarsAct = E5Action(
2619            self.tr('Toolbars'),
2620            UI.PixmapCache.getIcon("toolbarsConfigure"),
2621            self.tr('Tool&bars...'),
2622            0, 0, self, 'configure_toolbars')
2623        self.configToolBarsAct.setStatusTip(self.tr('Configure toolbars'))
2624        self.configToolBarsAct.setWhatsThis(self.tr(
2625            """<b>Toolbars</b>"""
2626            """<p>Configure the toolbars. With this dialog you may"""
2627            """ change the actions shown on the various toolbars and"""
2628            """ define your own toolbars.</p>"""
2629        ))
2630        self.configToolBarsAct.triggered.connect(self.__configToolBars)
2631        self.actions.append(self.configToolBarsAct)
2632
2633        self.shortcutsAct = E5Action(
2634            self.tr('Keyboard Shortcuts'),
2635            UI.PixmapCache.getIcon("configureShortcuts"),
2636            self.tr('Keyboard &Shortcuts...'),
2637            0, 0, self, 'keyboard_shortcuts')
2638        self.shortcutsAct.setStatusTip(self.tr(
2639            'Set the keyboard shortcuts'))
2640        self.shortcutsAct.setWhatsThis(self.tr(
2641            """<b>Keyboard Shortcuts</b>"""
2642            """<p>Set the keyboard shortcuts of the application"""
2643            """ with your prefered values.</p>"""
2644        ))
2645        self.shortcutsAct.triggered.connect(self.__configShortcuts)
2646        self.actions.append(self.shortcutsAct)
2647
2648        self.exportShortcutsAct = E5Action(
2649            self.tr('Export Keyboard Shortcuts'),
2650            UI.PixmapCache.getIcon("exportShortcuts"),
2651            self.tr('&Export Keyboard Shortcuts...'),
2652            0, 0, self, 'export_keyboard_shortcuts')
2653        self.exportShortcutsAct.setStatusTip(self.tr(
2654            'Export the keyboard shortcuts'))
2655        self.exportShortcutsAct.setWhatsThis(self.tr(
2656            """<b>Export Keyboard Shortcuts</b>"""
2657            """<p>Export the keyboard shortcuts of the application.</p>"""
2658        ))
2659        self.exportShortcutsAct.triggered.connect(self.__exportShortcuts)
2660        self.actions.append(self.exportShortcutsAct)
2661
2662        self.importShortcutsAct = E5Action(
2663            self.tr('Import Keyboard Shortcuts'),
2664            UI.PixmapCache.getIcon("importShortcuts"),
2665            self.tr('&Import Keyboard Shortcuts...'),
2666            0, 0, self, 'import_keyboard_shortcuts')
2667        self.importShortcutsAct.setStatusTip(self.tr(
2668            'Import the keyboard shortcuts'))
2669        self.importShortcutsAct.setWhatsThis(self.tr(
2670            """<b>Import Keyboard Shortcuts</b>"""
2671            """<p>Import the keyboard shortcuts of the application.</p>"""
2672        ))
2673        self.importShortcutsAct.triggered.connect(self.__importShortcuts)
2674        self.actions.append(self.importShortcutsAct)
2675
2676        if SSL_AVAILABLE:
2677            self.certificatesAct = E5Action(
2678                self.tr('Manage SSL Certificates'),
2679                UI.PixmapCache.getIcon("certificates"),
2680                self.tr('Manage SSL Certificates...'),
2681                0, 0, self, 'manage_ssl_certificates')
2682            self.certificatesAct.setStatusTip(self.tr(
2683                'Manage the saved SSL certificates'))
2684            self.certificatesAct.setWhatsThis(self.tr(
2685                """<b>Manage SSL Certificates...</b>"""
2686                """<p>Opens a dialog to manage the saved SSL certificates."""
2687                """</p>"""
2688            ))
2689            self.certificatesAct.triggered.connect(
2690                self.__showCertificatesDialog)
2691            self.actions.append(self.certificatesAct)
2692
2693        self.editMessageFilterAct = E5Action(
2694            self.tr('Edit Message Filters'),
2695            UI.PixmapCache.getIcon("warning"),
2696            self.tr('Edit Message Filters...'),
2697            0, 0, self, 'manage_message_filters')
2698        self.editMessageFilterAct.setStatusTip(self.tr(
2699            'Edit the message filters used to suppress unwanted messages'))
2700        self.editMessageFilterAct.setWhatsThis(self.tr(
2701            """<b>Edit Message Filters</b>"""
2702            """<p>Opens a dialog to edit the message filters used to"""
2703            """ suppress unwanted messages been shown in an error"""
2704            """ window.</p>"""
2705        ))
2706        self.editMessageFilterAct.triggered.connect(
2707            E5ErrorMessage.editMessageFilters)
2708        self.actions.append(self.editMessageFilterAct)
2709
2710        self.clearPrivateDataAct = E5Action(
2711            self.tr('Clear private data'),
2712            UI.PixmapCache.getIcon("clearPrivateData"),
2713            self.tr('Clear private data'),
2714            0, 0,
2715            self, 'clear_private_data')
2716        self.clearPrivateDataAct.setStatusTip(self.tr(
2717            'Clear private data'))
2718        self.clearPrivateDataAct.setWhatsThis(self.tr(
2719            """<b>Clear private data</b>"""
2720            """<p>Clears the private data like the various list of"""
2721            """ recently opened files, projects or multi projects.</p>"""
2722        ))
2723        self.clearPrivateDataAct.triggered.connect(
2724            self.__clearPrivateData)
2725        self.actions.append(self.clearPrivateDataAct)
2726
2727        self.viewmanagerActivateAct = E5Action(
2728            self.tr('Activate current editor'),
2729            self.tr('Activate current editor'),
2730            QKeySequence(self.tr("Alt+Shift+E")),
2731            0, self, 'viewmanager_activate')
2732        self.viewmanagerActivateAct.triggered.connect(
2733            self.__activateViewmanager)
2734        self.actions.append(self.viewmanagerActivateAct)
2735        self.addAction(self.viewmanagerActivateAct)
2736
2737        self.nextTabAct = E5Action(
2738            self.tr('Show next'),
2739            self.tr('Show next'),
2740            QKeySequence(self.tr('Ctrl+Alt+Tab')), 0,
2741            self, 'view_next_tab')
2742        self.nextTabAct.triggered.connect(self.__showNext)
2743        self.actions.append(self.nextTabAct)
2744        self.addAction(self.nextTabAct)
2745
2746        self.prevTabAct = E5Action(
2747            self.tr('Show previous'),
2748            self.tr('Show previous'),
2749            QKeySequence(self.tr('Shift+Ctrl+Alt+Tab')), 0,
2750            self, 'view_previous_tab')
2751        self.prevTabAct.triggered.connect(self.__showPrevious)
2752        self.actions.append(self.prevTabAct)
2753        self.addAction(self.prevTabAct)
2754
2755        self.switchTabAct = E5Action(
2756            self.tr('Switch between tabs'),
2757            self.tr('Switch between tabs'),
2758            QKeySequence(self.tr('Ctrl+1')), 0,
2759            self, 'switch_tabs')
2760        self.switchTabAct.triggered.connect(self.__switchTab)
2761        self.actions.append(self.switchTabAct)
2762        self.addAction(self.switchTabAct)
2763
2764        self.pluginInfoAct = E5Action(
2765            self.tr('Plugin Infos'),
2766            UI.PixmapCache.getIcon("plugin"),
2767            self.tr('&Plugin Infos...'), 0, 0, self, 'plugin_infos')
2768        self.pluginInfoAct.setStatusTip(self.tr('Show Plugin Infos'))
2769        self.pluginInfoAct.setWhatsThis(self.tr(
2770            """<b>Plugin Infos...</b>"""
2771            """<p>This opens a dialog, that show some information about"""
2772            """ loaded plugins.</p>"""
2773        ))
2774        self.pluginInfoAct.triggered.connect(self.__showPluginInfo)
2775        self.actions.append(self.pluginInfoAct)
2776
2777        self.pluginInstallAct = E5Action(
2778            self.tr('Install Plugins'),
2779            UI.PixmapCache.getIcon("pluginInstall"),
2780            self.tr('&Install Plugins...'),
2781            0, 0, self, 'plugin_install')
2782        self.pluginInstallAct.setStatusTip(self.tr('Install Plugins'))
2783        self.pluginInstallAct.setWhatsThis(self.tr(
2784            """<b>Install Plugins...</b>"""
2785            """<p>This opens a dialog to install or update plugins.</p>"""
2786        ))
2787        self.pluginInstallAct.triggered.connect(self.__installPlugins)
2788        self.actions.append(self.pluginInstallAct)
2789
2790        self.pluginDeinstallAct = E5Action(
2791            self.tr('Uninstall Plugin'),
2792            UI.PixmapCache.getIcon("pluginUninstall"),
2793            self.tr('&Uninstall Plugin...'),
2794            0, 0, self, 'plugin_deinstall')
2795        self.pluginDeinstallAct.setStatusTip(self.tr('Uninstall Plugin'))
2796        self.pluginDeinstallAct.setWhatsThis(self.tr(
2797            """<b>Uninstall Plugin...</b>"""
2798            """<p>This opens a dialog to uninstall a plugin.</p>"""
2799        ))
2800        self.pluginDeinstallAct.triggered.connect(self.__deinstallPlugin)
2801        self.actions.append(self.pluginDeinstallAct)
2802
2803        self.pluginRepoAct = E5Action(
2804            self.tr('Plugin Repository'),
2805            UI.PixmapCache.getIcon("pluginRepository"),
2806            self.tr('Plugin &Repository...'),
2807            0, 0, self, 'plugin_repository')
2808        self.pluginRepoAct.setStatusTip(self.tr(
2809            'Show Plugins available for download'))
2810        self.pluginRepoAct.setWhatsThis(self.tr(
2811            """<b>Plugin Repository...</b>"""
2812            """<p>This opens a dialog, that shows a list of plugins """
2813            """available on the Internet.</p>"""
2814        ))
2815        self.pluginRepoAct.triggered.connect(self.showPluginsAvailable)
2816        self.actions.append(self.pluginRepoAct)
2817
2818        self.virtualenvManagerAct = E5Action(
2819            self.tr('Virtualenv Manager'),
2820            UI.PixmapCache.getIcon("virtualenv"),
2821            self.tr('&Virtualenv Manager...'),
2822            0, 0, self,
2823            'virtualenv_manager')
2824        self.virtualenvManagerAct.setStatusTip(self.tr(
2825            'Virtualenv Manager'))
2826        self.virtualenvManagerAct.setWhatsThis(self.tr(
2827            """<b>Virtualenv Manager</b>"""
2828            """<p>This opens a dialog to manage the defined Python virtual"""
2829            """ environments.</p>"""
2830        ))
2831        self.virtualenvManagerAct.triggered.connect(
2832            self.virtualenvManager.showVirtualenvManagerDialog)
2833        self.actions.append(self.virtualenvManagerAct)
2834
2835        self.virtualenvConfigAct = E5Action(
2836            self.tr('Virtualenv Configurator'),
2837            UI.PixmapCache.getIcon("virtualenvConfig"),
2838            self.tr('Virtualenv &Configurator...'),
2839            0, 0, self,
2840            'virtualenv_configurator')
2841        self.virtualenvConfigAct.setStatusTip(self.tr(
2842            'Virtualenv Configurator'))
2843        self.virtualenvConfigAct.setWhatsThis(self.tr(
2844            """<b>Virtualenv Configurator</b>"""
2845            """<p>This opens a dialog for entering all the parameters"""
2846            """ needed to create a Python virtual environment using"""
2847            """ virtualenv or pyvenv.</p>"""
2848        ))
2849        self.virtualenvConfigAct.triggered.connect(
2850            self.virtualenvManager.createVirtualEnv)
2851        self.actions.append(self.virtualenvConfigAct)
2852
2853        # initialize viewmanager actions
2854        self.viewmanager.initActions()
2855
2856        # initialize debugger actions
2857        self.debuggerUI.initActions()
2858
2859        # initialize project actions
2860        self.project.initActions()
2861
2862        # initialize multi project actions
2863        self.multiProject.initActions()
2864
2865    def __initQtDocActions(self):
2866        """
2867        Private slot to initialize the action to show the Qt documentation.
2868        """
2869        self.qt5DocAct = E5Action(
2870            self.tr('Qt5 Documentation'),
2871            self.tr('Qt5 Documentation'),
2872            0, 0, self, 'qt5_documentation')
2873        self.qt5DocAct.setStatusTip(self.tr('Open Qt5 Documentation'))
2874        self.qt5DocAct.setWhatsThis(self.tr(
2875            """<b>Qt5 Documentation</b>"""
2876            """<p>Display the Qt5 Documentation. Dependent upon your"""
2877            """ settings, this will either show the help in Eric's internal"""
2878            """ help viewer/web browser, or execute a web browser or Qt"""
2879            """ Assistant. </p>"""
2880        ))
2881        self.qt5DocAct.triggered.connect(lambda: self.__showQtDoc(5))
2882        self.actions.append(self.qt5DocAct)
2883
2884        self.qt6DocAct = E5Action(
2885            self.tr('Qt6 Documentation'),
2886            self.tr('Qt6 Documentation'),
2887            0, 0, self, 'qt6_documentation')
2888        self.qt6DocAct.setStatusTip(self.tr('Open Qt6 Documentation'))
2889        self.qt6DocAct.setWhatsThis(self.tr(
2890            """<b>Qt6 Documentation</b>"""
2891            """<p>Display the Qt6 Documentation. Dependent upon your"""
2892            """ settings, this will either show the help in Eric's internal"""
2893            """ help viewer/web browser, or execute a web browser or Qt"""
2894            """ Assistant. </p>"""
2895        ))
2896        self.qt6DocAct.triggered.connect(lambda: self.__showQtDoc(6))
2897        self.actions.append(self.qt6DocAct)
2898
2899        self.pyqt5DocAct = E5Action(
2900            self.tr('PyQt5 Documentation'),
2901            self.tr('PyQt5 Documentation'),
2902            0, 0, self, 'pyqt5_documentation')
2903        self.pyqt5DocAct.setStatusTip(self.tr(
2904            'Open PyQt5 Documentation'))
2905        self.pyqt5DocAct.setWhatsThis(self.tr(
2906            """<b>PyQt5 Documentation</b>"""
2907            """<p>Display the PyQt5 Documentation. Dependent upon your"""
2908            """ settings, this will either show the help in Eric's"""
2909            """ internal help viewer/web browser, or execute a web"""
2910            """ browser or Qt Assistant. </p>"""
2911        ))
2912        self.pyqt5DocAct.triggered.connect(
2913            lambda: self.__showPyQtDoc(variant=5))
2914        self.actions.append(self.pyqt5DocAct)
2915
2916        self.pyqt6DocAct = E5Action(
2917            self.tr('PyQt6 Documentation'),
2918            self.tr('PyQt6 Documentation'),
2919            0, 0, self, 'pyqt6_documentation')
2920        self.pyqt6DocAct.setStatusTip(self.tr(
2921            'Open PyQt6 Documentation'))
2922        self.pyqt6DocAct.setWhatsThis(self.tr(
2923            """<b>PyQt6 Documentation</b>"""
2924            """<p>Display the PyQt6 Documentation. Dependent upon your"""
2925            """ settings, this will either show the help in Eric's"""
2926            """ internal help viewer/web browser, or execute a web"""
2927            """ browser or Qt Assistant. </p>"""
2928        ))
2929        self.pyqt6DocAct.triggered.connect(
2930            lambda: self.__showPyQtDoc(variant=6))
2931        self.actions.append(self.pyqt6DocAct)
2932
2933    def __initPythonDocActions(self):
2934        """
2935        Private slot to initialize the actions to show the Python
2936        documentation.
2937        """
2938        self.pythonDocAct = E5Action(
2939            self.tr('Python 3 Documentation'),
2940            self.tr('Python 3 Documentation'),
2941            0, 0, self, 'python3_documentation')
2942        self.pythonDocAct.setStatusTip(self.tr(
2943            'Open Python 3 Documentation'))
2944        self.pythonDocAct.setWhatsThis(self.tr(
2945            """<b>Python 3 Documentation</b>"""
2946            """<p>Display the Python 3 documentation. If no documentation"""
2947            """ directory is configured, the location of the Python 3"""
2948            """ documentation is assumed to be the doc directory underneath"""
2949            """ the location of the Python 3 executable on Windows and"""
2950            """ <i>/usr/share/doc/packages/python/html</i> on Unix. Set"""
2951            """ PYTHON3DOCDIR in your environment to override this.</p>"""
2952        ))
2953        self.pythonDocAct.triggered.connect(self.__showPythonDoc)
2954        self.actions.append(self.pythonDocAct)
2955
2956    def __initEricDocAction(self):
2957        """
2958        Private slot to initialize the action to show the eric documentation.
2959        """
2960        self.ericDocAct = E5Action(
2961            self.tr("Eric API Documentation"),
2962            self.tr('Eric API Documentation'),
2963            0, 0, self, 'eric_documentation')
2964        self.ericDocAct.setStatusTip(self.tr(
2965            "Open Eric API Documentation"))
2966        self.ericDocAct.setWhatsThis(self.tr(
2967            """<b>Eric API Documentation</b>"""
2968            """<p>Display the Eric API documentation. The location for the"""
2969            """ documentation is the Documentation/Source subdirectory of"""
2970            """ the eric installation directory.</p>"""
2971        ))
2972        self.ericDocAct.triggered.connect(self.__showEricDoc)
2973        self.actions.append(self.ericDocAct)
2974
2975    def __initPySideDocActions(self):
2976        """
2977        Private slot to initialize the actions to show the PySide
2978        documentation.
2979        """
2980        if Utilities.checkPyside(variant=2):
2981            self.pyside2DocAct = E5Action(
2982                self.tr('PySide2 Documentation'),
2983                self.tr('PySide2 Documentation'),
2984                0, 0, self, 'pyside2_documentation')
2985            self.pyside2DocAct.setStatusTip(self.tr(
2986                'Open PySide2 Documentation'))
2987            self.pyside2DocAct.setWhatsThis(self.tr(
2988                """<b>PySide2 Documentation</b>"""
2989                """<p>Display the PySide2 Documentation. Dependent upon your"""
2990                """ settings, this will either show the help in Eric's"""
2991                """ internal help viewer/web browser, or execute a web"""
2992                """ browser or Qt Assistant. </p>"""
2993            ))
2994            self.pyside2DocAct.triggered.connect(
2995                lambda: self.__showPySideDoc(variant=2))
2996            self.actions.append(self.pyside2DocAct)
2997        else:
2998            self.pyside2DocAct = None
2999
3000        if Utilities.checkPyside(variant=6):
3001            self.pyside6DocAct = E5Action(
3002                self.tr('PySide6 Documentation'),
3003                self.tr('PySide6 Documentation'),
3004                0, 0, self, 'pyside6_documentation')
3005            self.pyside6DocAct.setStatusTip(self.tr(
3006                'Open PySide6 Documentation'))
3007            self.pyside6DocAct.setWhatsThis(self.tr(
3008                """<b>PySide6 Documentation</b>"""
3009                """<p>Display the PySide6 Documentation. Dependent upon your"""
3010                """ settings, this will either show the help in Eric's"""
3011                """ internal help viewer/web browser, or execute a web"""
3012                """ browser or Qt Assistant. </p>"""
3013            ))
3014            self.pyside6DocAct.triggered.connect(
3015                lambda: self.__showPySideDoc(variant=6))
3016            self.actions.append(self.pyside6DocAct)
3017        else:
3018            self.pyside6DocAct = None
3019
3020    def __initMenus(self):
3021        """
3022        Private slot to create the menus.
3023        """
3024        self.__menus = {}
3025        mb = self.menuBar()
3026        if (
3027            Utilities.isLinuxPlatform() and
3028            not Preferences.getUI("UseNativeMenuBar")
3029        ):
3030            mb.setNativeMenuBar(False)
3031
3032        ##############################################################
3033        ## File menu
3034        ##############################################################
3035
3036        self.__menus["file"] = self.viewmanager.initFileMenu()
3037        mb.addMenu(self.__menus["file"])
3038        self.__menus["file"].addSeparator()
3039        self.__menus["file"].addAction(self.saveSessionAct)
3040        self.__menus["file"].addAction(self.loadSessionAct)
3041        self.__menus["file"].addSeparator()
3042        self.__menus["file"].addAction(self.restartAct)
3043        self.__menus["file"].addAction(self.exitAct)
3044        act = self.__menus["file"].actions()[0]
3045        sep = self.__menus["file"].insertSeparator(act)
3046        self.__menus["file"].insertAction(sep, self.newWindowAct)
3047        self.__menus["file"].aboutToShow.connect(self.__showFileMenu)
3048
3049        ##############################################################
3050        ## Edit menu
3051        ##############################################################
3052
3053        self.__menus["edit"] = self.viewmanager.initEditMenu()
3054        mb.addMenu(self.__menus["edit"])
3055
3056        ##############################################################
3057        ## Search menu
3058        ##############################################################
3059
3060        self.__menus["search"] = self.viewmanager.initSearchMenu()
3061        mb.addMenu(self.__menus["search"])
3062
3063        ##############################################################
3064        ## View menu
3065        ##############################################################
3066
3067        self.__menus["view"] = self.viewmanager.initViewMenu()
3068        mb.addMenu(self.__menus["view"])
3069
3070        ##############################################################
3071        ## Bookmarks menu
3072        ##############################################################
3073
3074        self.__menus["bookmarks"] = self.viewmanager.initBookmarkMenu()
3075        mb.addMenu(self.__menus["bookmarks"])
3076        self.__menus["bookmarks"].setTearOffEnabled(True)
3077
3078        ##############################################################
3079        ## Multiproject menu
3080        ##############################################################
3081
3082        self.__menus["multiproject"] = self.multiProject.initMenu()
3083        mb.addMenu(self.__menus["multiproject"])
3084
3085        ##############################################################
3086        ## Project menu
3087        ##############################################################
3088
3089        self.__menus["project"], self.__menus["project_tools"] = (
3090            self.project.initMenus()
3091        )
3092        mb.addMenu(self.__menus["project"])
3093        mb.addMenu(self.__menus["project_tools"])
3094
3095        ##############################################################
3096        ## Start and Debug menus
3097        ##############################################################
3098
3099        self.__menus["start"], self.__menus["debug"] = (
3100            self.debuggerUI.initMenus()
3101        )
3102        mb.addMenu(self.__menus["start"])
3103        mb.addMenu(self.__menus["debug"])
3104
3105        ##############################################################
3106        ## Extras menu
3107        ##############################################################
3108
3109        self.__menus["extras"] = QMenu(self.tr('E&xtras'), self)
3110        self.__menus["extras"].setTearOffEnabled(True)
3111        self.__menus["extras"].aboutToShow.connect(self.__showExtrasMenu)
3112        mb.addMenu(self.__menus["extras"])
3113        self.viewmanager.addToExtrasMenu(self.__menus["extras"])
3114
3115        ##############################################################
3116        ## Extras/Wizards menu
3117        ##############################################################
3118
3119        self.__menus["wizards"] = QMenu(self.tr('Wi&zards'), self)
3120        self.__menus["wizards"].setTearOffEnabled(True)
3121        self.__menus["wizards"].aboutToShow.connect(self.__showWizardsMenu)
3122        self.wizardsMenuAct = self.__menus["extras"].addMenu(
3123            self.__menus["wizards"])
3124        self.wizardsMenuAct.setEnabled(False)
3125
3126        ##############################################################
3127        ## Extras/Macros menu
3128        ##############################################################
3129
3130        self.__menus["macros"] = self.viewmanager.initMacroMenu()
3131        self.__menus["extras"].addMenu(self.__menus["macros"])
3132        self.__menus["extras"].addSeparator()
3133
3134        ##############################################################
3135        ## Extras/VirtualEnv Manager menu entries
3136        ##############################################################
3137
3138        self.__menus["extras"].addAction(self.virtualenvManagerAct)
3139        self.__menus["extras"].addAction(self.virtualenvConfigAct)
3140        self.__menus["extras"].addSeparator()
3141
3142        ##############################################################
3143        ## Extras/Plugins menu
3144        ##############################################################
3145
3146        pluginsMenu = QMenu(self.tr('P&lugins'), self)
3147        pluginsMenu.setIcon(UI.PixmapCache.getIcon("plugin"))
3148        pluginsMenu.setTearOffEnabled(True)
3149        pluginsMenu.addAction(self.pluginInfoAct)
3150        pluginsMenu.addAction(self.pluginInstallAct)
3151        pluginsMenu.addAction(self.pluginDeinstallAct)
3152        pluginsMenu.addSeparator()
3153        pluginsMenu.addAction(self.pluginRepoAct)
3154        pluginsMenu.addSeparator()
3155        pluginsMenu.addAction(
3156            self.tr("Configure..."), self.__pluginsConfigure)
3157
3158        self.__menus["extras"].addMenu(pluginsMenu)
3159        self.__menus["extras"].addSeparator()
3160
3161        ##############################################################
3162        ## Extras/Unittest menu
3163        ##############################################################
3164
3165        self.__menus["unittest"] = QMenu(self.tr('&Unittest'), self)
3166        self.__menus["unittest"].setTearOffEnabled(True)
3167        self.__menus["unittest"].addAction(self.utDialogAct)
3168        self.__menus["unittest"].addSeparator()
3169        self.__menus["unittest"].addAction(self.utRestartAct)
3170        self.__menus["unittest"].addAction(self.utRerunFailedAct)
3171        self.__menus["unittest"].addSeparator()
3172        self.__menus["unittest"].addAction(self.utScriptAct)
3173        self.__menus["unittest"].addAction(self.utProjectAct)
3174
3175        self.__menus["extras"].addMenu(self.__menus["unittest"])
3176        self.__menus["extras"].addSeparator()
3177
3178        ##############################################################
3179        ## Extras/Builtin,Plugin,User tools menus
3180        ##############################################################
3181
3182        self.toolGroupsMenu = QMenu(self.tr("Select Tool Group"), self)
3183        self.toolGroupsMenu.aboutToShow.connect(self.__showToolGroupsMenu)
3184        self.toolGroupsMenu.triggered.connect(self.__toolGroupSelected)
3185        self.toolGroupsMenuTriggered = False
3186        self.__initToolsMenus(self.__menus["extras"])
3187        self.__menus["extras"].addSeparator()
3188
3189        ##############################################################
3190        ## Settings menu
3191        ##############################################################
3192
3193        self.__menus["settings"] = QMenu(self.tr('Se&ttings'), self)
3194        mb.addMenu(self.__menus["settings"])
3195        self.__menus["settings"].setTearOffEnabled(True)
3196
3197        self.__menus["settings"].addAction(self.prefAct)
3198        self.__menus["settings"].addAction(self.prefExportAct)
3199        self.__menus["settings"].addAction(self.prefImportAct)
3200        self.__menus["settings"].addSeparator()
3201        self.__menus["settings"].addAction(self.reloadAPIsAct)
3202        self.__menus["settings"].addSeparator()
3203        self.__menus["settings"].addAction(self.configViewProfilesAct)
3204        self.__menus["settings"].addAction(self.configToolBarsAct)
3205        self.__menus["settings"].addSeparator()
3206        self.__menus["settings"].addAction(self.shortcutsAct)
3207        self.__menus["settings"].addAction(self.exportShortcutsAct)
3208        self.__menus["settings"].addAction(self.importShortcutsAct)
3209        self.__menus["settings"].addSeparator()
3210        self.__menus["settings"].addAction(self.showExternalToolsAct)
3211        if SSL_AVAILABLE:
3212            self.__menus["settings"].addSeparator()
3213            self.__menus["settings"].addAction(self.certificatesAct)
3214        self.__menus["settings"].addSeparator()
3215        self.__menus["settings"].addAction(self.editMessageFilterAct)
3216        self.__menus["settings"].addSeparator()
3217        self.__menus["settings"].addAction(self.clearPrivateDataAct)
3218
3219        ##############################################################
3220        ## Window menu
3221        ##############################################################
3222
3223        self.__menus["window"] = QMenu(self.tr('&Window'), self)
3224        mb.addMenu(self.__menus["window"])
3225        self.__menus["window"].setTearOffEnabled(True)
3226        self.__menus["window"].aboutToShow.connect(self.__showWindowMenu)
3227
3228        ##############################################################
3229        ## Window/Windows menu
3230        ##############################################################
3231
3232        self.__menus["subwindow"] = QMenu(self.tr("&Windows"),
3233                                          self.__menus["window"])
3234        self.__menus["subwindow"].setTearOffEnabled(True)
3235        # central park
3236        self.__menus["subwindow"].addSection(self.tr("Central Park"))
3237        self.__menus["subwindow"].addAction(self.viewmanagerActivateAct)
3238        # left side
3239        self.__menus["subwindow"].addSection(self.tr("Left Side"))
3240        if self.__shellPosition == "left":
3241            self.__menus["subwindow"].addAction(self.shellActivateAct)
3242        self.__menus["subwindow"].addAction(self.pbActivateAct)
3243        self.__menus["subwindow"].addAction(self.mpbActivateAct)
3244        if self.templateViewer is not None:
3245            self.__menus["subwindow"].addAction(self.templateViewerActivateAct)
3246        if self.browser is not None:
3247            self.__menus["subwindow"].addAction(self.browserActivateAct)
3248        if self.symbolsViewer is not None:
3249            self.__menus["subwindow"].addAction(self.symbolsViewerActivateAct)
3250        # bottom side
3251        self.__menus["subwindow"].addSection(self.tr("Bottom Side"))
3252        if self.__shellPosition == "bottom":
3253            self.__menus["subwindow"].addAction(self.shellActivateAct)
3254        self.__menus["subwindow"].addAction(self.taskViewerActivateAct)
3255        self.__menus["subwindow"].addAction(self.logViewerActivateAct)
3256        if self.numbersViewer is not None:
3257            self.__menus["subwindow"].addAction(self.numbersViewerActivateAct)
3258        self.__menus["subwindow"].addSection(self.tr("Right Side"))
3259        # right side
3260        if self.__shellPosition == "right":
3261            self.__menus["subwindow"].addAction(self.shellActivateAct)
3262        if self.codeDocumentationViewer is not None:
3263            self.__menus["subwindow"].addAction(
3264                self.codeDocumentationViewerActivateAct)
3265        self.__menus["subwindow"].addAction(self.debugViewerActivateAct)
3266        if self.pipWidget is not None:
3267            self.__menus["subwindow"].addAction(self.pipWidgetActivateAct)
3268        if self.condaWidget is not None:
3269            self.__menus["subwindow"].addAction(self.condaWidgetActivateAct)
3270        if self.cooperation is not None:
3271            self.__menus["subwindow"].addAction(
3272                self.cooperationViewerActivateAct)
3273        if self.irc is not None:
3274            self.__menus["subwindow"].addAction(self.ircActivateAct)
3275        if self.microPythonWidget is not None:
3276            self.__menus["subwindow"].addAction(
3277                self.microPythonWidgetActivateAct)
3278        self.__menus["subwindow"].addSection(self.tr("Plug-ins"))
3279
3280        ##############################################################
3281        ## Window/Toolbars menu
3282        ##############################################################
3283
3284        self.__menus["toolbars"] = QMenu(
3285            self.tr("&Toolbars"), self.__menus["window"])
3286        self.__menus["toolbars"].setTearOffEnabled(True)
3287        self.__menus["toolbars"].aboutToShow.connect(self.__showToolbarsMenu)
3288        self.__menus["toolbars"].triggered.connect(self.__TBMenuTriggered)
3289
3290        self.__showWindowMenu()  # to initialize these actions
3291
3292        mb.addSeparator()
3293
3294        ##############################################################
3295        ## Help menu
3296        ##############################################################
3297
3298        self.__menus["help"] = QMenu(self.tr('&Help'), self)
3299        mb.addMenu(self.__menus["help"])
3300        self.__menus["help"].setTearOffEnabled(True)
3301        if self.helpviewerAct:
3302            self.__menus["help"].addAction(self.helpviewerAct)
3303            self.__menus["help"].addSeparator()
3304        self.__menus["help"].addAction(self.ericDocAct)
3305        self.__menus["help"].addAction(self.pythonDocAct)
3306        self.__menus["help"].addAction(self.qt5DocAct)
3307        self.__menus["help"].addAction(self.qt6DocAct)
3308        self.__menus["help"].addAction(self.pyqt5DocAct)
3309        self.__menus["help"].addAction(self.pyqt6DocAct)
3310        if self.pyside2DocAct is not None:
3311            self.__menus["help"].addAction(self.pyside2DocAct)
3312        if self.pyside6DocAct is not None:
3313            self.__menus["help"].addAction(self.pyside6DocAct)
3314        self.__menus["help"].addSeparator()
3315        self.__menus["help"].addAction(self.versionAct)
3316        self.__menus["help"].addSeparator()
3317        self.__menus["help"].addAction(self.checkUpdateAct)
3318        self.__menus["help"].addAction(self.showVersionsAct)
3319        self.__menus["help"].addSeparator()
3320        self.__menus["help"].addAction(self.showInstallInfoAct)
3321        self.__menus["help"].addSeparator()
3322        self.__menus["help"].addAction(self.showErrorLogAct)
3323        self.__menus["help"].addAction(self.reportBugAct)
3324        self.__menus["help"].addAction(self.requestFeatureAct)
3325        self.__menus["help"].addSeparator()
3326        self.__menus["help"].addAction(self.whatsThisAct)
3327        self.__menus["help"].aboutToShow.connect(self.__showHelpMenu)
3328
3329    def getToolBarIconSize(self):
3330        """
3331        Public method to get the toolbar icon size.
3332
3333        @return toolbar icon size (QSize)
3334        """
3335        return Config.ToolBarIconSize
3336
3337    def __initToolbars(self):
3338        """
3339        Private slot to create the toolbars.
3340        """
3341        filetb = self.viewmanager.initFileToolbar(self.toolbarManager)
3342        edittb = self.viewmanager.initEditToolbar(self.toolbarManager)
3343        searchtb = self.viewmanager.initSearchToolbar(self.toolbarManager)
3344        viewtb = self.viewmanager.initViewToolbar(self.toolbarManager)
3345        starttb, debugtb = self.debuggerUI.initToolbars(self.toolbarManager)
3346        multiprojecttb = self.multiProject.initToolbar(self.toolbarManager)
3347        projecttb, vcstb = self.project.initToolbars(self.toolbarManager)
3348        toolstb = QToolBar(self.tr("Tools"), self)
3349        unittesttb = QToolBar(self.tr("Unittest"), self)
3350        bookmarktb = self.viewmanager.initBookmarkToolbar(self.toolbarManager)
3351        spellingtb = self.viewmanager.initSpellingToolbar(self.toolbarManager)
3352        settingstb = QToolBar(self.tr("Settings"), self)
3353        helptb = QToolBar(self.tr("Help"), self)
3354        profilestb = QToolBar(self.tr("Profiles"), self)
3355        pluginstb = QToolBar(self.tr("Plugins"), self)
3356
3357        toolstb.setIconSize(Config.ToolBarIconSize)
3358        unittesttb.setIconSize(Config.ToolBarIconSize)
3359        settingstb.setIconSize(Config.ToolBarIconSize)
3360        helptb.setIconSize(Config.ToolBarIconSize)
3361        profilestb.setIconSize(Config.ToolBarIconSize)
3362        pluginstb.setIconSize(Config.ToolBarIconSize)
3363
3364        toolstb.setObjectName("ToolsToolbar")
3365        unittesttb.setObjectName("UnittestToolbar")
3366        settingstb.setObjectName("SettingsToolbar")
3367        helptb.setObjectName("HelpToolbar")
3368        profilestb.setObjectName("ProfilesToolbar")
3369        pluginstb.setObjectName("PluginsToolbar")
3370
3371        toolstb.setToolTip(self.tr("Tools"))
3372        unittesttb.setToolTip(self.tr("Unittest"))
3373        settingstb.setToolTip(self.tr("Settings"))
3374        helptb.setToolTip(self.tr("Help"))
3375        profilestb.setToolTip(self.tr("Profiles"))
3376        pluginstb.setToolTip(self.tr("Plugins"))
3377
3378        filetb.addSeparator()
3379        filetb.addAction(self.restartAct)
3380        filetb.addAction(self.exitAct)
3381        act = filetb.actions()[0]
3382        sep = filetb.insertSeparator(act)
3383        filetb.insertAction(sep, self.newWindowAct)
3384        self.toolbarManager.addToolBar(filetb, filetb.windowTitle())
3385
3386        # setup the unittest toolbar
3387        unittesttb.addAction(self.utDialogAct)
3388        unittesttb.addSeparator()
3389        unittesttb.addAction(self.utRestartAct)
3390        unittesttb.addAction(self.utRerunFailedAct)
3391        unittesttb.addSeparator()
3392        unittesttb.addAction(self.utScriptAct)
3393        unittesttb.addAction(self.utProjectAct)
3394        self.toolbarManager.addToolBar(unittesttb, unittesttb.windowTitle())
3395
3396        # setup the tools toolbar
3397        if self.designer4Act is not None:
3398            toolstb.addAction(self.designer4Act)
3399        if self.linguist4Act is not None:
3400            toolstb.addAction(self.linguist4Act)
3401        toolstb.addAction(self.uipreviewerAct)
3402        toolstb.addAction(self.trpreviewerAct)
3403        toolstb.addSeparator()
3404        toolstb.addAction(self.diffAct)
3405        toolstb.addAction(self.compareAct)
3406        toolstb.addSeparator()
3407        toolstb.addAction(self.sqlBrowserAct)
3408        toolstb.addSeparator()
3409        toolstb.addAction(self.miniEditorAct)
3410        toolstb.addAction(self.hexEditorAct)
3411        toolstb.addAction(self.iconEditorAct)
3412        toolstb.addAction(self.snapshotAct)
3413        toolstb.addSeparator()
3414        toolstb.addAction(self.virtualenvManagerAct)
3415        toolstb.addAction(self.virtualenvConfigAct)
3416        if self.webBrowserAct:
3417            toolstb.addSeparator()
3418            toolstb.addAction(self.webBrowserAct)
3419        self.toolbarManager.addToolBar(toolstb, toolstb.windowTitle())
3420
3421        # setup the settings toolbar
3422        settingstb.addAction(self.prefAct)
3423        settingstb.addAction(self.configViewProfilesAct)
3424        settingstb.addAction(self.configToolBarsAct)
3425        settingstb.addAction(self.shortcutsAct)
3426        settingstb.addAction(self.showExternalToolsAct)
3427        self.toolbarManager.addToolBar(settingstb, settingstb.windowTitle())
3428        self.toolbarManager.addActions([
3429            self.exportShortcutsAct,
3430            self.importShortcutsAct,
3431            self.prefExportAct,
3432            self.prefImportAct,
3433            self.showExternalToolsAct,
3434            self.editMessageFilterAct,
3435            self.clearPrivateDataAct,
3436        ], settingstb.windowTitle())
3437        if SSL_AVAILABLE:
3438            self.toolbarManager.addAction(
3439                self.certificatesAct, settingstb.windowTitle())
3440
3441        # setup the help toolbar
3442        helptb.addAction(self.whatsThisAct)
3443        self.toolbarManager.addToolBar(helptb, helptb.windowTitle())
3444        if self.helpviewerAct:
3445            self.toolbarManager.addAction(self.helpviewerAct,
3446                                          helptb.windowTitle())
3447
3448        # setup the view profiles toolbar
3449        profilestb.addActions(self.viewProfileActGrp.actions())
3450        self.toolbarManager.addToolBar(profilestb, profilestb.windowTitle())
3451
3452        # setup the plugins toolbar
3453        pluginstb.addAction(self.pluginInfoAct)
3454        pluginstb.addAction(self.pluginInstallAct)
3455        pluginstb.addAction(self.pluginDeinstallAct)
3456        pluginstb.addSeparator()
3457        pluginstb.addAction(self.pluginRepoAct)
3458        self.toolbarManager.addToolBar(pluginstb, pluginstb.windowTitle())
3459
3460        # add the various toolbars
3461        self.addToolBar(filetb)
3462        self.addToolBar(edittb)
3463        self.addToolBar(searchtb)
3464        self.addToolBar(viewtb)
3465        self.addToolBar(starttb)
3466        self.addToolBar(debugtb)
3467        self.addToolBar(multiprojecttb)
3468        self.addToolBar(projecttb)
3469        self.addToolBar(vcstb)
3470        self.addToolBar(Qt.ToolBarArea.RightToolBarArea, settingstb)
3471        self.addToolBar(Qt.ToolBarArea.RightToolBarArea, toolstb)
3472        self.addToolBar(helptb)
3473        self.addToolBar(bookmarktb)
3474        self.addToolBar(spellingtb)
3475        self.addToolBar(unittesttb)
3476        self.addToolBar(profilestb)
3477        self.addToolBar(pluginstb)
3478
3479        # hide toolbars not wanted in the initial layout
3480        searchtb.hide()
3481        viewtb.hide()
3482        debugtb.hide()
3483        multiprojecttb.hide()
3484        helptb.hide()
3485        spellingtb.hide()
3486        unittesttb.hide()
3487        pluginstb.hide()
3488
3489        # just add new toolbars to the end of the list
3490        self.__toolbars = {}
3491        self.__toolbars["file"] = [filetb.windowTitle(), filetb, ""]
3492        self.__toolbars["edit"] = [edittb.windowTitle(), edittb, ""]
3493        self.__toolbars["search"] = [searchtb.windowTitle(), searchtb, ""]
3494        self.__toolbars["view"] = [viewtb.windowTitle(), viewtb, ""]
3495        self.__toolbars["start"] = [starttb.windowTitle(), starttb, ""]
3496        self.__toolbars["debug"] = [debugtb.windowTitle(), debugtb, ""]
3497        self.__toolbars["project"] = [projecttb.windowTitle(), projecttb, ""]
3498        self.__toolbars["tools"] = [toolstb.windowTitle(), toolstb, ""]
3499        self.__toolbars["help"] = [helptb.windowTitle(), helptb, ""]
3500        self.__toolbars["settings"] = [settingstb.windowTitle(), settingstb,
3501                                       ""]
3502        self.__toolbars["bookmarks"] = [bookmarktb.windowTitle(), bookmarktb,
3503                                        ""]
3504        self.__toolbars["unittest"] = [unittesttb.windowTitle(), unittesttb,
3505                                       ""]
3506        self.__toolbars["view_profiles"] = [profilestb.windowTitle(),
3507                                            profilestb, ""]
3508        self.__toolbars["plugins"] = [pluginstb.windowTitle(), pluginstb, ""]
3509        self.__toolbars["multiproject"] = [multiprojecttb.windowTitle(),
3510                                           multiprojecttb, ""]
3511        self.__toolbars["spelling"] = [spellingtb.windowTitle(), spellingtb,
3512                                       ""]
3513        self.__toolbars["vcs"] = [vcstb.windowTitle(), vcstb, "vcs"]
3514
3515    def __initDebugToolbarsLayout(self):
3516        """
3517        Private slot to initialize the toolbars layout for the debug profile.
3518        """
3519        # Step 1: set the edit profile to be sure
3520        self.__setEditProfile()
3521
3522        # Step 2: switch to debug profile and do the layout
3523        initSize = self.size()
3524        self.setDebugProfile()
3525        self.__toolbars["project"][1].hide()
3526        self.__toolbars["debug"][1].show()
3527        self.resize(initSize)
3528
3529        # Step 3: switch back to edit profile
3530        self.__setEditProfile()
3531
3532    def __initStatusbar(self):
3533        """
3534        Private slot to set up the status bar.
3535        """
3536        self.__statusBar = self.statusBar()
3537        self.__statusBar.setSizeGripEnabled(True)
3538
3539        self.sbLanguage = E5ClickableLabel(self.__statusBar)
3540        self.__statusBar.addPermanentWidget(self.sbLanguage)
3541        self.sbLanguage.setWhatsThis(self.tr(
3542            """<p>This part of the status bar displays the"""
3543            """ current editors language.</p>"""
3544        ))
3545
3546        self.sbEncoding = E5ClickableLabel(self.__statusBar)
3547        self.__statusBar.addPermanentWidget(self.sbEncoding)
3548        self.sbEncoding.setWhatsThis(self.tr(
3549            """<p>This part of the status bar displays the"""
3550            """ current editors encoding.</p>"""
3551        ))
3552
3553        self.sbEol = E5ClickableLabel(self.__statusBar)
3554        self.__statusBar.addPermanentWidget(self.sbEol)
3555        self.sbEol.setWhatsThis(self.tr(
3556            """<p>This part of the status bar displays the"""
3557            """ current editors eol setting.</p>"""
3558        ))
3559
3560        self.sbWritable = QLabel(self.__statusBar)
3561        self.__statusBar.addPermanentWidget(self.sbWritable)
3562        self.sbWritable.setWhatsThis(self.tr(
3563            """<p>This part of the status bar displays an indication of the"""
3564            """ current editors files writability.</p>"""
3565        ))
3566
3567        self.sbLine = QLabel(self.__statusBar)
3568        self.__statusBar.addPermanentWidget(self.sbLine)
3569        self.sbLine.setWhatsThis(self.tr(
3570            """<p>This part of the status bar displays the line number of"""
3571            """ the current editor.</p>"""
3572        ))
3573
3574        self.sbPos = QLabel(self.__statusBar)
3575        self.__statusBar.addPermanentWidget(self.sbPos)
3576        self.sbPos.setWhatsThis(self.tr(
3577            """<p>This part of the status bar displays the cursor position"""
3578            """ of the current editor.</p>"""
3579        ))
3580
3581        self.sbZoom = E5ZoomWidget(
3582            UI.PixmapCache.getPixmap("zoomOut"),
3583            UI.PixmapCache.getPixmap("zoomIn"),
3584            UI.PixmapCache.getPixmap("zoomReset"),
3585            self.__statusBar)
3586        self.__statusBar.addPermanentWidget(self.sbZoom)
3587        self.sbZoom.setWhatsThis(self.tr(
3588            """<p>This part of the status bar allows zooming the current"""
3589            """ editor or shell.</p>"""
3590        ))
3591
3592        self.viewmanager.setSbInfo(
3593            self.sbLine, self.sbPos, self.sbWritable, self.sbEncoding,
3594            self.sbLanguage, self.sbEol, self.sbZoom)
3595
3596        from VCS.StatusMonitorLed import StatusMonitorLedWidget
3597        self.sbVcsMonitorLed = StatusMonitorLedWidget(
3598            self.project, self.__statusBar)
3599        self.__statusBar.addPermanentWidget(self.sbVcsMonitorLed)
3600
3601    def __initExternalToolsActions(self):
3602        """
3603        Private slot to create actions for the configured external tools.
3604        """
3605        self.toolGroupActions = {}
3606        for toolGroup in self.toolGroups:
3607            category = self.tr("External Tools/{0}").format(toolGroup[0])
3608            for tool in toolGroup[1]:
3609                if tool['menutext'] != '--':
3610                    act = QAction(UI.PixmapCache.getIcon(tool['icon']),
3611                                  tool['menutext'], self)
3612                    act.setObjectName("{0}@@{1}".format(toolGroup[0],
3613                                      tool['menutext']))
3614                    act.triggered.connect(
3615                        functools.partial(self.__toolActionTriggered, act))
3616                    self.toolGroupActions[act.objectName()] = act
3617
3618                    self.toolbarManager.addAction(act, category)
3619
3620    def __updateExternalToolsActions(self):
3621        """
3622        Private method to update the external tools actions for the current
3623        tool group.
3624        """
3625        toolGroup = self.toolGroups[self.currentToolGroup]
3626        groupkey = "{0}@@".format(toolGroup[0])
3627        groupActionKeys = []
3628        # step 1: get actions for this group
3629        for key in self.toolGroupActions:
3630            if key.startswith(groupkey):
3631                groupActionKeys.append(key)
3632
3633        # step 2: build keys for all actions i.a.w. current configuration
3634        ckeys = []
3635        for tool in toolGroup[1]:
3636            if tool['menutext'] != '--':
3637                ckeys.append("{0}@@{1}".format(toolGroup[0], tool['menutext']))
3638
3639        # step 3: remove all actions not configured any more
3640        for key in groupActionKeys:
3641            if key not in ckeys:
3642                self.toolbarManager.removeAction(self.toolGroupActions[key])
3643                self.toolGroupActions[key].triggered.disconnect()
3644                del self.toolGroupActions[key]
3645
3646        # step 4: add all newly configured tools
3647        category = self.tr("External Tools/{0}").format(toolGroup[0])
3648        for tool in toolGroup[1]:
3649            if tool['menutext'] != '--':
3650                key = "{0}@@{1}".format(toolGroup[0], tool['menutext'])
3651                if key not in groupActionKeys:
3652                    act = QAction(UI.PixmapCache.getIcon(tool['icon']),
3653                                  tool['menutext'], self)
3654                    act.setObjectName(key)
3655                    act.triggered.connect(
3656                        functools.partial(self.__toolActionTriggered, act))
3657                    self.toolGroupActions[key] = act
3658
3659                    self.toolbarManager.addAction(act, category)
3660
3661    def __showFileMenu(self):
3662        """
3663        Private slot to display the File menu.
3664        """
3665        self.showMenu.emit("File", self.__menus["file"])
3666
3667    def __showExtrasMenu(self):
3668        """
3669        Private slot to display the Extras menu.
3670        """
3671        self.showMenu.emit("Extras", self.__menus["extras"])
3672
3673    def __showWizardsMenu(self):
3674        """
3675        Private slot to display the Wizards menu.
3676        """
3677        self.showMenu.emit("Wizards", self.__menus["wizards"])
3678
3679    def __showHelpMenu(self):
3680        """
3681        Private slot to display the Help menu.
3682        """
3683        self.checkUpdateAct.setEnabled(not self.__inVersionCheck)
3684        self.showVersionsAct.setEnabled(not self.__inVersionCheck)
3685        self.showErrorLogAct.setEnabled(self.__hasErrorLog())
3686
3687        infoFileName = Globals.getInstallInfoFilePath()
3688        self.showInstallInfoAct.setEnabled(os.path.exists(infoFileName))
3689
3690        self.showMenu.emit("Help", self.__menus["help"])
3691
3692    def __showSettingsMenu(self):
3693        """
3694        Private slot to show the Settings menu.
3695        """
3696        self.editMessageFilterAct.setEnabled(
3697            E5ErrorMessage.messageHandlerInstalled())
3698
3699        self.showMenu.emit("Settings", self.__menus["settings"])
3700
3701    def __showNext(self):
3702        """
3703        Private slot used to show the next tab or file.
3704        """
3705        fwidget = QApplication.focusWidget()
3706        while fwidget and not hasattr(fwidget, 'nextTab'):
3707            fwidget = fwidget.parent()
3708        if fwidget:
3709            fwidget.nextTab()
3710
3711    def __showPrevious(self):
3712        """
3713        Private slot used to show the previous tab or file.
3714        """
3715        fwidget = QApplication.focusWidget()
3716        while fwidget and not hasattr(fwidget, 'prevTab'):
3717            fwidget = fwidget.parent()
3718        if fwidget:
3719            fwidget.prevTab()
3720
3721    def __switchTab(self):
3722        """
3723        Private slot used to switch between the current and the previous
3724        current tab.
3725        """
3726        fwidget = QApplication.focusWidget()
3727        while fwidget and not hasattr(fwidget, 'switchTab'):
3728            fwidget = fwidget.parent()
3729        if fwidget:
3730            fwidget.switchTab()
3731
3732    def __whatsThis(self):
3733        """
3734        Private slot called in to enter Whats This mode.
3735        """
3736        QWhatsThis.enterWhatsThisMode()
3737
3738    def __showVersions(self):
3739        """
3740        Private slot to handle the Versions dialog.
3741        """
3742        try:
3743            try:
3744                from PyQt5 import sip
3745            except ImportError:
3746                import sip
3747            sip_version_str = sip.SIP_VERSION_STR
3748        except (ImportError, AttributeError):
3749            sip_version_str = "sip version not available"
3750
3751        sizeStr = "64-Bit" if sys.maxsize > 2**32 else "32-Bit"
3752
3753        versionText = self.tr(
3754            """<h2>Version Numbers</h2>"""
3755            """<table>""")
3756        versionText += (
3757            """<tr><td><b>Python</b></td><td>{0}, {1}</td></tr>"""
3758        ).format(sys.version.split()[0], sizeStr)
3759        versionText += (
3760            """<tr><td><b>Qt</b></td><td>{0}</td></tr>"""
3761        ).format(qVersion())
3762        versionText += (
3763            """<tr><td><b>PyQt</b></td><td>{0}</td></tr>"""
3764        ).format(PYQT_VERSION_STR)
3765        with contextlib.suppress(ImportError, AttributeError):
3766            from PyQt5 import QtChart
3767            versionText += (
3768                """<tr><td><b>PyQtChart</b></td><td>{0}</td></tr>"""
3769            ).format(QtChart.PYQT_CHART_VERSION_STR)
3770        with contextlib.suppress(ImportError, AttributeError):
3771            from PyQt5 import QtWebEngine
3772            versionText += (
3773                """<tr><td><b>PyQtWebEngine</b></td><td>{0}</td></tr>"""
3774            ).format(QtWebEngine.PYQT_WEBENGINE_VERSION_STR)
3775        versionText += (
3776            """<tr><td><b>QScintilla</b></td><td>{0}</td></tr>"""
3777        ).format(QSCINTILLA_VERSION_STR)
3778        versionText += (
3779            """<tr><td><b>sip</b></td><td>{0}</td></tr>"""
3780        ).format(sip_version_str)
3781        with contextlib.suppress(ImportError):
3782            from WebBrowser.Tools import WebBrowserTools
3783            chromeVersion = WebBrowserTools.getWebEngineVersions()[0]
3784            versionText += (
3785                """<tr><td><b>WebEngine</b></td><td>{0}</td></tr>"""
3786            ).format(chromeVersion)
3787        versionText += ("""<tr><td><b>{0}</b></td><td>{1}</td></tr>"""
3788                        ).format(Program, Version)
3789        versionText += self.tr("""</table>""")
3790
3791        E5MessageBox.about(self, Program, versionText)
3792
3793    def __reportBug(self):
3794        """
3795        Private slot to handle the Report Bug dialog.
3796        """
3797        self.showEmailDialog("bug")
3798
3799    def __requestFeature(self):
3800        """
3801        Private slot to handle the Feature Request dialog.
3802        """
3803        self.showEmailDialog("feature")
3804
3805    def showEmailDialog(self, mode, attachFile=None, deleteAttachFile=False):
3806        """
3807        Public slot to show the email dialog in a given mode.
3808
3809        @param mode mode of the email dialog (string, "bug" or "feature")
3810        @param attachFile name of a file to attach to the email (string)
3811        @param deleteAttachFile flag indicating to delete the attached file
3812            after it has been sent (boolean)
3813        """
3814        if Preferences.getUser("UseSystemEmailClient"):
3815            self.__showSystemEmailClient(mode, attachFile, deleteAttachFile)
3816        else:
3817            if not Preferences.getUser("UseGoogleMailOAuth2") and (
3818                Preferences.getUser("Email") == "" or
3819                    Preferences.getUser("MailServer") == ""):
3820                E5MessageBox.critical(
3821                    self,
3822                    self.tr("Report Bug"),
3823                    self.tr(
3824                        """Email address or mail server address is empty."""
3825                        """ Please configure your Email settings in the"""
3826                        """ Preferences Dialog."""))
3827                self.showPreferences("emailPage")
3828                return
3829
3830            from .EmailDialog import EmailDialog
3831            self.dlg = EmailDialog(mode=mode)
3832            if attachFile is not None:
3833                self.dlg.attachFile(attachFile, deleteAttachFile)
3834            self.dlg.show()
3835
3836    def __showSystemEmailClient(self, mode, attachFile=None,
3837                                deleteAttachFile=False):
3838        """
3839        Private slot to show the system email dialog.
3840
3841        @param mode mode of the email dialog (string, "bug" or "feature")
3842        @param attachFile name of a file to put into the body of the
3843            email (string)
3844        @param deleteAttachFile flag indicating to delete the file after
3845            it has been read (boolean)
3846        """
3847        address = FeatureAddress if mode == "feature" else BugAddress
3848        subject = "[eric] "
3849        if attachFile is not None:
3850            with open(attachFile, "r", encoding="utf-8") as f:
3851                body = f.read()
3852            if deleteAttachFile:
3853                os.remove(attachFile)
3854        else:
3855            body = "\r\n----\r\n{0}\r\n----\r\n{1}\r\n----\r\n{2}".format(
3856                Utilities.generateVersionInfo("\r\n"),
3857                Utilities.generatePluginsVersionInfo("\r\n"),
3858                Utilities.generateDistroInfo("\r\n"))
3859
3860        url = QUrl("mailto:{0}".format(address))
3861        urlQuery = QUrlQuery(url)
3862        urlQuery.addQueryItem("subject", subject)
3863        urlQuery.addQueryItem("body", body)
3864        url.setQuery(urlQuery)
3865        QDesktopServices.openUrl(url)
3866
3867    def checkForErrorLog(self):
3868        """
3869        Public method to check for the presence of an error log and ask the
3870        user, what to do with it.
3871        """
3872        if Preferences.getUI("CheckErrorLog"):
3873            logFile = os.path.join(Utilities.getConfigDir(),
3874                                   self.ErrorLogFileName)
3875            if os.path.exists(logFile):
3876                from .ErrorLogDialog import ErrorLogDialog
3877                dlg = ErrorLogDialog(logFile, False, self)
3878                dlg.exec()
3879
3880    def __hasErrorLog(self):
3881        """
3882        Private method to check, if an error log file exists.
3883
3884        @return flag indicating the existence of an error log file (boolean)
3885        """
3886        logFile = os.path.join(Utilities.getConfigDir(),
3887                               self.ErrorLogFileName)
3888        return os.path.exists(logFile)
3889
3890    def __showErrorLog(self):
3891        """
3892        Private slot to show the most recent error log message.
3893        """
3894        logFile = os.path.join(Utilities.getConfigDir(),
3895                               self.ErrorLogFileName)
3896        if os.path.exists(logFile):
3897            from .ErrorLogDialog import ErrorLogDialog
3898            dlg = ErrorLogDialog(logFile, True, self)
3899            dlg.show()
3900
3901    def __showInstallInfo(self):
3902        """
3903        Private slot to show a dialog containing information about the
3904        installation process.
3905        """
3906        from .InstallInfoDialog import InstallInfoDialog
3907        dlg = InstallInfoDialog(self)
3908        if dlg.wasLoaded():
3909            dlg.exec()
3910
3911    def __compareFiles(self):
3912        """
3913        Private slot to handle the Compare Files dialog.
3914        """
3915        aw = self.viewmanager.activeWindow()
3916        fn = aw and aw.getFileName() or None
3917        if self.diffDlg is None:
3918            from .DiffDialog import DiffDialog
3919            self.diffDlg = DiffDialog()
3920        self.diffDlg.show(fn)
3921
3922    def __compareFilesSbs(self):
3923        """
3924        Private slot to handle the Compare Files dialog.
3925        """
3926        aw = self.viewmanager.activeWindow()
3927        fn = aw and aw.getFileName() or None
3928        if self.compareDlg is None:
3929            from .CompareDialog import CompareDialog
3930            self.compareDlg = CompareDialog()
3931        self.compareDlg.show(fn)
3932
3933    def __openMiniEditor(self):
3934        """
3935        Private slot to show a mini editor window.
3936        """
3937        from QScintilla.MiniEditor import MiniEditor
3938        editor = MiniEditor(parent=self)
3939        editor.show()
3940
3941    def addE5Actions(self, actions, actionType):
3942        """
3943        Public method to add actions to the list of actions.
3944
3945        @param actions list of actions to be added (list of E5Action)
3946        @param actionType string denoting the action set to add to.
3947            It must be one of "ui" or "wizards".
3948        """
3949        if actionType == 'ui':
3950            self.actions.extend(actions)
3951        elif actionType == 'wizards':
3952            self.wizardsActions.extend(actions)
3953
3954    def removeE5Actions(self, actions, actionType='ui'):
3955        """
3956        Public method to remove actions from the list of actions.
3957
3958        @param actions list of actions (list of E5Action)
3959        @param actionType string denoting the action set to remove from.
3960            It must be one of "ui" or "wizards".
3961        """
3962        for act in actions:
3963            with contextlib.suppress(ValueError):
3964                if actionType == 'ui':
3965                    self.actions.remove(act)
3966                elif actionType == 'wizards':
3967                    self.wizardsActions.remove(act)
3968
3969    def getActions(self, actionType):
3970        """
3971        Public method to get a list of all actions.
3972
3973        @param actionType string denoting the action set to get.
3974            It must be one of "ui" or "wizards".
3975        @return list of all actions (list of E5Action)
3976        """
3977        if actionType == 'ui':
3978            return self.actions[:]
3979        elif actionType == 'wizards':
3980            return self.wizardsActions[:]
3981        else:
3982            return []
3983
3984    def getMenuAction(self, menuName, actionName):
3985        """
3986        Public method to get a reference to an action of a menu.
3987
3988        @param menuName name of the menu to search in (string)
3989        @param actionName object name of the action to search for
3990            (string)
3991        @return reference to the menu action (QAction)
3992        """
3993        try:
3994            menu = self.__menus[menuName]
3995        except KeyError:
3996            return None
3997
3998        for act in menu.actions():
3999            if act.objectName() == actionName:
4000                return act
4001
4002        return None
4003
4004    def getMenuBarAction(self, menuName):
4005        """
4006        Public method to get a reference to an action of the main menu.
4007
4008        @param menuName name of the menu to search in (string)
4009        @return reference to the menu bar action (QAction)
4010        """
4011        try:
4012            menu = self.__menus[menuName]
4013        except KeyError:
4014            return None
4015
4016        return menu.menuAction()
4017
4018    def getMenu(self, name):
4019        """
4020        Public method to get a reference to a specific menu.
4021
4022        @param name name of the menu (string)
4023        @return reference to the menu (QMenu)
4024        """
4025        try:
4026            return self.__menus[name]
4027        except KeyError:
4028            return None
4029
4030    def registerToolbar(self, name, text, toolbar, category=""):
4031        """
4032        Public method to register a toolbar.
4033
4034        This method must be called in order to make a toolbar manageable by the
4035        UserInterface object.
4036
4037        @param name name of the toolbar. This is used as the key into
4038            the dictionary of toolbar references.
4039        @type str
4040        @param text user visible text for the toolbar entry
4041        @type str
4042        @param toolbar reference to the toolbar to be registered
4043        @type QToolBar
4044        @param category toolbar category
4045        @type str
4046        @exception KeyError raised, if a toolbar with the given name was
4047            already registered
4048        """
4049        if name in self.__toolbars:
4050            raise KeyError("Toolbar '{0}' already registered.".format(name))
4051
4052        self.__toolbars[name] = [text, toolbar, category]
4053
4054    def reregisterToolbar(self, name, text, category=""):
4055        """
4056        Public method to change the visible text for the named toolbar.
4057
4058        @param name name of the toolbar to be changed
4059        @type str
4060        @param text new user visible text for the toolbar entry
4061        @type str
4062        @param category new toolbar category for the toolbar entry
4063        @type str
4064        """
4065        if name in self.__toolbars:
4066            self.__toolbars[name][0] = text
4067            self.__toolbars[name][2] = category
4068
4069    def unregisterToolbar(self, name):
4070        """
4071        Public method to unregister a toolbar.
4072
4073        @param name name of the toolbar (string).
4074        """
4075        if name in self.__toolbars:
4076            del self.__toolbars[name]
4077
4078    def getToolbar(self, name):
4079        """
4080        Public method to get a reference to a specific toolbar.
4081
4082        @param name name of the toolbar (string)
4083        @return reference to the toolbar entry (tuple of string and QToolBar)
4084        """
4085        try:
4086            return self.__toolbars[name]
4087        except KeyError:
4088            return None
4089
4090    def getToolbarsByCategory(self, category):
4091        """
4092        Public method to get a list of toolbars belonging to a given toolbar
4093        category.
4094
4095        @param category toolbar category
4096        @type str
4097        @return list of toolbars
4098        @rtype list of QToolBar
4099        """
4100        toolbars = []
4101        for tbName in self.__toolbars:
4102            with contextlib.suppress(IndexError):
4103                if self.__toolbars[tbName][2] == category:
4104                    toolbars.append(self.__toolbars[tbName][1])
4105
4106        return toolbars
4107
4108    def getLocale(self):
4109        """
4110        Public method to get the locale of the IDE.
4111
4112        @return locale of the IDE (string or None)
4113        """
4114        return self.locale
4115
4116    def __quit(self):
4117        """
4118        Private method to quit the application.
4119        """
4120        if self.__shutdown():
4121            e5App().closeAllWindows()
4122
4123    @pyqtSlot()
4124    def __restart(self, ask=False):
4125        """
4126        Private method to restart the application.
4127
4128        @param ask flag indicating to ask the user for permission
4129        @type bool
4130        """
4131        res = (
4132            E5MessageBox.yesNo(
4133                self,
4134                self.tr("Restart application"),
4135                self.tr(
4136                    """The application needs to be restarted. Do it now?"""),
4137                yesDefault=True)
4138            if ask else
4139            True
4140        )
4141
4142        if res and self.__shutdown():
4143            e5App().closeAllWindows()
4144            program = sys.executable
4145            eric6 = os.path.join(getConfig("ericDir"), "eric6.py")
4146            args = [eric6]
4147            args.append("--start-session")
4148            args.extend(self.__restartArgs)
4149            QProcess.startDetached(program, args)
4150
4151    def __newWindow(self):
4152        """
4153        Private slot to start a new instance of eric.
4154        """
4155        if not Preferences.getUI("SingleApplicationMode"):
4156            # start eric without any arguments
4157            program = sys.executable
4158            eric6 = os.path.join(getConfig("ericDir"), "eric6.py")
4159            args = [eric6]
4160            QProcess.startDetached(program, args)
4161
4162    def __initToolsMenus(self, menu):
4163        """
4164        Private slot to initialize the various tool menus.
4165
4166        @param menu reference to the parent menu
4167        @type QMenu
4168        """
4169        btMenu = QMenu(self.tr("&Builtin Tools"), self)
4170        if self.designer4Act is not None:
4171            btMenu.addAction(self.designer4Act)
4172        if self.linguist4Act is not None:
4173            btMenu.addAction(self.linguist4Act)
4174        btMenu.addAction(self.uipreviewerAct)
4175        btMenu.addAction(self.trpreviewerAct)
4176        btMenu.addAction(self.diffAct)
4177        btMenu.addAction(self.compareAct)
4178        btMenu.addAction(self.sqlBrowserAct)
4179        btMenu.addAction(self.miniEditorAct)
4180        btMenu.addAction(self.hexEditorAct)
4181        btMenu.addAction(self.iconEditorAct)
4182        btMenu.addAction(self.snapshotAct)
4183        if self.webBrowserAct:
4184            btMenu.addAction(self.webBrowserAct)
4185
4186        ptMenu = QMenu(self.tr("&Plugin Tools"), self)
4187        ptMenu.aboutToShow.connect(self.__showPluginToolsMenu)
4188
4189        utMenu = QMenu(self.tr("&User Tools"), self)
4190        utMenu.triggered.connect(self.__toolExecute)
4191        utMenu.aboutToShow.connect(self.__showUserToolsMenu)
4192
4193        menu.addMenu(btMenu)
4194        menu.addMenu(ptMenu)
4195        menu.addMenu(utMenu)
4196
4197        self.__menus["builtin_tools"] = btMenu
4198        self.__menus["plugin_tools"] = ptMenu
4199        self.__menus["user_tools"] = utMenu
4200
4201    def __showPluginToolsMenu(self):
4202        """
4203        Private slot to show the Plugin Tools menu.
4204        """
4205        self.showMenu.emit("PluginTools", self.__menus["plugin_tools"])
4206
4207    def __showUserToolsMenu(self):
4208        """
4209        Private slot to display the User Tools menu.
4210        """
4211        self.__menus["user_tools"].clear()
4212
4213        self.__menus["user_tools"].addMenu(self.toolGroupsMenu)
4214        act = self.__menus["user_tools"].addAction(
4215            self.tr("Configure Tool Groups ..."),
4216            self.__toolGroupsConfiguration)
4217        act.setData(-1)
4218        act = self.__menus["user_tools"].addAction(
4219            self.tr("Configure current Tool Group ..."),
4220            self.__toolsConfiguration)
4221        act.setData(-2)
4222        act.setEnabled(self.currentToolGroup >= 0)
4223        self.__menus["user_tools"].addSeparator()
4224
4225        # add the configurable entries
4226        try:
4227            for idx, tool in enumerate(
4228                self.toolGroups[self.currentToolGroup][1]
4229            ):
4230                if tool['menutext'] == '--':
4231                    self.__menus["user_tools"].addSeparator()
4232                else:
4233                    act = self.__menus["user_tools"].addAction(
4234                        UI.PixmapCache.getIcon(tool['icon']),
4235                        tool['menutext'])
4236                    act.setData(idx)
4237        except IndexError:
4238            # the current tool group might have been deleted
4239            act = self.__menus["user_tools"].addAction(
4240                self.tr("No User Tools Configured"))
4241            act.setData(-3)
4242
4243    def __showToolGroupsMenu(self):
4244        """
4245        Private slot to display the Tool Groups menu.
4246        """
4247        self.toolGroupsMenu.clear()
4248
4249        # add the configurable tool groups
4250        if self.toolGroups:
4251            for idx, toolGroup in enumerate(self.toolGroups):
4252                act = self.toolGroupsMenu.addAction(toolGroup[0])
4253                act.setData(idx)
4254                if self.currentToolGroup == idx:
4255                    font = act.font()
4256                    font.setBold(True)
4257                    act.setFont(font)
4258        else:
4259            act = self.toolGroupsMenu.addAction(
4260                self.tr("No User Tools Configured"))
4261            act.setData(-3)
4262
4263    def __toolGroupSelected(self, act):
4264        """
4265        Private slot to set the current tool group.
4266
4267        @param act reference to the action that was triggered (QAction)
4268        """
4269        self.toolGroupsMenuTriggered = True
4270        idx = act.data()
4271        if idx is not None:
4272            self.currentToolGroup = idx
4273
4274    def __showWindowMenu(self):
4275        """
4276        Private slot to display the Window menu.
4277        """
4278        self.__menus["window"].clear()
4279
4280        self.__menus["window"].addActions(self.viewProfileActGrp.actions())
4281        self.__menus["window"].addSeparator()
4282
4283        if self.__layoutType == "Toolboxes":
4284            self.__menus["window"].addAction(self.ltAct)
4285            self.ltAct.setChecked(not self.lToolboxDock.isHidden())
4286            self.__menus["window"].addAction(self.rtAct)
4287            self.rtAct.setChecked(not self.lToolboxDock.isHidden())
4288            self.__menus["window"].addAction(self.htAct)
4289            self.htAct.setChecked(not self.hToolboxDock.isHidden())
4290        elif self.__layoutType == "Sidebars":
4291            self.__menus["window"].addAction(self.lsbAct)
4292            self.lsbAct.setChecked(not self.leftSidebar.isHidden())
4293            self.__menus["window"].addAction(self.rsbAct)
4294            self.rsbAct.setChecked(not self.rightSidebar.isHidden())
4295            self.__menus["window"].addAction(self.bsbAct)
4296            self.bsbAct.setChecked(not self.bottomSidebar.isHidden())
4297
4298        # Insert menu entry for sub-windows
4299        self.__menus["window"].addSeparator()
4300        self.__menus["window"].addMenu(self.__menus["subwindow"])
4301
4302        # Insert menu entry for toolbar settings
4303        self.__menus["window"].addSeparator()
4304        self.__menus["window"].addMenu(self.__menus["toolbars"])
4305
4306        # Now do any Source Viewer related stuff.
4307        self.viewmanager.showWindowMenu(self.__menus["window"])
4308
4309        self.showMenu.emit("Window", self.__menus["window"])
4310
4311    def __showSubWindowMenu(self):
4312        """
4313        Private slot to display the Window menu of the Window menu.
4314        """
4315        self.showMenu.emit("Subwindows", self.__menus["subwindow"])
4316
4317    def __populateToolbarsMenu(self, menu):
4318        """
4319        Private method to populate a toolbars menu.
4320
4321        @param menu reference to the menu to be populated (QMenu)
4322        """
4323        menu.clear()
4324
4325        for name, (text, tb, _category) in sorted(
4326            self.__toolbars.items(), key=lambda t: t[1][0]
4327        ):
4328            act = menu.addAction(text)
4329            act.setCheckable(True)
4330            act.setChecked(not tb.isHidden())
4331            act.setData(name)
4332        menu.addSeparator()
4333        act = menu.addAction(self.tr("&Show all"))
4334        act.setData("__SHOW__")
4335        act = menu.addAction(self.tr("&Hide all"))
4336        act.setData("__HIDE__")
4337
4338    def createPopupMenu(self):
4339        """
4340        Public method to create the toolbars menu for Qt.
4341
4342        @return toolbars menu (QMenu)
4343        """
4344        menu = QMenu(self)
4345        menu.triggered.connect(self.__TBPopupMenuTriggered)
4346
4347        self.__populateToolbarsMenu(menu)
4348
4349        return menu
4350
4351    def __showToolbarsMenu(self):
4352        """
4353        Private slot to display the Toolbars menu.
4354        """
4355        self.__populateToolbarsMenu(self.__menus["toolbars"])
4356
4357    def __TBMenuTriggered(self, act):
4358        """
4359        Private method to handle the toggle of a toolbar via the Window->
4360        Toolbars submenu.
4361
4362        @param act reference to the action that was triggered (QAction)
4363        """
4364        name = act.data()
4365        if name:
4366            if name == "__SHOW__":
4367                for _text, tb, _category in self.__toolbars.values():
4368                    tb.show()
4369                if self.__menus["toolbars"].isTearOffMenuVisible():
4370                    self.__menus["toolbars"].hideTearOffMenu()
4371            elif name == "__HIDE__":
4372                for _text, tb, _category in self.__toolbars.values():
4373                    tb.hide()
4374                if self.__menus["toolbars"].isTearOffMenuVisible():
4375                    self.__menus["toolbars"].hideTearOffMenu()
4376            else:
4377                tb = self.__toolbars[name][1]
4378                if act.isChecked():
4379                    tb.show()
4380                    tb.setEnabled(True)
4381                else:
4382                    tb.hide()
4383
4384    def __TBPopupMenuTriggered(self, act):
4385        """
4386        Private method to handle the toggle of a toolbar via the QMainWindow
4387        Toolbars popup menu.
4388
4389        @param act reference to the action that was triggered (QAction)
4390        """
4391        name = act.data()
4392        if name:
4393            if name == "__SHOW__":
4394                for _text, tb, _category in self.__toolbars.values():
4395                    tb.show()
4396            elif name == "__HIDE__":
4397                for _text, tb, _category in self.__toolbars.values():
4398                    tb.hide()
4399            else:
4400                tb = self.__toolbars[name][1]
4401                if act.isChecked():
4402                    tb.show()
4403                    tb.setEnabled(True)
4404                else:
4405                    tb.hide()
4406            if self.__menus["toolbars"].isTearOffMenuVisible():
4407                self.__menus["toolbars"].hideTearOffMenu()
4408
4409    def __saveCurrentViewProfile(self, save):
4410        """
4411        Private slot to save the window geometries of the active profile.
4412
4413        @param save flag indicating that the current profile should
4414            be saved (boolean)
4415        """
4416        if self.currentProfile and save:
4417            # step 1: save the window geometries of the active profile
4418            if self.__layoutType in ["Toolboxes", "Sidebars"]:
4419                state = self.saveState()
4420                self.profiles[self.currentProfile][0] = state
4421                if self.__layoutType == "Sidebars":
4422                    state = self.leftSplitter.saveState()
4423                    self.profiles[self.currentProfile][2][0] = state
4424                    state = self.verticalSplitter.saveState()
4425                    self.profiles[self.currentProfile][2][1] = state
4426                    state = self.leftSidebar.saveState()
4427                    self.profiles[self.currentProfile][2][2] = state
4428                    state = self.bottomSidebar.saveState()
4429                    self.profiles[self.currentProfile][2][3] = state
4430                    state = self.rightSplitter.saveState()
4431                    self.profiles[self.currentProfile][2][4] = state
4432                    state = self.rightSidebar.saveState()
4433                    self.profiles[self.currentProfile][2][5] = state
4434            # step 2: save the visibility of the windows of the active profile
4435            if self.__layoutType == "Toolboxes":
4436                self.profiles[self.currentProfile][1][0] = (
4437                    self.lToolboxDock.isVisible()
4438                )
4439                self.profiles[self.currentProfile][1][1] = (
4440                    self.hToolboxDock.isVisible()
4441                )
4442                self.profiles[self.currentProfile][1][2] = (
4443                    self.rToolboxDock.isVisible()
4444                )
4445            elif self.__layoutType == "Sidebars":
4446                self.profiles[self.currentProfile][1][0] = (
4447                    self.leftSidebar.isVisible()
4448                )
4449                self.profiles[self.currentProfile][1][1] = (
4450                    self.bottomSidebar.isVisible()
4451                )
4452                self.profiles[self.currentProfile][1][2] = (
4453                    self.rightSidebar.isVisible()
4454                )
4455            Preferences.setUI("ViewProfiles2", self.profiles)
4456
4457    def __activateViewProfile(self, name, save=True):
4458        """
4459        Private slot to activate a view profile.
4460
4461        @param name name of the profile to be activated (string)
4462        @param save flag indicating that the current profile should
4463            be saved (boolean)
4464        """
4465        if self.currentProfile != name or not save:
4466            # step 1: save the active profile
4467            self.__saveCurrentViewProfile(save)
4468
4469            # step 2: set the window geometries of the new profile
4470            if self.__layoutType in ["Toolboxes", "Sidebars"]:
4471                state = self.profiles[name][0]
4472                if not state.isEmpty():
4473                    self.restoreState(state)
4474                if self.__layoutType == "Sidebars":
4475                    state = self.profiles[name][2][0]
4476                    if not state.isEmpty():
4477                        self.leftSplitter.restoreState(state)
4478                    state = self.profiles[name][2][1]
4479                    if not state.isEmpty():
4480                        self.verticalSplitter.restoreState(state)
4481                    state = self.profiles[name][2][2]
4482                    if not state.isEmpty():
4483                        self.leftSidebar.restoreState(state)
4484                    state = self.profiles[name][2][3]
4485                    if not state.isEmpty():
4486                        self.bottomSidebar.restoreState(state)
4487                    state = self.profiles[name][2][4]
4488                    if not state.isEmpty():
4489                        self.rightSplitter.restoreState(state)
4490                    state = self.profiles[name][2][5]
4491                    if not state.isEmpty():
4492                        self.rightSidebar.restoreState(state)
4493
4494                if self.__layoutType == "Toolboxes":
4495                    # set the corner usages
4496                    self.setCorner(Qt.Corner.TopLeftCorner,
4497                                   Qt.DockWidgetArea.LeftDockWidgetArea)
4498                    self.setCorner(Qt.Corner.BottomLeftCorner,
4499                                   Qt.DockWidgetArea.LeftDockWidgetArea)
4500                    self.setCorner(Qt.Corner.TopRightCorner,
4501                                   Qt.DockWidgetArea.RightDockWidgetArea)
4502                    self.setCorner(Qt.Corner.BottomRightCorner,
4503                                   Qt.DockWidgetArea.RightDockWidgetArea)
4504
4505            # step 3: activate the windows of the new profile
4506            if self.__layoutType == "Toolboxes":
4507                self.lToolboxDock.setVisible(self.profiles[name][1][0])
4508                self.hToolboxDock.setVisible(self.profiles[name][1][1])
4509                self.rToolboxDock.setVisible(self.profiles[name][1][2])
4510            elif self.__layoutType == "Sidebars":
4511                self.leftSidebar.setVisible(self.profiles[name][1][0])
4512                self.bottomSidebar.setVisible(self.profiles[name][1][1])
4513                self.rightSidebar.setVisible(self.profiles[name][1][2])
4514
4515            # step 4: remember the new profile
4516            self.currentProfile = name
4517
4518            # step 5: make sure that cursor of the shell is visible
4519            self.shell.ensureCursorVisible()
4520
4521            # step 6: make sure, that the toolbars and window menu are
4522            #         shown correctly
4523            if self.__menus["toolbars"].isTearOffMenuVisible():
4524                self.__showToolbarsMenu()
4525            if self.__menus["window"].isTearOffMenuVisible():
4526                self.__showWindowMenu()
4527
4528    def __debuggingStarted(self):
4529        """
4530        Private slot to handle the start of a debugging session.
4531        """
4532        self.setDebugProfile()
4533        if self.__layoutType == "Toolboxes":
4534            self.__currentRightWidget = self.rToolbox.currentWidget()
4535            self.rToolbox.setCurrentWidget(self.debugViewer)
4536            self.__currentBottomWidget = self.hToolbox.currentWidget()
4537            self.hToolbox.setCurrentWidget(self.shellAssembly)
4538        elif self.__layoutType == "Sidebars":
4539            self.__currentRightWidget = self.rightSidebar.currentWidget()
4540            self.rightSidebar.setCurrentWidget(self.debugViewer)
4541            self.__currentBottomWidget = self.bottomSidebar.currentWidget()
4542            self.bottomSidebar.setCurrentWidget(self.shellAssembly)
4543
4544    def __debuggingDone(self):
4545        """
4546        Private slot to handle the end of a debugging session.
4547        """
4548        self.__setEditProfile()
4549        if self.__layoutType == "Toolboxes":
4550            if self.__currentRightWidget:
4551                self.rToolbox.setCurrentWidget(self.__currentRightWidget)
4552            if self.__currentBottomWidget:
4553                self.hToolbox.setCurrentWidget(self.__currentBottomWidget)
4554        elif self.__layoutType == "Sidebars":
4555            if self.__currentRightWidget:
4556                self.rightSidebar.setCurrentWidget(self.__currentRightWidget)
4557            if self.__currentBottomWidget:
4558                self.bottomSidebar.setCurrentWidget(self.__currentBottomWidget)
4559        self.__currentRightWidget = None
4560        self.__currentBottomWidget = None
4561        self.__activateViewmanager()
4562
4563    @pyqtSlot()
4564    def __setEditProfile(self, save=True):
4565        """
4566        Private slot to activate the edit view profile.
4567
4568        @param save flag indicating that the current profile should
4569            be saved (boolean)
4570        """
4571        self.__activateViewProfile("edit", save)
4572        self.setEditProfileAct.setChecked(True)
4573
4574    @pyqtSlot()
4575    def setDebugProfile(self, save=True):
4576        """
4577        Public slot to activate the debug view profile.
4578
4579        @param save flag indicating that the current profile should
4580            be saved (boolean)
4581        """
4582        self.viewmanager.searchWidget().hide()
4583        self.viewmanager.replaceWidget().hide()
4584        self.__activateViewProfile("debug", save)
4585        self.setDebugProfileAct.setChecked(True)
4586
4587    def getViewProfile(self):
4588        """
4589        Public method to get the current view profile.
4590
4591        @return the name of the current view profile (string)
4592        """
4593        return self.currentProfile
4594
4595    def getLayoutType(self):
4596        """
4597        Public method to get the current layout type.
4598
4599        @return current layout type
4600        @rtype str
4601        """
4602        return self.__layoutType
4603
4604    def __activateProjectBrowser(self):
4605        """
4606        Private slot to handle the activation of the project browser.
4607        """
4608        if self.__layoutType == "Toolboxes":
4609            self.lToolboxDock.show()
4610            self.lToolbox.setCurrentWidget(self.projectBrowser)
4611        elif self.__layoutType == "Sidebars":
4612            self.leftSidebar.show()
4613            self.leftSidebar.setCurrentWidget(self.projectBrowser)
4614        self.projectBrowser.currentWidget().setFocus(
4615            Qt.FocusReason.ActiveWindowFocusReason)
4616
4617    def __activateMultiProjectBrowser(self):
4618        """
4619        Private slot to handle the activation of the project browser.
4620        """
4621        if self.__layoutType == "Toolboxes":
4622            self.lToolboxDock.show()
4623            self.lToolbox.setCurrentWidget(self.multiProjectBrowser)
4624        elif self.__layoutType == "Sidebars":
4625            self.leftSidebar.show()
4626            self.leftSidebar.setCurrentWidget(self.multiProjectBrowser)
4627        self.multiProjectBrowser.setFocus(
4628            Qt.FocusReason.ActiveWindowFocusReason)
4629
4630    def activateDebugViewer(self):
4631        """
4632        Public slot to handle the activation of the debug viewer.
4633        """
4634        if self.__layoutType == "Toolboxes":
4635            self.rToolboxDock.show()
4636            self.rToolbox.setCurrentWidget(self.debugViewer)
4637        elif self.__layoutType == "Sidebars":
4638            self.rightSidebar.show()
4639            self.rightSidebar.setCurrentWidget(self.debugViewer)
4640        self.debugViewer.currentWidget().setFocus(
4641            Qt.FocusReason.ActiveWindowFocusReason)
4642
4643    def __activateShell(self):
4644        """
4645        Private slot to handle the activation of the Shell window.
4646        """
4647        if self.__layoutType == "Toolboxes":
4648            self.__shellParent.show()
4649            self.__shellParent.widget().setCurrentWidget(self.shellAssembly)
4650        elif self.__layoutType == "Sidebars":
4651            self.__shellParent.show()
4652            self.__shellParent.setCurrentWidget(self.shellAssembly)
4653        self.shell.setFocus(Qt.FocusReason.ActiveWindowFocusReason)
4654
4655    def __activateLogViewer(self):
4656        """
4657        Private slot to handle the activation of the Log Viewer.
4658        """
4659        if self.__layoutType == "Toolboxes":
4660            self.hToolboxDock.show()
4661            self.hToolbox.setCurrentWidget(self.logViewer)
4662        elif self.__layoutType == "Sidebars":
4663            self.bottomSidebar.show()
4664            self.bottomSidebar.setCurrentWidget(self.logViewer)
4665        self.logViewer.setFocus(Qt.FocusReason.ActiveWindowFocusReason)
4666
4667    def __activateTaskViewer(self):
4668        """
4669        Private slot to handle the activation of the Task Viewer.
4670        """
4671        if self.__layoutType == "Toolboxes":
4672            self.hToolboxDock.show()
4673            self.hToolbox.setCurrentWidget(self.taskViewer)
4674        elif self.__layoutType == "Sidebars":
4675            self.bottomSidebar.show()
4676            self.bottomSidebar.setCurrentWidget(self.taskViewer)
4677        self.taskViewer.setFocus(Qt.FocusReason.ActiveWindowFocusReason)
4678
4679    def __activateTemplateViewer(self):
4680        """
4681        Private slot to handle the activation of the Template Viewer.
4682        """
4683        if self.templateViewer is not None:
4684            if self.__layoutType == "Toolboxes":
4685                self.lToolboxDock.show()
4686                self.lToolbox.setCurrentWidget(self.templateViewer)
4687            elif self.__layoutType == "Sidebars":
4688                self.leftSidebar.show()
4689                self.leftSidebar.setCurrentWidget(self.templateViewer)
4690            self.templateViewer.setFocus(
4691                Qt.FocusReason.ActiveWindowFocusReason)
4692
4693    def __activateBrowser(self):
4694        """
4695        Private slot to handle the activation of the file browser.
4696        """
4697        if self.browser is not None:
4698            if self.__layoutType == "Toolboxes":
4699                self.lToolboxDock.show()
4700                self.lToolbox.setCurrentWidget(self.browser)
4701            elif self.__layoutType == "Sidebars":
4702                self.leftSidebar.show()
4703                self.leftSidebar.setCurrentWidget(self.browser)
4704            self.browser.setFocus(Qt.FocusReason.ActiveWindowFocusReason)
4705
4706    def __toggleLeftToolbox(self):
4707        """
4708        Private slot to handle the toggle of the Left Toolbox window.
4709        """
4710        hasFocus = self.lToolbox.currentWidget().hasFocus()
4711        shown = self.__toggleWindow(self.lToolboxDock)
4712        if shown:
4713            self.lToolbox.currentWidget().setFocus(
4714                Qt.FocusReason.ActiveWindowFocusReason)
4715        else:
4716            if hasFocus:
4717                self.__activateViewmanager()
4718
4719    def __toggleRightToolbox(self):
4720        """
4721        Private slot to handle the toggle of the Right Toolbox window.
4722        """
4723        hasFocus = self.rToolbox.currentWidget().hasFocus()
4724        shown = self.__toggleWindow(self.rToolboxDock)
4725        if shown:
4726            self.rToolbox.currentWidget().setFocus(
4727                Qt.FocusReason.ActiveWindowFocusReason)
4728        else:
4729            if hasFocus:
4730                self.__activateViewmanager()
4731
4732    def __toggleHorizontalToolbox(self):
4733        """
4734        Private slot to handle the toggle of the Horizontal Toolbox window.
4735        """
4736        hasFocus = self.hToolbox.currentWidget().hasFocus()
4737        shown = self.__toggleWindow(self.hToolboxDock)
4738        if shown:
4739            self.hToolbox.currentWidget().setFocus(
4740                Qt.FocusReason.ActiveWindowFocusReason)
4741        else:
4742            if hasFocus:
4743                self.__activateViewmanager()
4744
4745    def __toggleLeftSidebar(self):
4746        """
4747        Private slot to handle the toggle of the left sidebar window.
4748        """
4749        hasFocus = self.leftSidebar.currentWidget().hasFocus()
4750        shown = self.__toggleWindow(self.leftSidebar)
4751        if shown:
4752            self.leftSidebar.currentWidget().setFocus(
4753                Qt.FocusReason.ActiveWindowFocusReason)
4754        else:
4755            if hasFocus:
4756                self.__activateViewmanager()
4757
4758    def __toggleRightSidebar(self):
4759        """
4760        Private slot to handle the toggle of the right sidebar window.
4761        """
4762        hasFocus = self.rightSidebar.currentWidget().hasFocus()
4763        shown = self.__toggleWindow(self.rightSidebar)
4764        if shown:
4765            self.rightSidebar.currentWidget().setFocus(
4766                Qt.FocusReason.ActiveWindowFocusReason)
4767        else:
4768            if hasFocus:
4769                self.__activateViewmanager()
4770
4771    def __toggleBottomSidebar(self):
4772        """
4773        Private slot to handle the toggle of the bottom sidebar window.
4774        """
4775        hasFocus = self.bottomSidebar.currentWidget().hasFocus()
4776        shown = self.__toggleWindow(self.bottomSidebar)
4777        if shown:
4778            self.bottomSidebar.currentWidget().setFocus(
4779                Qt.FocusReason.ActiveWindowFocusReason)
4780        else:
4781            if hasFocus:
4782                self.__activateViewmanager()
4783
4784    def activateCooperationViewer(self):
4785        """
4786        Public slot to handle the activation of the cooperation window.
4787        """
4788        if self.cooperation is not None:
4789            if self.__layoutType == "Toolboxes":
4790                self.rToolboxDock.show()
4791                self.rToolbox.setCurrentWidget(self.cooperation)
4792            elif self.__layoutType == "Sidebars":
4793                self.rightSidebar.show()
4794                self.rightSidebar.setCurrentWidget(self.cooperation)
4795            self.cooperation.setFocus(Qt.FocusReason.ActiveWindowFocusReason)
4796
4797    def __activateIRC(self):
4798        """
4799        Private slot to handle the activation of the IRC window.
4800        """
4801        if self.irc is not None:
4802            if self.__layoutType == "Toolboxes":
4803                self.rToolboxDock.show()
4804                self.rToolbox.setCurrentWidget(self.irc)
4805            elif self.__layoutType == "Sidebars":
4806                self.rightSidebar.show()
4807                self.rightSidebar.setCurrentWidget(self.irc)
4808            self.irc.setFocus(Qt.FocusReason.ActiveWindowFocusReason)
4809
4810    def __activateSymbolsViewer(self):
4811        """
4812        Private slot to handle the activation of the Symbols Viewer.
4813        """
4814        if self.symbolsViewer is not None:
4815            if self.__layoutType == "Toolboxes":
4816                self.lToolboxDock.show()
4817                self.lToolbox.setCurrentWidget(self.symbolsViewer)
4818            elif self.__layoutType == "Sidebars":
4819                self.leftSidebar.show()
4820                self.leftSidebar.setCurrentWidget(self.symbolsViewer)
4821            self.symbolsViewer.setFocus(Qt.FocusReason.ActiveWindowFocusReason)
4822
4823    def __activateNumbersViewer(self):
4824        """
4825        Private slot to handle the activation of the Numbers Viewer.
4826        """
4827        if self.numbersViewer is not None:
4828            if self.__layoutType == "Toolboxes":
4829                self.hToolboxDock.show()
4830                self.hToolbox.setCurrentWidget(self.numbersViewer)
4831            elif self.__layoutType == "Sidebars":
4832                self.bottomSidebar.show()
4833                self.bottomSidebar.setCurrentWidget(self.numbersViewer)
4834            self.numbersViewer.setFocus(Qt.FocusReason.ActiveWindowFocusReason)
4835
4836    def __activateViewmanager(self):
4837        """
4838        Private slot to handle the activation of the current editor.
4839        """
4840        aw = self.viewmanager.activeWindow()
4841        if aw is not None:
4842            aw.setFocus(Qt.FocusReason.ActiveWindowFocusReason)
4843
4844    def activateCodeDocumentationViewer(self, switchFocus=True):
4845        """
4846        Public slot to handle the activation of the Code Documentation Viewer.
4847
4848        @param switchFocus flag indicating to transfer the input focus
4849        @type bool
4850        """
4851        if self.codeDocumentationViewer is not None:
4852            if self.__layoutType == "Toolboxes":
4853                self.rToolboxDock.show()
4854                self.rToolbox.setCurrentWidget(self.codeDocumentationViewer)
4855            elif self.__layoutType == "Sidebars":
4856                self.rightSidebar.show()
4857                self.rightSidebar.setCurrentWidget(
4858                    self.codeDocumentationViewer)
4859            if switchFocus:
4860                self.codeDocumentationViewer.setFocus(
4861                    Qt.FocusReason.ActiveWindowFocusReason)
4862
4863    def __activatePipWidget(self):
4864        """
4865        Private slot to handle the activation of the PyPI manager widget.
4866        """
4867        if self.pipWidget is not None:
4868            if self.__layoutType == "Toolboxes":
4869                self.rToolboxDock.show()
4870                self.rToolbox.setCurrentWidget(self.pipWidget)
4871            elif self.__layoutType == "Sidebars":
4872                self.rightSidebar.show()
4873                self.rightSidebar.setCurrentWidget(self.pipWidget)
4874            self.pipWidget.setFocus(Qt.FocusReason.ActiveWindowFocusReason)
4875
4876    def __activateCondaWidget(self):
4877        """
4878        Private slot to handle the activation of the Conda manager widget.
4879        """
4880        if self.condaWidget is not None:
4881            if self.__layoutType == "Toolboxes":
4882                self.rToolboxDock.show()
4883                self.rToolbox.setCurrentWidget(self.condaWidget)
4884            elif self.__layoutType == "Sidebars":
4885                self.rightSidebar.show()
4886                self.rightSidebar.setCurrentWidget(self.condaWidget)
4887            self.condaWidget.setFocus(Qt.FocusReason.ActiveWindowFocusReason)
4888
4889    def __activateMicroPython(self):
4890        """
4891        Private slot to handle the activation of the MicroPython widget.
4892        """
4893        if self.microPythonWidget is not None:
4894            if self.__layoutType == "Toolboxes":
4895                self.rToolboxDock.show()
4896                self.rToolbox.setCurrentWidget(self.microPythonWidget)
4897            elif self.__layoutType == "Sidebars":
4898                self.rightSidebar.show()
4899                self.rightSidebar.setCurrentWidget(self.microPythonWidget)
4900            self.microPythonWidget.setFocus(
4901                Qt.FocusReason.ActiveWindowFocusReason)
4902
4903    def __toggleWindow(self, w):
4904        """
4905        Private method to toggle a workspace editor window.
4906
4907        @param w reference to the workspace editor window
4908        @return flag indicating, if the window was shown (boolean)
4909        """
4910        if w.isHidden():
4911            w.show()
4912            return True
4913        else:
4914            w.hide()
4915            return False
4916
4917    def __toolsConfiguration(self):
4918        """
4919        Private slot to handle the tools configuration menu entry.
4920        """
4921        from Preferences.ToolConfigurationDialog import ToolConfigurationDialog
4922        dlg = ToolConfigurationDialog(
4923            self.toolGroups[self.currentToolGroup][1], self)
4924        if dlg.exec() == QDialog.DialogCode.Accepted:
4925            self.toolGroups[self.currentToolGroup][1] = dlg.getToollist()
4926            self.__updateExternalToolsActions()
4927
4928    def __toolGroupsConfiguration(self):
4929        """
4930        Private slot to handle the tool groups configuration menu entry.
4931        """
4932        from Preferences.ToolGroupConfigurationDialog import (
4933            ToolGroupConfigurationDialog
4934        )
4935        dlg = ToolGroupConfigurationDialog(
4936            self.toolGroups, self.currentToolGroup, self)
4937        if dlg.exec() == QDialog.DialogCode.Accepted:
4938            self.toolGroups, self.currentToolGroup = dlg.getToolGroups()
4939
4940    def __createUnitTestDialog(self):
4941        """
4942        Private slot to generate the unit test dialog on demand.
4943        """
4944        if self.unittestDialog is None:
4945            from PyUnit.UnittestDialog import UnittestDialog
4946            self.unittestDialog = UnittestDialog(
4947                None, self.debuggerUI.debugServer, self)
4948            self.unittestDialog.unittestFile.connect(
4949                self.viewmanager.setFileLine)
4950            self.unittestDialog.unittestStopped.connect(self.__unittestStopped)
4951
4952    def __unittestStopped(self):
4953        """
4954        Private slot to handle the end of a unit test run.
4955        """
4956        self.utRerunFailedAct.setEnabled(self.unittestDialog.hasFailedTests())
4957        self.utRestartAct.setEnabled(True)
4958
4959    def __unittest(self):
4960        """
4961        Private slot for displaying the unittest dialog.
4962        """
4963        self.__createUnitTestDialog()
4964        self.unittestDialog.show()
4965        self.unittestDialog.raise_()
4966
4967    @pyqtSlot()
4968    @pyqtSlot(str)
4969    def __unittestScript(self, prog=None):
4970        """
4971        Private slot for displaying the unittest dialog and run the current
4972        script.
4973
4974        @param prog the python program to be opened
4975        """
4976        if prog is None:
4977            aw = self.viewmanager.activeWindow()
4978            fn = aw.getFileName()
4979            tfn = Utilities.getTestFileName(fn)
4980            if os.path.exists(tfn):
4981                prog = tfn
4982            else:
4983                prog = fn
4984
4985        self.__unittest()
4986        self.unittestDialog.setProjectMode(False)
4987        self.unittestDialog.insertProg(prog)
4988        self.utRestartAct.setEnabled(False)
4989        self.utRerunFailedAct.setEnabled(False)
4990
4991    def __unittestProject(self):
4992        """
4993        Private slot for displaying the unittest dialog and run the current
4994        project.
4995        """
4996        prog = None
4997        fn = self.project.getMainScript(True)
4998        if fn:
4999            tfn = Utilities.getTestFileName(fn)
5000            if os.path.exists(tfn):
5001                prog = tfn
5002            else:
5003                prog = fn
5004
5005        self.__unittest()
5006        self.unittestDialog.setProjectMode(True)
5007        self.unittestDialog.insertProg(prog)
5008        self.utRestartAct.setEnabled(False)
5009        self.utRerunFailedAct.setEnabled(False)
5010
5011    def __unittestRestart(self):
5012        """
5013        Private slot to display the unittest dialog and rerun the last
5014        unit test.
5015        """
5016        self.__unittest()
5017        self.unittestDialog.startTests()
5018
5019    def __unittestRerunFailed(self):
5020        """
5021        Private slot to display the unittest dialog and rerun all failed tests
5022        of the last run.
5023        """
5024        self.__unittest()
5025        self.unittestDialog.startTests(failedOnly=True)
5026
5027    @pyqtSlot()
5028    @pyqtSlot(str)
5029    def __designer(self, fn=None):
5030        """
5031        Private slot to start the Qt-Designer executable.
5032
5033        @param fn filename of the form to be opened
5034        @type str
5035        """
5036        args = []
5037        if fn is not None:
5038            try:
5039                if os.path.isfile(fn) and os.path.getsize(fn):
5040                    args.append(fn)
5041                else:
5042                    E5MessageBox.critical(
5043                        self,
5044                        self.tr('Problem'),
5045                        self.tr(
5046                            '<p>The file <b>{0}</b> does not exist or'
5047                            ' is zero length.</p>')
5048                        .format(fn))
5049                    return
5050            except OSError:
5051                E5MessageBox.critical(
5052                    self,
5053                    self.tr('Problem'),
5054                    self.tr(
5055                        '<p>The file <b>{0}</b> does not exist or'
5056                        ' is zero length.</p>')
5057                    .format(fn))
5058                return
5059
5060        if Utilities.isMacPlatform():
5061            designer, args = Utilities.prepareQtMacBundle(
5062                "designer", args)
5063        else:
5064            designer = os.path.join(
5065                Utilities.getQtBinariesPath(),
5066                Utilities.generateQtToolName("designer"))
5067            if Utilities.isWindowsPlatform():
5068                designer += '.exe'
5069
5070        if designer:
5071            proc = QProcess()
5072            if not proc.startDetached(designer, args):
5073                E5MessageBox.critical(
5074                    self,
5075                    self.tr('Process Generation Error'),
5076                    self.tr(
5077                        '<p>Could not start Qt-Designer.<br>'
5078                        'Ensure that it is available as <b>{0}</b>.</p>'
5079                    ).format(designer)
5080                )
5081        else:
5082            E5MessageBox.critical(
5083                self,
5084                self.tr('Process Generation Error'),
5085                self.tr(
5086                    '<p>Could not find the Qt-Designer executable.<br>'
5087                    'Ensure that it is installed and optionally configured on'
5088                    ' the Qt configuration page.</p>'
5089                )
5090            )
5091
5092    @pyqtSlot()
5093    @pyqtSlot(str)
5094    def __linguist(self, fn=None):
5095        """
5096        Private slot to start the Qt-Linguist executable.
5097
5098        @param fn filename of the translation file to be opened
5099        @type str
5100        """
5101        args = []
5102        if fn is not None:
5103            fn = fn.replace('.qm', '.ts')
5104            try:
5105                if (
5106                    os.path.isfile(fn) and
5107                    os.path.getsize(fn) and
5108                    fn not in args
5109                ):
5110                    args.append(fn)
5111                else:
5112                    E5MessageBox.critical(
5113                        self,
5114                        self.tr('Problem'),
5115                        self.tr(
5116                            '<p>The file <b>{0}</b> does not exist or'
5117                            ' is zero length.</p>')
5118                        .format(fn))
5119                    return
5120            except OSError:
5121                E5MessageBox.critical(
5122                    self,
5123                    self.tr('Problem'),
5124                    self.tr(
5125                        '<p>The file <b>{0}</b> does not exist or'
5126                        ' is zero length.</p>')
5127                    .format(fn))
5128                return
5129
5130        if Utilities.isMacPlatform():
5131            linguist, args = Utilities.prepareQtMacBundle(
5132                "linguist", args)
5133        else:
5134            linguist = os.path.join(
5135                Utilities.getQtBinariesPath(),
5136                Utilities.generateQtToolName("linguist"))
5137            if Utilities.isWindowsPlatform():
5138                linguist += '.exe'
5139
5140        if linguist:
5141            proc = QProcess()
5142            if not proc.startDetached(linguist, args):
5143                E5MessageBox.critical(
5144                    self,
5145                    self.tr('Process Generation Error'),
5146                    self.tr(
5147                        '<p>Could not start Qt-Linguist.<br>'
5148                        'Ensure that it is available as <b>{0}</b>.</p>'
5149                    ).format(linguist)
5150                )
5151        else:
5152            E5MessageBox.critical(
5153                self,
5154                self.tr('Process Generation Error'),
5155                self.tr(
5156                    '<p>Could not find the Qt-Linguist executable.<br>'
5157                    'Ensure that it is installed and optionally configured on'
5158                    ' the Qt configuration page.</p>'
5159                )
5160            )
5161
5162    def __assistant(self, home=None):
5163        """
5164        Private slot to start the Qt-Assistant executable.
5165
5166        @param home full pathname of a file to display
5167        @type str
5168        """
5169        args = []
5170        if home:
5171            args.append('-showUrl')
5172            args.append(home)
5173
5174        if Utilities.isMacPlatform():
5175            assistant, args = Utilities.prepareQtMacBundle(
5176                "assistant", args)
5177        else:
5178            assistant = os.path.join(
5179                Utilities.getQtBinariesPath(),
5180                Utilities.generateQtToolName("assistant"))
5181            if Utilities.isWindowsPlatform():
5182                assistant += '.exe'
5183
5184        if assistant:
5185            proc = QProcess()
5186            if not proc.startDetached(assistant, args):
5187                E5MessageBox.critical(
5188                    self,
5189                    self.tr('Process Generation Error'),
5190                    self.tr(
5191                        '<p>Could not start Qt-Assistant.<br>'
5192                        'Ensure that it is available as <b>{0}</b>.</p>'
5193                    ).format(assistant)
5194                )
5195        else:
5196            E5MessageBox.critical(
5197                self,
5198                self.tr('Process Generation Error'),
5199                self.tr(
5200                    '<p>Could not find the Qt-Assistant executable.<br>'
5201                    'Ensure that it is installed and optionally configured on'
5202                    ' the Qt configuration page.</p>'
5203                )
5204            )
5205
5206    def __startWebBrowser(self):
5207        """
5208        Private slot to start the eric web browser.
5209        """
5210        self.launchHelpViewer("")
5211
5212    def __customViewer(self, home=None):
5213        """
5214        Private slot to start a custom viewer.
5215
5216        @param home full pathname of a file to display (string)
5217        """
5218        customViewer = Preferences.getHelp("CustomViewer")
5219        if not customViewer:
5220            E5MessageBox.information(
5221                self,
5222                self.tr("Help"),
5223                self.tr(
5224                    """Currently no custom viewer is selected."""
5225                    """ Please use the preferences dialog to specify one."""))
5226            return
5227
5228        proc = QProcess()
5229        args = []
5230        if home:
5231            args.append(home)
5232
5233        if not proc.startDetached(customViewer, args):
5234            E5MessageBox.critical(
5235                self,
5236                self.tr('Process Generation Error'),
5237                self.tr(
5238                    '<p>Could not start custom viewer.<br>'
5239                    'Ensure that it is available as <b>{0}</b>.</p>'
5240                ).format(customViewer))
5241
5242    def __chmViewer(self, home=None):
5243        """
5244        Private slot to start the win help viewer to show *.chm files.
5245
5246        @param home full pathname of a file to display (string)
5247        """
5248        if home:
5249            proc = QProcess()
5250            args = []
5251            args.append(home)
5252
5253            if not proc.startDetached("hh", args):
5254                E5MessageBox.critical(
5255                    self,
5256                    self.tr('Process Generation Error'),
5257                    self.tr(
5258                        '<p>Could not start the help viewer.<br>'
5259                        'Ensure that it is available as <b>hh</b>.</p>'
5260                    ))
5261
5262    @pyqtSlot()
5263    @pyqtSlot(str)
5264    def __UIPreviewer(self, fn=None):
5265        """
5266        Private slot to start the UI Previewer executable.
5267
5268        @param fn filename of the form to be previewed (string)
5269        """
5270        proc = QProcess()
5271
5272        viewer = os.path.join(getConfig("ericDir"), "eric6_uipreviewer.py")
5273
5274        args = []
5275        args.append(viewer)
5276
5277        if fn is not None:
5278            try:
5279                if os.path.isfile(fn) and os.path.getsize(fn):
5280                    args.append(fn)
5281                else:
5282                    E5MessageBox.critical(
5283                        self,
5284                        self.tr('Problem'),
5285                        self.tr(
5286                            '<p>The file <b>{0}</b> does not exist or'
5287                            ' is zero length.</p>')
5288                        .format(fn))
5289                    return
5290            except OSError:
5291                E5MessageBox.critical(
5292                    self,
5293                    self.tr('Problem'),
5294                    self.tr(
5295                        '<p>The file <b>{0}</b> does not exist or'
5296                        ' is zero length.</p>')
5297                    .format(fn))
5298                return
5299
5300        if (
5301            not os.path.isfile(viewer) or
5302            not proc.startDetached(sys.executable, args)
5303        ):
5304            E5MessageBox.critical(
5305                self,
5306                self.tr('Process Generation Error'),
5307                self.tr(
5308                    '<p>Could not start UI Previewer.<br>'
5309                    'Ensure that it is available as <b>{0}</b>.</p>'
5310                ).format(viewer))
5311
5312    @pyqtSlot()
5313    @pyqtSlot(str)
5314    @pyqtSlot(str, bool)
5315    def __TRPreviewer(self, fileNames=None, ignore=False):
5316        """
5317        Private slot to start the Translation Previewer executable.
5318
5319        @param fileNames filenames of forms and/or translations to be previewed
5320            (list of strings)
5321        @param ignore flag indicating non existing files should be ignored
5322            (boolean)
5323        """
5324        proc = QProcess()
5325
5326        viewer = os.path.join(getConfig("ericDir"), "eric6_trpreviewer.py")
5327
5328        args = []
5329        args.append(viewer)
5330
5331        if fileNames is not None:
5332            for fn in fileNames:
5333                try:
5334                    if os.path.isfile(fn) and os.path.getsize(fn):
5335                        args.append(fn)
5336                    else:
5337                        if not ignore:
5338                            E5MessageBox.critical(
5339                                self,
5340                                self.tr('Problem'),
5341                                self.tr(
5342                                    '<p>The file <b>{0}</b> does not exist or'
5343                                    ' is zero length.</p>')
5344                                .format(fn))
5345                            return
5346                except OSError:
5347                    if not ignore:
5348                        E5MessageBox.critical(
5349                            self,
5350                            self.tr('Problem'),
5351                            self.tr(
5352                                '<p>The file <b>{0}</b> does not exist or'
5353                                ' is zero length.</p>')
5354                            .format(fn))
5355                        return
5356
5357        if (
5358            not os.path.isfile(viewer) or
5359            not proc.startDetached(sys.executable, args)
5360        ):
5361            E5MessageBox.critical(
5362                self,
5363                self.tr('Process Generation Error'),
5364                self.tr(
5365                    '<p>Could not start Translation Previewer.<br>'
5366                    'Ensure that it is available as <b>{0}</b>.</p>'
5367                ).format(viewer))
5368
5369    def __sqlBrowser(self):
5370        """
5371        Private slot to start the SQL browser tool.
5372        """
5373        proc = QProcess()
5374
5375        browser = os.path.join(getConfig("ericDir"), "eric6_sqlbrowser.py")
5376
5377        args = []
5378        args.append(browser)
5379
5380        if (
5381            not os.path.isfile(browser) or
5382            not proc.startDetached(sys.executable, args)
5383        ):
5384            E5MessageBox.critical(
5385                self,
5386                self.tr('Process Generation Error'),
5387                self.tr(
5388                    '<p>Could not start SQL Browser.<br>'
5389                    'Ensure that it is available as <b>{0}</b>.</p>'
5390                ).format(browser))
5391
5392    @pyqtSlot()
5393    @pyqtSlot(str)
5394    def __openHexEditor(self, fn=""):
5395        """
5396        Private slot to open the hex editor window.
5397
5398        @param fn filename of the file to show (string)
5399        """
5400        from HexEdit.HexEditMainWindow import HexEditMainWindow
5401        dlg = HexEditMainWindow(fn, self, fromEric=True, project=self.project)
5402        dlg.show()
5403
5404    @pyqtSlot()
5405    @pyqtSlot(str)
5406    def __editPixmap(self, fn=""):
5407        """
5408        Private slot to show a pixmap in a dialog.
5409
5410        @param fn filename of the file to show (string)
5411        """
5412        from IconEditor.IconEditorWindow import IconEditorWindow
5413        dlg = IconEditorWindow(fn, self, fromEric=True, project=self.project)
5414        dlg.show()
5415
5416    @pyqtSlot()
5417    @pyqtSlot(str)
5418    def __showPixmap(self, fn):
5419        """
5420        Private slot to show a pixmap in a dialog.
5421
5422        @param fn filename of the file to show (string)
5423        """
5424        from Graphics.PixmapDiagram import PixmapDiagram
5425        dlg = PixmapDiagram(fn, self)
5426        if dlg.getStatus():
5427            dlg.show()
5428
5429    @pyqtSlot()
5430    @pyqtSlot(str)
5431    def __showSvg(self, fn):
5432        """
5433        Private slot to show a SVG file in a dialog.
5434
5435        @param fn filename of the file to show (string)
5436        """
5437        from Graphics.SvgDiagram import SvgDiagram
5438        dlg = SvgDiagram(fn, self)
5439        dlg.show()
5440
5441    @pyqtSlot(str)
5442    def __showUml(self, fn):
5443        """
5444        Private slot to show an eric graphics file in a dialog.
5445
5446        @param fn name of the file to be shown
5447        @type str
5448        """
5449        from Graphics.UMLDialog import UMLDialog, UMLDialogType
5450        dlg = UMLDialog(UMLDialogType.NO_DIAGRAM, self.project, parent=self)
5451        if dlg.load(fn):
5452            dlg.show(fromFile=True)
5453
5454    def __snapshot(self):
5455        """
5456        Private slot to start the snapshot tool.
5457        """
5458        proc = QProcess()
5459
5460        snap = os.path.join(getConfig("ericDir"), "eric6_snap.py")
5461
5462        args = []
5463        args.append(snap)
5464
5465        if (
5466            not os.path.isfile(snap) or
5467            not proc.startDetached(sys.executable, args)
5468        ):
5469            E5MessageBox.critical(
5470                self,
5471                self.tr('Process Generation Error'),
5472                self.tr(
5473                    '<p>Could not start Snapshot tool.<br>'
5474                    'Ensure that it is available as <b>{0}</b>.</p>'
5475                ).format(snap))
5476
5477    def __toolActionTriggered(self, act):
5478        """
5479        Private slot called by external tools toolbar actions.
5480
5481        @param act reference to the action that triggered the slot
5482        @type QAction
5483        """
5484        toolGroupName, toolMenuText = act.objectName().split('@@', 1)
5485        for toolGroup in self.toolGroups:
5486            if toolGroup[0] == toolGroupName:
5487                for tool in toolGroup[1]:
5488                    if tool['menutext'] == toolMenuText:
5489                        self.__startToolProcess(tool)
5490                        return
5491
5492                E5MessageBox.information(
5493                    self,
5494                    self.tr("External Tools"),
5495                    self.tr(
5496                        """No tool entry found for external tool '{0}' """
5497                        """in tool group '{1}'.""")
5498                    .format(toolMenuText, toolGroupName))
5499                return
5500
5501        E5MessageBox.information(
5502            self,
5503            self.tr("External Tools"),
5504            self.tr("""No toolgroup entry '{0}' found.""")
5505            .format(toolGroupName)
5506        )
5507
5508    def __toolExecute(self, act):
5509        """
5510        Private slot to execute a particular tool.
5511
5512        @param act reference to the action that was triggered (QAction)
5513        """
5514        if self.toolGroupsMenuTriggered:
5515            # ignore actions triggered from the select tool group submenu
5516            self.toolGroupsMenuTriggered = False
5517            return
5518
5519        if self.currentToolGroup < 0:
5520            # it was an action not to be handled here
5521            return
5522
5523        idx = act.data()
5524        if idx is not None and idx >= 0:
5525            tool = self.toolGroups[self.currentToolGroup][1][idx]
5526            self.__startToolProcess(tool)
5527
5528    def __startToolProcess(self, tool):
5529        """
5530        Private slot to start an external tool process.
5531
5532        @param tool list of tool entries
5533        """
5534        proc = QProcess()
5535        procData = (None,)
5536        program = tool['executable']
5537        args = []
5538        argv = Utilities.parseOptionString(tool['arguments'])
5539        args.extend(argv)
5540        t = self.tr("Starting process '{0} {1}'.\n"
5541                    ).format(program, tool['arguments'])
5542        self.appendToStdout(t)
5543
5544        proc.finished.connect(self.__toolFinished)
5545        if tool['redirect'] != 'no':
5546            proc.readyReadStandardOutput.connect(self.__processToolStdout)
5547            proc.readyReadStandardError.connect(self.__processToolStderr)
5548            if tool['redirect'] in ["insert", "replaceSelection"]:
5549                aw = self.viewmanager.activeWindow()
5550                procData = (aw, tool['redirect'], [])
5551                if aw is not None:
5552                    aw.beginUndoAction()
5553
5554        proc.start(program, args)
5555        if not proc.waitForStarted():
5556            E5MessageBox.critical(
5557                self,
5558                self.tr('Process Generation Error'),
5559                self.tr(
5560                    '<p>Could not start the tool entry <b>{0}</b>.<br>'
5561                    'Ensure that it is available as <b>{1}</b>.</p>')
5562                .format(tool['menutext'], tool['executable']))
5563        else:
5564            self.toolProcs.append((program, proc, procData))
5565            if tool['redirect'] == 'no':
5566                proc.closeReadChannel(QProcess.ProcessChannel.StandardOutput)
5567                proc.closeReadChannel(QProcess.ProcessChannel.StandardError)
5568                proc.closeWriteChannel()
5569
5570    def __processToolStdout(self):
5571        """
5572        Private slot to handle the readyReadStdout signal of a tool process.
5573        """
5574        ioEncoding = Preferences.getSystem("IOEncoding")
5575
5576        # loop through all running tool processes
5577        for program, toolProc, toolProcData in self.toolProcs:
5578            toolProc.setReadChannel(QProcess.ProcessChannel.StandardOutput)
5579
5580            if (
5581                toolProcData[0] is None or
5582                toolProcData[1] not in ["insert", "replaceSelection"]
5583            ):
5584                # not connected to an editor or wrong mode
5585                while toolProc.canReadLine():
5586                    output = str(toolProc.readLine(), ioEncoding, 'replace')
5587                    s = "{0} - {1}".format(program, output)
5588                    self.appendToStdout(s)
5589            else:
5590                if toolProcData[1] == "insert":
5591                    text = str(toolProc.readAll(), ioEncoding, 'replace')
5592                    toolProcData[0].insert(text)
5593                elif toolProcData[1] == "replaceSelection":
5594                    text = str(toolProc.readAll(), ioEncoding, 'replace')
5595                    toolProcData[2].append(text)
5596
5597    def __processToolStderr(self):
5598        """
5599        Private slot to handle the readyReadStderr signal of a tool process.
5600        """
5601        ioEncoding = Preferences.getSystem("IOEncoding")
5602
5603        # loop through all running tool processes
5604        for program, toolProc, _toolProcData in self.toolProcs:
5605            toolProc.setReadChannel(QProcess.ProcessChannel.StandardError)
5606
5607            while toolProc.canReadLine():
5608                error = str(toolProc.readLine(), ioEncoding, 'replace')
5609                s = "{0} - {1}".format(program, error)
5610                self.appendToStderr(s)
5611
5612    def __toolFinished(self, exitCode, exitStatus):
5613        """
5614        Private slot to handle the finished signal of a tool process.
5615
5616        @param exitCode exit code of the process (integer)
5617        @param exitStatus exit status of the process (QProcess.ExitStatus)
5618        """
5619        exitedProcs = []
5620
5621        # loop through all running tool processes
5622        for program, toolProc, toolProcData in self.toolProcs:
5623            if toolProc.state() == QProcess.ProcessState.NotRunning:
5624                exitedProcs.append((program, toolProc, toolProcData))
5625                if toolProcData[0] is not None:
5626                    if toolProcData[1] == "replaceSelection":
5627                        text = ''.join(toolProcData[2])
5628                        toolProcData[0].replace(text)
5629                    toolProcData[0].endUndoAction()
5630
5631        # now delete the exited procs from the list of running processes
5632        for proc in exitedProcs:
5633            self.toolProcs.remove(proc)
5634            t = self.tr("Process '{0}' has exited.\n").format(proc[0])
5635            self.appendToStdout(t)
5636
5637    def __showPythonDoc(self):
5638        """
5639        Private slot to show the Python 3 documentation.
5640        """
5641        pythonDocDir = Preferences.getHelp("PythonDocDir")
5642        if not pythonDocDir:
5643            if Utilities.isWindowsPlatform():
5644                venvName = Preferences.getDebugger("Python3VirtualEnv")
5645                interpreter = (
5646                    e5App().getObject("VirtualEnvManager")
5647                    .getVirtualenvInterpreter(venvName)
5648                )
5649                if interpreter:
5650                    default = os.path.join(os.path.dirname(interpreter), "doc")
5651                else:
5652                    default = ""
5653                pythonDocDir = Utilities.getEnvironmentEntry(
5654                    "PYTHON3DOCDIR", default)
5655            else:
5656                pythonDocDir = Utilities.getEnvironmentEntry(
5657                    "PYTHON3DOCDIR",
5658                    '/usr/share/doc/packages/python3/html')
5659        if not pythonDocDir.startswith(("http://", "https://", "qthelp://")):
5660            if pythonDocDir.startswith("file://"):
5661                pythonDocDir = pythonDocDir[7:]
5662            if not os.path.splitext(pythonDocDir)[1]:
5663                home = Utilities.normjoinpath(pythonDocDir, 'index.html')
5664
5665                if Utilities.isWindowsPlatform() and not os.path.exists(home):
5666                    pyversion = sys.hexversion >> 16
5667                    vers = "{0:d}{1:d}".format((pyversion >> 8) & 0xff,
5668                                               pyversion & 0xff)
5669                    home = os.path.join(
5670                        pythonDocDir, "python{0}.chm".format(vers))
5671            else:
5672                home = pythonDocDir
5673
5674            if not os.path.exists(home):
5675                E5MessageBox.warning(
5676                    self,
5677                    self.tr("Documentation Missing"),
5678                    self.tr("""<p>The documentation starting point"""
5679                            """ "<b>{0}</b>" could not be found.</p>""")
5680                    .format(home))
5681                return
5682
5683            if not home.endswith(".chm"):
5684                if Utilities.isWindowsPlatform():
5685                    home = "file:///" + Utilities.fromNativeSeparators(home)
5686                else:
5687                    home = "file://" + home
5688        else:
5689            home = pythonDocDir
5690
5691        if home.endswith(".chm"):
5692            self.__chmViewer(home)
5693        else:
5694            hvType = Preferences.getWebBrowser("HelpViewerType")
5695            if hvType == 1:
5696                self.launchHelpViewer(home)
5697            elif hvType == 2:
5698                if home.startswith("qthelp://"):
5699                    self.__assistant(home)
5700                else:
5701                    self.__webBrowser(home)
5702            elif hvType == 3:
5703                self.__webBrowser(home)
5704            else:
5705                self.__customViewer(home)
5706
5707    def __showQtDoc(self, version):
5708        """
5709        Private method to show the Qt documentation.
5710
5711        @param version Qt version to show documentation for
5712        @type int
5713        """
5714        if version in [5, 6]:
5715            qtDocDir = Preferences.getQtDocDir(version)
5716        else:
5717            return
5718
5719        if qtDocDir.startswith("qthelp://"):
5720            if not os.path.splitext(qtDocDir)[1]:
5721                home = qtDocDir + "/index.html"
5722            else:
5723                home = qtDocDir
5724        elif qtDocDir.startswith(("http://", "https://")):
5725            home = qtDocDir
5726        else:
5727            if qtDocDir.startswith("file://"):
5728                qtDocDir = qtDocDir[7:]
5729            if not os.path.splitext(qtDocDir)[1]:
5730                home = Utilities.normjoinpath(qtDocDir, 'index.html')
5731            else:
5732                home = qtDocDir
5733
5734            if not os.path.exists(home):
5735                E5MessageBox.warning(
5736                    self,
5737                    self.tr("Documentation Missing"),
5738                    self.tr("""<p>The documentation starting point"""
5739                            """ "<b>{0}</b>" could not be found.</p>""")
5740                    .format(home))
5741                return
5742
5743            if Utilities.isWindowsPlatform():
5744                home = "file:///" + Utilities.fromNativeSeparators(home)
5745            else:
5746                home = "file://" + home
5747
5748        hvType = Preferences.getWebBrowser("HelpViewerType")
5749        if hvType == 1:
5750            self.launchHelpViewer(home)
5751        elif hvType == 2:
5752            if home.startswith("qthelp://"):
5753                self.__assistant(home)
5754            else:
5755                self.__webBrowser(home)
5756        elif hvType == 3:
5757            self.__webBrowser(home)
5758        else:
5759            self.__customViewer(home)
5760
5761    def __showPyQtDoc(self, variant=5):
5762        """
5763        Private slot to show the PyQt5/6 documentation.
5764
5765        @param variant PyQt variant to show documentation for (5 or 6)
5766        @type int or str
5767        """
5768        pyqtDocDir = Preferences.getHelp("PyQt{0}DocDir".format(variant))
5769        if not pyqtDocDir:
5770            pyqtDocDir = Utilities.getEnvironmentEntry(
5771                "PYQT{0}DOCDIR".format(variant), None)
5772
5773        if not pyqtDocDir:
5774            E5MessageBox.warning(
5775                self,
5776                self.tr("Documentation"),
5777                self.tr("""<p>The PyQt{0} documentation starting point"""
5778                        """ has not been configured.</p>""").format(variant))
5779            return
5780
5781        if not pyqtDocDir.startswith(("http://", "https://", "qthelp://")):
5782            home = ""
5783            if pyqtDocDir:
5784                if pyqtDocDir.startswith("file://"):
5785                    pyqtDocDir = pyqtDocDir[7:]
5786                if not os.path.splitext(pyqtDocDir)[1]:
5787                    possibleHomes = [
5788                        Utilities.normjoinpath(
5789                            pyqtDocDir, 'index.html'),
5790                        Utilities.normjoinpath(
5791                            pyqtDocDir, 'class_reference.html'),
5792                    ]
5793                    for possibleHome in possibleHomes:
5794                        if os.path.exists(possibleHome):
5795                            home = possibleHome
5796                            break
5797                else:
5798                    home = pyqtDocDir
5799
5800            if not home or not os.path.exists(home):
5801                E5MessageBox.warning(
5802                    self,
5803                    self.tr("Documentation Missing"),
5804                    self.tr("""<p>The documentation starting point"""
5805                            """ "<b>{0}</b>" could not be found.</p>""")
5806                    .format(home))
5807                return
5808
5809            if Utilities.isWindowsPlatform():
5810                home = "file:///" + Utilities.fromNativeSeparators(home)
5811            else:
5812                home = "file://" + home
5813        else:
5814            home = pyqtDocDir
5815
5816        hvType = Preferences.getWebBrowser("HelpViewerType")
5817        if hvType == 1:
5818            self.launchHelpViewer(home)
5819        elif hvType == 2:
5820            if home.startswith("qthelp://"):
5821                self.__assistant(home)
5822            else:
5823                self.__webBrowser(home)
5824        elif hvType == 3:
5825            self.__webBrowser(home)
5826        else:
5827            self.__customViewer(home)
5828
5829    def __showEricDoc(self):
5830        """
5831        Private slot to show the Eric documentation.
5832        """
5833        home = Preferences.getHelp("EricDocDir")
5834        if not home:
5835            home = Utilities.normjoinpath(
5836                getConfig('ericDocDir'), "Source", "index.html")
5837
5838        if not home.startswith(("http://", "https://", "qthelp://")):
5839            if not os.path.exists(home):
5840                E5MessageBox.warning(
5841                    self,
5842                    self.tr("Documentation Missing"),
5843                    self.tr("""<p>The documentation starting point"""
5844                            """ "<b>{0}</b>" could not be found.</p>""")
5845                    .format(home))
5846                return
5847
5848            if Utilities.isWindowsPlatform():
5849                home = "file:///" + Utilities.fromNativeSeparators(home)
5850            else:
5851                home = "file://" + home
5852
5853        hvType = Preferences.getWebBrowser("HelpViewerType")
5854        if hvType == 1:
5855            self.launchHelpViewer(home)
5856        elif hvType == 2:
5857            if home.startswith("qthelp://"):
5858                self.__assistant(home)
5859            else:
5860                self.__webBrowser(home)
5861        elif hvType == 3:
5862            self.__webBrowser(home)
5863        else:
5864            self.__customViewer(home)
5865
5866    def __showPySideDoc(self, variant=2):
5867        """
5868        Private slot to show the PySide2/PySide6 documentation.
5869
5870        @param variant PySide variant (2 or 6)
5871        @type int or str
5872        """
5873        pysideDocDir = Preferences.getHelp("PySide{0}DocDir".format(variant))
5874        if not pysideDocDir:
5875            pysideDocDir = Utilities.getEnvironmentEntry(
5876                "PYSIDE{0}DOCDIR".format(variant), None)
5877
5878        if not pysideDocDir:
5879            E5MessageBox.warning(
5880                self,
5881                self.tr("Documentation"),
5882                self.tr("""<p>The PySide{0} documentation starting point"""
5883                        """ has not been configured.</p>""").format(
5884                    variant)
5885            )
5886            return
5887
5888        if not pysideDocDir.startswith(("http://", "https://", "qthelp://")):
5889            if pysideDocDir.startswith("file://"):
5890                pysideDocDir = pysideDocDir[7:]
5891            if not os.path.splitext(pysideDocDir)[1]:
5892                home = Utilities.normjoinpath(pysideDocDir, 'index.html')
5893            else:
5894                home = pysideDocDir
5895            if not os.path.exists(home):
5896                E5MessageBox.warning(
5897                    self,
5898                    self.tr("Documentation Missing"),
5899                    self.tr("""<p>The documentation starting point"""
5900                            """ "<b>{0}</b>" could not be found.</p>""")
5901                    .format(home))
5902                return
5903
5904            if Utilities.isWindowsPlatform():
5905                home = "file:///" + Utilities.fromNativeSeparators(home)
5906            else:
5907                home = "file://" + home
5908        else:
5909            home = pysideDocDir
5910
5911        hvType = Preferences.getWebBrowser("HelpViewerType")
5912        if hvType == 1:
5913            self.launchHelpViewer(home)
5914        elif hvType == 2:
5915            if home.startswith("qthelp://"):
5916                self.__assistant(home)
5917            else:
5918                self.__webBrowser(home)
5919        elif hvType == 3:
5920            self.__webBrowser(home)
5921        else:
5922            self.__customViewer(home)
5923
5924    @pyqtSlot(QUrl)
5925    def handleUrl(self, url):
5926        """
5927        Public slot to handle opening a URL.
5928
5929        @param url URL to be shown
5930        @type QUrl
5931        """
5932        self.launchHelpViewer(url)
5933
5934    def launchHelpViewer(self, home, searchWord=None, useSingle=False):
5935        """
5936        Public slot to start the help viewer/web browser.
5937
5938        @param home filename of file to be shown or URL to be opened
5939        @type str or QUrl
5940        @param searchWord word to search for
5941        @type str
5942        @param useSingle flag indicating to use a single browser window
5943        @type bool
5944        """
5945        if isinstance(home, QUrl):
5946            home = home.toString(QUrl.UrlFormattingOption.None_)
5947
5948        if len(home) > 0:
5949            homeUrl = QUrl(home)
5950            if not homeUrl.scheme():
5951                home = QUrl.fromLocalFile(home).toString()
5952
5953        launchResult = self.__launchExternalWebBrowser(
5954            home, searchWord=searchWord)
5955        if not launchResult:
5956            self.__webBrowser(home)
5957
5958    def __launchExternalWebBrowser(self, home, searchWord=None):
5959        """
5960        Private method to start an external web browser and communicate with
5961        it.
5962
5963        @param home filename of file to be shown or URL to be opened
5964        @type str
5965        @param searchWord word to search for
5966        @type str
5967        @return flag indicating a successful launch
5968        @rtype bool
5969        """
5970        clientArgs = []
5971        if searchWord:
5972            clientArgs.append("--search={0}".format(searchWord))
5973
5974        if self.__webBrowserProcess is None:
5975            webBrowsers = [
5976                os.path.join(
5977                    os.path.dirname(__file__), "..", "eric6_browser.py"),
5978                # QtWebEngine based web browser
5979            ]
5980            process = QProcess()
5981            for browser in webBrowsers:
5982                args = [
5983                    browser,
5984                    "--quiet",
5985                    "--qthelp",
5986                    "--single",
5987                    "--name={0}".format(self.__webBrowserSAName),
5988                    home
5989                ]
5990                process.start(sys.executable, args)
5991                if not process.waitForStarted():
5992                    E5MessageBox.warning(
5993                        self,
5994                        self.tr("Start Web Browser"),
5995                        self.tr("""The eric web browser could not be"""
5996                                """ started."""))
5997                    return False
5998
5999                res = self.__connectToWebBrowser(process)
6000                if res == 1:
6001                    # connection unsuccessful
6002                    return False
6003                elif res == 0:
6004                    # successful
6005                    break
6006                elif res == -1:
6007                    # web browser did not start
6008                    continue
6009            else:
6010                return False
6011
6012            process.finished.connect(self.__webBrowserFinished)
6013            self.__webBrowserProcess = process
6014
6015        else:
6016            clientArgs.append("--newtab={0}".format(home))
6017
6018        if clientArgs and self.__webBrowserClient:
6019            self.__webBrowserClient.processArgs(clientArgs, disconnect=False)
6020
6021        return True
6022
6023    def __connectToWebBrowser(self, process):
6024        """
6025        Private method to connect to a started web browser.
6026
6027        @param process reference to the started web browser process
6028        @type QProcess
6029        @return error indication (1 = connection not possible, 0 = ok,
6030            -1 = server exited with an error code)
6031        @rtype int
6032        """
6033        from WebBrowser.WebBrowserSingleApplication import (
6034            WebBrowserSingleApplicationClient
6035        )
6036
6037        webBrowserClient = WebBrowserSingleApplicationClient(
6038            self.__webBrowserSAName)
6039        connectCount = 30
6040        while connectCount:
6041            res = webBrowserClient.connect()
6042            if res != 0:
6043                break
6044            else:
6045                connectCount -= 1
6046                QThread.msleep(1000)
6047                QApplication.processEvents()
6048            if (
6049                process.state() == QProcess.ProcessState.NotRunning and
6050                process.exitStatus() == QProcess.ExitStatus.NormalExit and
6051                process.exitCode() == 100
6052            ):
6053                # Process exited prematurely due to missing pre-requisites
6054                return -1
6055        if res <= 0:
6056            E5MessageBox.warning(
6057                self,
6058                self.tr("Start Web Browser"),
6059                self.tr("""<p>The eric web browser is not started.</p>"""
6060                        """<p>Reason: {0}</p>""").format(
6061                    webBrowserClient.errstr())
6062            )
6063            return 1
6064
6065        self.__webBrowserClient = webBrowserClient
6066        return 0
6067
6068    def __webBrowserFinished(self):
6069        """
6070        Private slot handling the end of the external web browser process.
6071        """
6072        self.__webBrowserProcess = None
6073        self.__webBrowserClient = None
6074
6075    def __webBrowserShutdown(self):
6076        """
6077        Private method to shut down the web browser.
6078        """
6079        self.__webBrowserClient.processArgs(["--shutdown"], disconnect=False)
6080
6081    def __helpViewer(self):
6082        """
6083        Private slot to start an empty help viewer/web browser.
6084        """
6085        searchWord = self.viewmanager.textForFind(False)
6086        if searchWord == "":
6087            searchWord = None
6088
6089        self.launchHelpViewer("", searchWord=searchWord)
6090
6091    def __webBrowser(self, home=""):
6092        """
6093        Private slot to start the eric web browser.
6094
6095        @param home full pathname of a file to display (string)
6096        """
6097        started = QDesktopServices.openUrl(QUrl(home))
6098        if not started:
6099            E5MessageBox.critical(
6100                self,
6101                self.tr('Open Browser'),
6102                self.tr('Could not start a web browser'))
6103
6104    @pyqtSlot()
6105    @pyqtSlot(str)
6106    def showPreferences(self, pageName=None):
6107        """
6108        Public slot to set the preferences.
6109
6110        @param pageName name of the configuration page to show (string)
6111        """
6112        if self.__configurationDialog is None:
6113            # only one invocation at a time is allowed
6114            from Preferences.ConfigurationDialog import ConfigurationDialog
6115            self.__configurationDialog = ConfigurationDialog(
6116                self, 'Configuration',
6117                expandedEntries=self.__expandedConfigurationEntries,
6118            )
6119            self.__configurationDialog.preferencesChanged.connect(
6120                self.__preferencesChanged)
6121            self.__configurationDialog.masterPasswordChanged.connect(
6122                self.__masterPasswordChanged)
6123            self.__configurationDialog.show()
6124            if pageName is not None:
6125                self.__configurationDialog.showConfigurationPageByName(
6126                    pageName)
6127            elif self.__lastConfigurationPageName:
6128                self.__configurationDialog.showConfigurationPageByName(
6129                    self.__lastConfigurationPageName)
6130            else:
6131                self.__configurationDialog.showConfigurationPageByName("empty")
6132            self.__configurationDialog.exec()
6133            QApplication.processEvents()
6134            if (
6135                self.__configurationDialog.result() ==
6136                QDialog.DialogCode.Accepted
6137            ):
6138                self.__configurationDialog.setPreferences()
6139                Preferences.syncPreferences()
6140                self.__preferencesChanged()
6141            self.__lastConfigurationPageName = (
6142                self.__configurationDialog.getConfigurationPageName()
6143            )
6144            self.__expandedConfigurationEntries = (
6145                self.__configurationDialog.getExpandedEntries()
6146            )
6147
6148            self.__configurationDialog.deleteLater()
6149            self.__configurationDialog = None
6150
6151    def __exportPreferences(self):
6152        """
6153        Private slot to export the current preferences.
6154        """
6155        Preferences.exportPreferences()
6156
6157    def __importPreferences(self):
6158        """
6159        Private slot to import preferences.
6160        """
6161        Preferences.importPreferences()
6162        self.__preferencesChanged()
6163
6164    def __preferencesChanged(self):
6165        """
6166        Private slot to handle a change of the preferences.
6167        """
6168        self.setStyle(Preferences.getUI("Style"),
6169                      Preferences.getUI("StyleSheet"))
6170
6171        if Preferences.getUI("SingleApplicationMode"):
6172            if self.SAServer is None:
6173                self.SAServer = E5SingleApplicationServer()
6174        else:
6175            if self.SAServer is not None:
6176                self.SAServer.shutdown()
6177                self.SAServer = None
6178        self.newWindowAct.setEnabled(
6179            not Preferences.getUI("SingleApplicationMode"))
6180
6181        self.maxEditorPathLen = Preferences.getUI("CaptionFilenameLength")
6182        self.captionShowsFilename = Preferences.getUI("CaptionShowsFilename")
6183        if not self.captionShowsFilename:
6184            self.__setWindowCaption(editor="")
6185        else:
6186            aw = self.viewmanager.activeWindow()
6187            fn = aw and aw.getFileName() or None
6188            if fn:
6189                self.__setWindowCaption(editor=fn)
6190            else:
6191                self.__setWindowCaption(editor="")
6192
6193        self.__httpAlternatives = Preferences.getUI("VersionsUrls6")
6194        self.performVersionCheck(False)
6195
6196        from QScintilla.SpellChecker import SpellChecker
6197        SpellChecker.setDefaultLanguage(
6198            Preferences.getEditor("SpellCheckingDefaultLanguage"))
6199
6200        if self.__layoutType == "Sidebars":
6201            delay = Preferences.getUI("SidebarDelay")
6202            self.leftSidebar.setDelay(delay)
6203            self.bottomSidebar.setDelay(delay)
6204            self.rightSidebar.setDelay(delay)
6205
6206        if Preferences.getUI("UseSystemProxy"):
6207            QNetworkProxyFactory.setUseSystemConfiguration(True)
6208        else:
6209            self.__proxyFactory = E5NetworkProxyFactory()
6210            QNetworkProxyFactory.setApplicationProxyFactory(
6211                self.__proxyFactory)
6212            QNetworkProxyFactory.setUseSystemConfiguration(False)
6213
6214        from HexEdit.HexEditMainWindow import HexEditMainWindow
6215        for hexEditor in HexEditMainWindow.windows:
6216            hexEditor.preferencesChanged()
6217
6218        # set the keyboard input interval
6219        interval = Preferences.getUI("KeyboardInputInterval")
6220        if interval > 0:
6221            QApplication.setKeyboardInputInterval(interval)
6222        else:
6223            QApplication.setKeyboardInputInterval(-1)
6224
6225        if not self.__disableCrashSession:
6226            if Preferences.getUI("CrashSessionEnabled"):
6227                self.__writeCrashSession()
6228            else:
6229                self.__deleteCrashSession()
6230
6231        self.preferencesChanged.emit()
6232
6233    def __masterPasswordChanged(self, oldPassword, newPassword):
6234        """
6235        Private slot to handle the change of the master password.
6236
6237        @param oldPassword current master password (string)
6238        @param newPassword new master password (string)
6239        """
6240        import Globals
6241
6242        self.masterPasswordChanged.emit(oldPassword, newPassword)
6243        Preferences.convertPasswords(oldPassword, newPassword)
6244        variant = Globals.getWebBrowserSupport()
6245        if variant == "QtWebEngine":
6246            from WebBrowser.Passwords.PasswordManager import (
6247                PasswordManager
6248            )
6249            pwManager = PasswordManager()
6250            pwManager.masterPasswordChanged(oldPassword, newPassword)
6251        Utilities.crypto.changeRememberedMaster(newPassword)
6252
6253    def __reloadAPIs(self):
6254        """
6255        Private slot to reload the api information.
6256        """
6257        self.reloadAPIs.emit()
6258
6259    def __showExternalTools(self):
6260        """
6261        Private slot to display a dialog show a list of external tools used
6262        by eric.
6263        """
6264        if self.programsDialog is None:
6265            from Preferences.ProgramsDialog import ProgramsDialog
6266            self.programsDialog = ProgramsDialog(self)
6267        self.programsDialog.show()
6268
6269    def __configViewProfiles(self):
6270        """
6271        Private slot to configure the various view profiles.
6272        """
6273        from Preferences.ViewProfileDialog import ViewProfileDialog
6274        dlg = ViewProfileDialog(self.__layoutType, self.profiles['edit'][1],
6275                                self.profiles['debug'][1])
6276        if dlg.exec() == QDialog.DialogCode.Accepted:
6277            edit, debug = dlg.getVisibilities()
6278            self.profiles['edit'][1] = edit
6279            self.profiles['debug'][1] = debug
6280            Preferences.setUI("ViewProfiles2", self.profiles)
6281            if self.currentProfile == "edit":
6282                self.__setEditProfile(False)
6283            elif self.currentProfile == "debug":
6284                self.setDebugProfile(False)
6285
6286    def __configToolBars(self):
6287        """
6288        Private slot to configure the various toolbars.
6289        """
6290        from E5Gui.E5ToolBarDialog import E5ToolBarDialog
6291        dlg = E5ToolBarDialog(self.toolbarManager)
6292        if dlg.exec() == QDialog.DialogCode.Accepted:
6293            Preferences.setUI(
6294                "ToolbarManagerState", self.toolbarManager.saveState())
6295
6296    def __configShortcuts(self):
6297        """
6298        Private slot to configure the keyboard shortcuts.
6299        """
6300        if self.shortcutsDialog is None:
6301            from Preferences.ShortcutsDialog import ShortcutsDialog
6302            self.shortcutsDialog = ShortcutsDialog(self)
6303        self.shortcutsDialog.populate()
6304        self.shortcutsDialog.show()
6305
6306    def __exportShortcuts(self):
6307        """
6308        Private slot to export the keyboard shortcuts.
6309        """
6310        fn, selectedFilter = E5FileDialog.getSaveFileNameAndFilter(
6311            None,
6312            self.tr("Export Keyboard Shortcuts"),
6313            "",
6314            self.tr("Keyboard Shortcuts File (*.ekj);;"
6315                    "XML Keyboard Shortcuts File (*.e4k)"),
6316            "",
6317            E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite))
6318
6319        if not fn:
6320            return
6321
6322        ext = QFileInfo(fn).suffix()
6323        if not ext:
6324            ex = selectedFilter.split("(*")[1].split(")")[0]
6325            if ex:
6326                fn += ex
6327
6328        ok = (
6329            E5MessageBox.yesNo(
6330                self,
6331                self.tr("Export Keyboard Shortcuts"),
6332                self.tr("""<p>The keyboard shortcuts file <b>{0}</b> exists"""
6333                        """ already. Overwrite it?</p>""").format(fn))
6334            if os.path.exists(fn) else
6335            True
6336        )
6337
6338        if ok:
6339            from Preferences import Shortcuts
6340            Shortcuts.exportShortcuts(fn)
6341
6342    def __importShortcuts(self):
6343        """
6344        Private slot to import the keyboard shortcuts.
6345        """
6346        fn = E5FileDialog.getOpenFileName(
6347            None,
6348            self.tr("Import Keyboard Shortcuts"),
6349            "",
6350            self.tr("Keyboard Shortcuts File (*.ekj);;"
6351                    "XML Keyboard shortcut file (*.e4k)"))
6352
6353        if fn:
6354            from Preferences import Shortcuts
6355            Shortcuts.importShortcuts(fn)
6356
6357    def __showCertificatesDialog(self):
6358        """
6359        Private slot to show the certificates management dialog.
6360        """
6361        from E5Network.E5SslCertificatesDialog import E5SslCertificatesDialog
6362
6363        dlg = E5SslCertificatesDialog(self)
6364        dlg.exec()
6365
6366    def __clearPrivateData(self):
6367        """
6368        Private slot to clear the private data lists.
6369        """
6370        from .ClearPrivateDataDialog import ClearPrivateDataDialog
6371        dlg = ClearPrivateDataDialog(self)
6372        if dlg.exec() == QDialog.DialogCode.Accepted:
6373            # recent files, recent projects, recent multi  projects,
6374            # debug histories, shell histories
6375            (files, projects, multiProjects, debug, shell, vcs, plugins) = (
6376                dlg.getData()
6377            )
6378            if files:
6379                # clear list of recently opened files
6380                self.viewmanager.clearRecent()
6381            if projects:
6382                # clear list of recently opened projects and other histories
6383                self.project.clearHistories()
6384            if multiProjects:
6385                # clear list of recently opened multi projects
6386                self.multiProject.clearRecent()
6387            if debug:
6388                # clear the various debug histories
6389                self.debuggerUI.clearHistories()
6390            if shell:
6391                # clear the shell histories
6392                self.shell.clearAllHistories()
6393            if vcs:
6394                # clear the VCS related histories
6395                self.pluginManager.clearPluginsPrivateData("version_control")
6396            if plugins:
6397                # clear private data of plug-ins not covered above
6398                self.pluginManager.clearPluginsPrivateData("")
6399
6400            Preferences.syncPreferences()
6401
6402    def __newProject(self):
6403        """
6404        Private slot to handle the NewProject signal.
6405        """
6406        self.__setWindowCaption(project=self.project.name)
6407
6408    def __projectOpened(self):
6409        """
6410        Private slot to handle the projectOpened signal.
6411        """
6412        from Debugger.DebugClientCapabilities import HasUnittest
6413        self.__setWindowCaption(project=self.project.name)
6414        cap = e5App().getObject("DebugServer").getClientCapabilities(
6415            self.project.getProjectLanguage())
6416        self.utProjectAct.setEnabled(cap & HasUnittest)
6417        self.utProjectOpen = cap & HasUnittest
6418
6419    def __projectClosed(self):
6420        """
6421        Private slot to handle the projectClosed signal.
6422        """
6423        self.__setWindowCaption(project="")
6424        self.utProjectAct.setEnabled(False)
6425        if not self.utEditorOpen:
6426            self.utRestartAct.setEnabled(False)
6427            self.utRerunFailedAct.setEnabled(False)
6428        self.utProjectOpen = False
6429
6430    def __programChange(self, fn):
6431        """
6432        Private slot to handle the programChange signal.
6433
6434        This primarily is here to set the currentProg variable.
6435
6436        @param fn filename to be set as current prog (string)
6437        """
6438        # Delete the old program if there was one.
6439        if self.currentProg is not None:
6440            del self.currentProg
6441
6442        self.currentProg = os.path.normpath(fn)
6443
6444    def __lastEditorClosed(self):
6445        """
6446        Private slot to handle the lastEditorClosed signal.
6447        """
6448        self.wizardsMenuAct.setEnabled(False)
6449        self.utScriptAct.setEnabled(False)
6450        self.utEditorOpen = False
6451        if not self.utProjectOpen:
6452            self.utRestartAct.setEnabled(False)
6453            self.utRerunFailedAct.setEnabled(False)
6454        self.__setWindowCaption(editor="")
6455
6456    def __editorOpened(self, fn):
6457        """
6458        Private slot to handle the editorOpened signal.
6459
6460        @param fn filename of the opened editor (string)
6461        """
6462        self.wizardsMenuAct.setEnabled(
6463            len(self.__menus["wizards"].actions()) > 0)
6464
6465        if fn and str(fn) != "None":
6466            dbs = e5App().getObject("DebugServer")
6467            for language in dbs.getSupportedLanguages():
6468                exts = dbs.getExtensions(language)
6469                if fn.endswith(exts):
6470                    from Debugger.DebugClientCapabilities import HasUnittest
6471                    cap = dbs.getClientCapabilities(language)
6472                    self.utScriptAct.setEnabled(cap & HasUnittest)
6473                    self.utEditorOpen = cap & HasUnittest
6474                    return
6475
6476            if self.viewmanager.getOpenEditor(fn).isPyFile():
6477                self.utScriptAct.setEnabled(True)
6478                self.utEditorOpen = True
6479
6480    def __checkActions(self, editor):
6481        """
6482        Private slot to check some actions for their enable/disable status.
6483
6484        @param editor editor window
6485        """
6486        fn = editor.getFileName() if editor else None
6487
6488        if fn:
6489            dbs = e5App().getObject("DebugServer")
6490            for language in dbs.getSupportedLanguages():
6491                exts = dbs.getExtensions(language)
6492                if fn.endswith(exts):
6493                    from Debugger.DebugClientCapabilities import HasUnittest
6494                    cap = dbs.getClientCapabilities(language)
6495                    self.utScriptAct.setEnabled(cap & HasUnittest)
6496                    self.utEditorOpen = cap & HasUnittest
6497                    return
6498
6499            if editor.isPyFile():
6500                self.utScriptAct.setEnabled(True)
6501                self.utEditorOpen = True
6502                return
6503
6504        self.utScriptAct.setEnabled(False)
6505
6506    def __writeTasks(self):
6507        """
6508        Private slot to write the tasks data to a JSON file (.etj).
6509        """
6510        fn = os.path.join(Utilities.getConfigDir(), "eric6tasks.etj")
6511        self.__tasksFile.writeFile(fn)
6512
6513    def __readTasks(self):
6514        """
6515        Private slot to read in the tasks file (.etj or .e6t).
6516        """
6517        fn = os.path.join(Utilities.getConfigDir(), "eric6tasks.etj")
6518        if os.path.exists(fn):
6519            # try new style JSON file first
6520            self.__tasksFile.readFile(fn)
6521        else:
6522            # try old style XML file second
6523            fn = os.path.join(Utilities.getConfigDir(), "eric6tasks.e6t")
6524            if os.path.exists(fn):
6525                f = QFile(fn)
6526                if f.open(QIODevice.OpenModeFlag.ReadOnly):
6527                    from E5XML.TasksReader import TasksReader
6528                    reader = TasksReader(f, viewer=self.taskViewer)
6529                    reader.readXML()
6530                    f.close()
6531                else:
6532                    E5MessageBox.critical(
6533                        self,
6534                        self.tr("Read Tasks"),
6535                        self.tr(
6536                            "<p>The tasks file <b>{0}</b> could not be"
6537                            " read.</p>")
6538                        .format(fn))
6539
6540    def __writeSession(self, filename="", crashSession=False):
6541        """
6542        Private slot to write the session data to a JSON file (.esj).
6543
6544        @param filename name of a session file to write
6545        @type str
6546        @param crashSession flag indicating to write a crash session file
6547        @type bool
6548        @return flag indicating success
6549        @rtype bool
6550        """
6551        if filename:
6552            fn = filename
6553        elif crashSession:
6554            fn = os.path.join(Utilities.getConfigDir(),
6555                              "eric6_crash_session.esj")
6556        else:
6557            fn = os.path.join(Utilities.getConfigDir(),
6558                              "eric6session.esj")
6559
6560        if fn.endswith(".esj"):
6561            res = self.__sessionFile.writeFile(fn)
6562        else:
6563            f = QFile(fn)
6564            if f.open(QIODevice.OpenModeFlag.WriteOnly):
6565                from E5XML.SessionWriter import SessionWriter
6566                SessionWriter(f, None).writeXML()
6567                f.close()
6568                res = True
6569            else:
6570                E5MessageBox.critical(
6571                    self,
6572                    self.tr("Save Session"),
6573                    self.tr("<p>The session file <b>{0}</b> could not be"
6574                            " written.</p>")
6575                    .format(fn))
6576                res = False
6577
6578        return res
6579
6580    def __readSession(self, filename=""):
6581        """
6582        Private slot to read in the session file (.esj or .e5s).
6583
6584        @param filename name of a session file to read
6585        @type str
6586        @return flag indicating success
6587        @rtype bool
6588        """
6589        if filename:
6590            fn = filename
6591        else:
6592            fn = os.path.join(Utilities.getConfigDir(),
6593                              "eric6session.esj")
6594            if not os.path.exists(fn):
6595                fn = os.path.join(Utilities.getConfigDir(),
6596                                  "eric6session.e5s")
6597                if not os.path.exists(fn):
6598                    E5MessageBox.critical(
6599                        self,
6600                        self.tr("Read Session"),
6601                        self.tr("<p>The session file <b>{0}</b> could not"
6602                                " be read.</p>")
6603                        .format(fn))
6604                    fn = ""
6605
6606        res = False
6607        if fn:
6608            if fn.endswith(".esj"):
6609                # new JSON based format
6610                self.__readingSession = True
6611                res = self.__sessionFile.readFile(fn)
6612                self.__readingSession = False
6613            else:
6614                # old XML based format
6615                f = QFile(fn)
6616                if f.open(QIODevice.OpenModeFlag.ReadOnly):
6617                    from E5XML.SessionReader import SessionReader
6618                    self.__readingSession = True
6619                    reader = SessionReader(f, True)
6620                    reader.readXML()
6621                    self.__readingSession = False
6622                    f.close()
6623                    res = True
6624                else:
6625                    E5MessageBox.critical(
6626                        self,
6627                        self.tr("Read session"),
6628                        self.tr("<p>The session file <b>{0}</b> could not be"
6629                                " read.</p>")
6630                        .format(fn))
6631
6632        # Write a crash session after a session was read.
6633        self.__writeCrashSession()
6634
6635        return res
6636
6637    def __saveSessionToFile(self):
6638        """
6639        Private slot to save a session to disk.
6640        """
6641        sessionFile, selectedFilter = E5FileDialog.getSaveFileNameAndFilter(
6642            self,
6643            self.tr("Save Session"),
6644            Utilities.getHomeDir(),
6645            self.tr("eric Session Files (*.esj);;"
6646                    "eric XML Session Files (*.e5s)"),
6647            "")
6648
6649        if not sessionFile:
6650            return
6651
6652        ext = QFileInfo(sessionFile).suffix()
6653        if not ext:
6654            ex = selectedFilter.split("(*")[1].split(")")[0]
6655            if ex:
6656                sessionFile += ex
6657
6658        self.__writeSession(filename=sessionFile)
6659
6660    def __loadSessionFromFile(self):
6661        """
6662        Private slot to load a session from disk.
6663        """
6664        sessionFile = E5FileDialog.getOpenFileName(
6665            self,
6666            self.tr("Load session"),
6667            Utilities.getHomeDir(),
6668            self.tr("eric Session Files (*.esj);;"
6669                    "eric XML Session Files (*.e5s)"))
6670
6671        if not sessionFile:
6672            return
6673
6674        self.__readSession(filename=sessionFile)
6675
6676    def __deleteCrashSession(self):
6677        """
6678        Private slot to delete the crash session file.
6679        """
6680        for ext in (".esj", ".e5s"):
6681            fn = os.path.join(Utilities.getConfigDir(),
6682                              f"eric6_crash_session{ext}")
6683            if os.path.exists(fn):
6684                with contextlib.suppress(OSError):
6685                    os.remove(fn)
6686
6687    def __writeCrashSession(self):
6688        """
6689        Private slot to write a crash session file.
6690        """
6691        if (
6692            not self.__readingSession and
6693            not self.__disableCrashSession and
6694            Preferences.getUI("CrashSessionEnabled")
6695        ):
6696            self.__writeSession(crashSession=True)
6697
6698    def __readCrashSession(self):
6699        """
6700        Private method to check for and read a crash session.
6701
6702        @return flag indicating a crash session file was found and read
6703        @rtype bool
6704        """
6705        res = False
6706        if (
6707            not self.__disableCrashSession and
6708            not self.__noCrashOpenAtStartup and
6709            Preferences.getUI("OpenCrashSessionOnStartup")
6710        ):
6711            fn = os.path.join(Utilities.getConfigDir(),
6712                              "eric6_crash_session.esj")
6713            if os.path.exists(fn):
6714                yes = E5MessageBox.yesNo(
6715                    self,
6716                    self.tr("Crash Session found!"),
6717                    self.tr("""A session file of a crashed session was"""
6718                            """ found. Shall this session be restored?"""))
6719                if yes:
6720                    res = self.__readSession(filename=fn)
6721
6722        return res
6723
6724    def showFindFileByNameDialog(self):
6725        """
6726        Public slot to show the Find File by Name dialog.
6727        """
6728        if self.findFileNameDialog is None:
6729            from .FindFileNameDialog import FindFileNameDialog
6730            self.findFileNameDialog = FindFileNameDialog(self.project)
6731            self.findFileNameDialog.sourceFile.connect(
6732                self.viewmanager.openSourceFile)
6733            self.findFileNameDialog.designerFile.connect(self.__designer)
6734        self.findFileNameDialog.show()
6735        self.findFileNameDialog.raise_()
6736        self.findFileNameDialog.activateWindow()
6737
6738    def showFindFilesDialog(self, txt="", searchDir="", openFiles=False):
6739        """
6740        Public slot to show the Find In Files dialog.
6741
6742        @param txt text to search for (string)
6743        @param searchDir directory to search in (string)
6744        @param openFiles flag indicating to operate on open files (boolean)
6745        """
6746        if self.findFilesDialog is None:
6747            from .FindFileDialog import FindFileDialog
6748            self.findFilesDialog = FindFileDialog(self.project)
6749            self.findFilesDialog.sourceFile.connect(
6750                self.viewmanager.openSourceFile)
6751            self.findFilesDialog.designerFile.connect(self.__designer)
6752        if searchDir:
6753            self.findFilesDialog.setSearchDirectory(searchDir)
6754        self.findFilesDialog.show(txt)
6755        if openFiles:
6756            self.findFilesDialog.setOpenFiles()
6757        self.findFilesDialog.raise_()
6758        self.findFilesDialog.activateWindow()
6759
6760    def showReplaceFilesDialog(self, txt="", searchDir="", openFiles=False):
6761        """
6762        Public slot to show the Find & Replace In Files dialog.
6763
6764        @param txt text to search for (string)
6765        @param searchDir directory to search in (string)
6766        @param openFiles flag indicating to operate on open files (boolean)
6767        """
6768        if self.replaceFilesDialog is None:
6769            from .FindFileDialog import FindFileDialog
6770            self.replaceFilesDialog = FindFileDialog(
6771                self.project, replaceMode=True)
6772            self.replaceFilesDialog.sourceFile.connect(
6773                self.viewmanager.openSourceFile)
6774            self.replaceFilesDialog.designerFile.connect(self.__designer)
6775        if searchDir:
6776            self.replaceFilesDialog.setSearchDirectory(searchDir)
6777        self.replaceFilesDialog.show(txt)
6778        if openFiles:
6779            self.replaceFilesDialog.setOpenFiles()
6780        self.replaceFilesDialog.raise_()
6781        self.replaceFilesDialog.activateWindow()
6782
6783    ##########################################################
6784    ## Below are slots to handle StdOut and StdErr
6785    ##########################################################
6786
6787    def appendToStdout(self, s):
6788        """
6789        Public slot to append text to the stdout log viewer tab.
6790
6791        @param s output to be appended (string)
6792        """
6793        self.appendStdout.emit(s)
6794
6795    def appendToStderr(self, s):
6796        """
6797        Public slot to append text to the stderr log viewer tab.
6798
6799        @param s output to be appended (string)
6800        """
6801        self.appendStderr.emit(s)
6802
6803    ##########################################################
6804    ## Below are slots needed by the plugin menu
6805    ##########################################################
6806
6807    def __showPluginInfo(self):
6808        """
6809        Private slot to show the plugin info dialog.
6810        """
6811        from PluginManager.PluginInfoDialog import PluginInfoDialog
6812        self.__pluginInfoDialog = PluginInfoDialog(self.pluginManager, self)
6813        self.__pluginInfoDialog.show()
6814
6815    @pyqtSlot()
6816    def __installPlugins(self, pluginFileNames=None):
6817        """
6818        Private slot to show a dialog to install a new plugin.
6819
6820        @param pluginFileNames list of plugin files suggested for
6821            installation list of strings
6822        """
6823        from PluginManager.PluginInstallDialog import PluginInstallDialog
6824        self.__pluginInstallDialog = PluginInstallDialog(
6825            self.pluginManager,
6826            [] if pluginFileNames is None else pluginFileNames[:],
6827            self)
6828        self.__pluginInstallDialog.setModal(False)
6829        self.__pluginInstallDialog.finished.connect(
6830            self.__pluginInstallFinished)
6831        self.__pluginInstallDialog.show()
6832
6833    @pyqtSlot()
6834    def __pluginInstallFinished(self):
6835        """
6836        Private slot to handle the finishing of the plugin install dialog.
6837        """
6838        if self.__pluginInstallDialog.restartNeeded():
6839            self.__pluginInstallDialog.deleteLater()
6840            del self.__pluginInstallDialog
6841            self.__restart(ask=True)
6842
6843    def __deinstallPlugin(self):
6844        """
6845        Private slot to show a dialog to uninstall a plugin.
6846        """
6847        from PluginManager.PluginUninstallDialog import PluginUninstallDialog
6848        dlg = PluginUninstallDialog(self.pluginManager, self)
6849        dlg.exec()
6850
6851    def showPluginsAvailable(self):
6852        """
6853        Public slot to show the plugins available for download.
6854        """
6855        from PluginManager.PluginRepositoryDialog import PluginRepositoryDialog
6856        dlg = PluginRepositoryDialog(self.pluginManager, self)
6857        res = dlg.exec()
6858        if res == (QDialog.DialogCode.Accepted + 1):
6859            self.__installPlugins(dlg.getDownloadedPlugins())
6860
6861    def __pluginsConfigure(self):
6862        """
6863        Private slot to show the plugin manager configuration page.
6864        """
6865        self.showPreferences("pluginManagerPage")
6866
6867    def checkPluginUpdatesAvailable(self):
6868        """
6869        Public method to check the availability of updates of plug-ins.
6870        """
6871        self.pluginManager.checkPluginUpdatesAvailable()
6872
6873    #################################################################
6874    ## Drag and Drop Support
6875    #################################################################
6876
6877    def dragEnterEvent(self, event):
6878        """
6879        Protected method to handle the drag enter event.
6880
6881        @param event the drag enter event (QDragEnterEvent)
6882        """
6883        self.inDragDrop = event.mimeData().hasUrls()
6884        if self.inDragDrop:
6885            event.acceptProposedAction()
6886
6887    def dragMoveEvent(self, event):
6888        """
6889        Protected method to handle the drag move event.
6890
6891        @param event the drag move event (QDragMoveEvent)
6892        """
6893        if self.inDragDrop:
6894            event.acceptProposedAction()
6895
6896    def dragLeaveEvent(self, event):
6897        """
6898        Protected method to handle the drag leave event.
6899
6900        @param event the drag leave event (QDragLeaveEvent)
6901        """
6902        if self.inDragDrop:
6903            self.inDragDrop = False
6904
6905    def dropEvent(self, event):
6906        """
6907        Protected method to handle the drop event.
6908
6909        @param event the drop event (QDropEvent)
6910        """
6911        if event.mimeData().hasUrls():
6912            event.acceptProposedAction()
6913            for url in event.mimeData().urls():
6914                fname = url.toLocalFile()
6915                if fname:
6916                    if QFileInfo(fname).isFile():
6917                        self.viewmanager.openSourceFile(fname)
6918                    else:
6919                        E5MessageBox.information(
6920                            self,
6921                            self.tr("Drop Error"),
6922                            self.tr("""<p><b>{0}</b> is not a file.</p>""")
6923                            .format(fname))
6924
6925        self.inDragDrop = False
6926
6927    ##########################################################
6928    ## Below are methods needed for shutting down the IDE
6929    ##########################################################
6930
6931    def closeEvent(self, event):
6932        """
6933        Protected event handler for the close event.
6934
6935        This event handler saves the preferences.
6936
6937        @param event close event (QCloseEvent)
6938        """
6939        if self.__shutdown():
6940            event.accept()
6941            if not self.inCloseEvent:
6942                self.inCloseEvent = True
6943                QTimer.singleShot(0, e5App().closeAllWindows)
6944        else:
6945            event.ignore()
6946
6947    def __shutdown(self):
6948        """
6949        Private method to perform all necessary steps to close down the IDE.
6950
6951        @return flag indicating success
6952        """
6953        if self.shutdownCalled:
6954            return True
6955
6956        if self.__webBrowserProcess is not None:
6957            self.__webBrowserShutdown()
6958
6959        if self.irc is not None and not self.irc.shutdown():
6960            return False
6961
6962        sessionCreated = self.__writeSession()
6963
6964        self.__astViewer.hide()
6965
6966        if not self.project.closeProject():
6967            return False
6968
6969        if not self.multiProject.closeMultiProject():
6970            return False
6971
6972        if not self.viewmanager.closeViewManager():
6973            return False
6974
6975        if sessionCreated and not self.__disableCrashSession:
6976            self.__deleteCrashSession()
6977
6978        if self.codeDocumentationViewer is not None:
6979            self.codeDocumentationViewer.shutdown()
6980
6981        self.__previewer.shutdown()
6982
6983        self.__astViewer.shutdown()
6984
6985        self.shell.closeShell()
6986
6987        self.__writeTasks()
6988
6989        if self.templateViewer is not None:
6990            self.templateViewer.save()
6991
6992        if not self.debuggerUI.shutdownServer():
6993            return False
6994        self.debuggerUI.shutdown()
6995
6996        self.backgroundService.shutdown()
6997
6998        if self.cooperation is not None:
6999            self.cooperation.shutdown()
7000
7001        self.pluginManager.doShutdown()
7002
7003        self.virtualenvManager.shutdown()
7004
7005        if self.__layoutType == "Sidebars":
7006            self.leftSidebar.shutdown()
7007            self.bottomSidebar.shutdown()
7008            self.rightSidebar.shutdown()
7009
7010        if self.SAServer is not None:
7011            self.SAServer.shutdown()
7012            self.SAServer = None
7013
7014        # set proxy factory to None to avoid crashes
7015        QNetworkProxyFactory.setApplicationProxyFactory(None)
7016
7017        Preferences.setGeometry("MainMaximized", self.isMaximized())
7018        if not self.isMaximized():
7019            Preferences.setGeometry("MainGeometry", self.saveGeometry())
7020
7021        if self.browser is not None:
7022            self.browser.saveToplevelDirs()
7023
7024        Preferences.setUI(
7025            "ToolbarManagerState", self.toolbarManager.saveState())
7026        self.__saveCurrentViewProfile(True)
7027        Preferences.saveToolGroups(self.toolGroups, self.currentToolGroup)
7028        Preferences.syncPreferences()
7029        self.shutdownCalled = True
7030        return True
7031
7032    ##############################################
7033    ## Below are methods to check for new versions
7034    ##############################################
7035
7036    def showAvailableVersionsInfo(self):
7037        """
7038        Public method to show the eric versions available for download.
7039        """
7040        self.performVersionCheck(manual=True, showVersions=True)
7041
7042    @pyqtSlot()
7043    def performVersionCheck(self, manual=True, alternative=0,
7044                            showVersions=False):
7045        """
7046        Public method to check the internet for an eric update.
7047
7048        @param manual flag indicating an invocation via the menu (boolean)
7049        @param alternative index of server to download from (integer)
7050        @param showVersions flag indicating the show versions mode (boolean)
7051        """
7052        if not manual:
7053            if VersionOnly.startswith("@@"):
7054                return
7055            else:
7056                period = Preferences.getUI("PerformVersionCheck")
7057                if period == 0:
7058                    return
7059                elif period in [2, 3, 4]:
7060                    lastCheck = Preferences.Prefs.settings.value(
7061                        "Updates/LastCheckDate", QDate(1970, 1, 1))
7062                    if lastCheck.isValid():
7063                        now = QDate.currentDate()
7064                        if (
7065                            (period == 2 and lastCheck.day() == now.day()) or
7066                            (period == 3 and lastCheck.daysTo(now) < 7) or
7067                            (period == 4 and (lastCheck.daysTo(now) <
7068                                              lastCheck.daysInMonth()))
7069                        ):
7070                            # daily, weekly, monthly
7071                            return
7072
7073        self.__inVersionCheck = True
7074        self.manualUpdatesCheck = manual
7075        self.showAvailableVersions = showVersions
7076        self.httpAlternative = alternative
7077        url = QUrl(self.__httpAlternatives[alternative])
7078        self.__versionCheckCanceled = False
7079        if manual:
7080            if self.__versionCheckProgress is None:
7081                self.__versionCheckProgress = E5ProgressDialog(
7082                    "", self.tr("&Cancel"),
7083                    0, len(self.__httpAlternatives),
7084                    self.tr("%v/%m"), self)
7085                self.__versionCheckProgress.setWindowTitle(
7086                    self.tr("Version Check"))
7087                self.__versionCheckProgress.setMinimumDuration(0)
7088                self.__versionCheckProgress.canceled.connect(
7089                    self.__versionsDownloadCanceled)
7090            self.__versionCheckProgress.setLabelText(
7091                self.tr("Trying host {0}").format(url.host()))
7092            self.__versionCheckProgress.setValue(alternative)
7093        request = QNetworkRequest(url)
7094        request.setAttribute(
7095            QNetworkRequest.Attribute.CacheLoadControlAttribute,
7096            QNetworkRequest.CacheLoadControl.AlwaysNetwork)
7097        reply = self.__networkManager.get(request)
7098        reply.finished.connect(lambda: self.__versionsDownloadDone(reply))
7099        self.__replies.append(reply)
7100
7101    @pyqtSlot()
7102    def __versionsDownloadDone(self, reply):
7103        """
7104        Private slot called, after the versions file has been downloaded
7105        from the internet.
7106
7107        @param reply reference to the network reply
7108        @type QNetworkReply
7109        """
7110        if self.__versionCheckCanceled:
7111            self.__inVersionCheck = False
7112            if self.__versionCheckProgress is not None:
7113                self.__versionCheckProgress.reset()
7114                self.__versionCheckProgress = None
7115            return
7116
7117        reply.deleteLater()
7118        if reply in self.__replies:
7119            self.__replies.remove(reply)
7120        if reply.error() == QNetworkReply.NetworkError.NoError:
7121            ioEncoding = Preferences.getSystem("IOEncoding")
7122            versions = str(reply.readAll(), ioEncoding, 'replace').splitlines()
7123        reply.close()
7124        if (
7125            reply.error() != QNetworkReply.NetworkError.NoError or
7126            len(versions) == 0 or
7127            versions[0].startswith("<")
7128        ):
7129            # network error or an error page
7130            self.httpAlternative += 1
7131            if self.httpAlternative >= len(self.__httpAlternatives):
7132                self.__inVersionCheck = False
7133                if self.__versionCheckProgress is not None:
7134                    self.__versionCheckProgress.reset()
7135                    self.__versionCheckProgress = None
7136                firstFailure = Preferences.Prefs.settings.value(
7137                    "Updates/FirstFailedCheckDate", QDate.currentDate())
7138                failedDuration = firstFailure.daysTo(QDate.currentDate())
7139                Preferences.Prefs.settings.setValue(
7140                    "Updates/FirstFailedCheckDate", firstFailure)
7141                if self.manualUpdatesCheck:
7142                    E5MessageBox.warning(
7143                        self,
7144                        self.tr("Error getting versions information"),
7145                        self.tr("""The versions information could not be"""
7146                                """ downloaded."""
7147                                """ Please go online and try again."""))
7148                elif failedDuration > 7:
7149                    E5MessageBox.warning(
7150                        self,
7151                        self.tr("Error getting versions information"),
7152                        self.tr("""The versions information could not be"""
7153                                """ downloaded for the last 7 days."""
7154                                """ Please go online and try again."""))
7155                return
7156            else:
7157                self.performVersionCheck(self.manualUpdatesCheck,
7158                                         self.httpAlternative,
7159                                         self.showAvailableVersions)
7160                return
7161
7162        self.__inVersionCheck = False
7163        if self.__versionCheckProgress is not None:
7164            self.__versionCheckProgress.reset()
7165            self.__versionCheckProgress = None
7166        self.__updateVersionsUrls(versions)
7167        if self.showAvailableVersions:
7168            self.__showAvailableVersionInfos(versions)
7169        else:
7170            Preferences.Prefs.settings.remove("Updates/FirstFailedCheckDate")
7171            Preferences.Prefs.settings.setValue(
7172                "Updates/LastCheckDate", QDate.currentDate())
7173            self.__versionCheckResult(versions)
7174
7175    def __updateVersionsUrls(self, versions):
7176        """
7177        Private method to update the URLs from which to retrieve the versions
7178        file.
7179
7180        @param versions contents of the downloaded versions file (list of
7181            strings)
7182        """
7183        if len(versions) > 5 and versions[4] == "---":
7184            line = 5
7185            urls = []
7186            while line < len(versions):
7187                urls.append(versions[line])
7188                line += 1
7189
7190            Preferences.setUI("VersionsUrls6", urls)
7191
7192    def __versionCheckResult(self, versions):
7193        """
7194        Private method to show the result of the version check action.
7195
7196        @param versions contents of the downloaded versions file (list of
7197            strings)
7198        """
7199        url = ""
7200        try:
7201            if "snapshot-" in VersionOnly:
7202                # check snapshot version like snapshot-20170810
7203                if "snapshot-" in versions[2]:
7204                    installedSnapshotDate = VersionOnly.rsplit("-", 1)[-1]
7205                    availableSnapshotDate = versions[2].rsplit("-", 1)[-1]
7206                    if availableSnapshotDate > installedSnapshotDate:
7207                        res = E5MessageBox.yesNo(
7208                            self,
7209                            self.tr("Update available"),
7210                            self.tr(
7211                                """The update to <b>{0}</b> of eric is"""
7212                                """ available at <b>{1}</b>. Would you like"""
7213                                """ to get it?""")
7214                            .format(versions[2], versions[3]),
7215                            yesDefault=True)
7216                        url = res and versions[3] or ''
7217                else:
7218                    if self.manualUpdatesCheck:
7219                        E5MessageBox.information(
7220                            self,
7221                            self.tr("Update Check"),
7222                            self.tr(
7223                                """You are using a snapshot release of"""
7224                                """ eric. A more up-to-date stable release"""
7225                                """ might be available."""))
7226            elif VersionOnly.startswith(("rev_", "@@")):
7227                # check installation from source
7228                if self.manualUpdatesCheck:
7229                    E5MessageBox.information(
7230                        self,
7231                        self.tr("Update Check"),
7232                        self.tr(
7233                            """You installed eric directly from the source"""
7234                            """ code. There is no possibility to check"""
7235                            """ for the availability of an update."""))
7236            else:
7237                # check release version
7238                installedVersionTuple = self.__versionToTuple(VersionOnly)
7239                availableVersionTuple = self.__versionToTuple(versions[0])
7240                if availableVersionTuple > installedVersionTuple:
7241                    res = E5MessageBox.yesNo(
7242                        self,
7243                        self.tr("Update available"),
7244                        self.tr(
7245                            """The update to <b>{0}</b> of eric is"""
7246                            """ available at <b>{1}</b>. Would you like"""
7247                            """ to get it?""")
7248                        .format(versions[0], versions[1]),
7249                        yesDefault=True)
7250                    url = res and versions[1] or ''
7251                else:
7252                    if self.manualUpdatesCheck:
7253                        E5MessageBox.information(
7254                            self,
7255                            self.tr("eric is up to date"),
7256                            self.tr(
7257                                """You are using the latest version of"""
7258                                """ eric"""))
7259        except (IndexError, TypeError):
7260            E5MessageBox.warning(
7261                self,
7262                self.tr("Error during updates check"),
7263                self.tr("""Could not perform updates check."""))
7264
7265        if url:
7266            QDesktopServices.openUrl(QUrl(url))
7267
7268    @pyqtSlot()
7269    def __versionsDownloadCanceled(self):
7270        """
7271        Private slot called to cancel the version check.
7272        """
7273        if self.__replies:
7274            self.__versionCheckCanceled = True
7275            self.__replies[-1].abort()
7276
7277    def __showAvailableVersionInfos(self, versions):
7278        """
7279        Private method to show the versions available for download.
7280
7281        @param versions contents of the downloaded versions file (list of
7282            strings)
7283        """
7284        versionText = self.tr(
7285            """<h3>Available versions</h3>"""
7286            """<table>""")
7287        line = 0
7288        while line < len(versions):
7289            if versions[line] == "---":
7290                break
7291
7292            versionText += (
7293                """<tr><td>{0}</td><td><a href="{1}">{2}</a></td></tr>"""
7294            ).format(
7295                versions[line], versions[line + 1],
7296                'sourceforge' in versions[line + 1] and
7297                "SourceForge" or versions[line + 1])
7298            line += 2
7299        versionText += self.tr("""</table>""")
7300
7301        self.__versionsDialog = E5MessageBox.E5MessageBox(
7302            E5MessageBox.NoIcon,
7303            Program,
7304            versionText,
7305            modal=False,
7306            buttons=E5MessageBox.Ok,
7307            parent=self
7308        )
7309        self.__versionsDialog.setIconPixmap(
7310            UI.PixmapCache.getPixmap("eric").scaled(64, 64))
7311        self.__versionsDialog.show()
7312
7313    def __sslErrors(self, reply, errors):
7314        """
7315        Private slot to handle SSL errors.
7316
7317        @param reply reference to the reply object (QNetworkReply)
7318        @param errors list of SSL errors (list of QSslError)
7319        """
7320        ignored = self.__sslErrorHandler.sslErrorsReply(reply, errors)[0]
7321        if ignored == E5SslErrorState.NOT_IGNORED:
7322            self.__downloadCancelled = True
7323
7324    #######################################
7325    ## Below are methods for various checks
7326    #######################################
7327
7328    def checkConfigurationStatus(self):
7329        """
7330        Public method to check, if eric has been configured. If it is not,
7331        the configuration dialog is shown.
7332        """
7333        if not Preferences.isConfigured():
7334            self.__initDebugToolbarsLayout()
7335
7336            E5MessageBox.information(
7337                self,
7338                self.tr("First time usage"),
7339                self.tr("""eric has not been configured yet. """
7340                        """The configuration dialog will be started."""))
7341            self.showPreferences()
7342
7343    def checkProjectsWorkspace(self):
7344        """
7345        Public method to check, if a projects workspace has been configured. If
7346        it has not, a dialog is shown.
7347        """
7348        if not Preferences.isConfigured():
7349            # eric hasn't been configured at all
7350            self.checkConfigurationStatus()
7351
7352        workspace = Preferences.getMultiProject("Workspace")
7353        if workspace == "":
7354            default = Utilities.getHomeDir()
7355            workspace = E5FileDialog.getExistingDirectory(
7356                None,
7357                self.tr("Select Workspace Directory"),
7358                default,
7359                E5FileDialog.Options(E5FileDialog.Option(0)))
7360            Preferences.setMultiProject("Workspace", workspace)
7361
7362    def versionIsNewer(self, required, snapshot=None):
7363        """
7364        Public method to check, if the eric version is good compared to
7365        the required version.
7366
7367        @param required required version (string)
7368        @param snapshot required snapshot version (string)
7369        @return flag indicating, that the version is newer than the required
7370            one (boolean)
7371        """
7372        if VersionOnly.startswith("@@"):
7373            # development version, always newer
7374            return True
7375
7376        if VersionOnly.startswith("rev_"):
7377            # installed from cloned sources, always newer
7378            return True
7379
7380        if "snapshot-" in VersionOnly:
7381            # check snapshot version
7382            if snapshot is None:
7383                return True
7384            else:
7385                vers = VersionOnly.split("snapshot-")[1]
7386                return vers > snapshot
7387
7388        versionTuple = self.__versionToTuple(VersionOnly)
7389        if isinstance(required, str):
7390            required = self.__versionToTuple(required)
7391        try:
7392            res = versionTuple > required
7393        except TypeError:
7394            # some mismatching types, assume newer
7395            res = True
7396        return res
7397
7398    def __versionToTuple(self, version):
7399        """
7400        Private method to convert a version string into a tuple.
7401
7402        @param version version string
7403        @type str
7404        @return version tuple
7405        @rtype tuple of int
7406        """
7407        versionParts = []
7408        for part in version.split("."):
7409            part = part.strip()
7410            if part:
7411                try:
7412                    part = int(part)
7413                except ValueError:
7414                    # not an integer, ignore
7415                    continue
7416                versionParts.append(part)
7417        return tuple(versionParts)
7418
7419    #################################
7420    ## Below are some utility methods
7421    #################################
7422
7423    def __getFloatingGeometry(self, w):
7424        """
7425        Private method to get the geometry of a floating windows.
7426
7427        @param w reference to the widget to be saved (QWidget)
7428        @return list giving the widget's geometry and its visibility
7429        """
7430        s = w.size()
7431        p = w.pos()
7432        return [p.x(), p.y(), s.width(), s.height(), not w.isHidden()]
7433
7434    def getOriginalPathString(self):
7435        """
7436        Public method to get the original PATH environment variable
7437        (i.e. before modifications by eric and PyQt5).
7438
7439        @return original PATH environment variable
7440        @rtype str
7441        """
7442        return self.__originalPathString
7443
7444    ############################
7445    ## some event handlers below
7446    ############################
7447
7448    def showEvent(self, evt):
7449        """
7450        Protected method to handle the show event.
7451
7452        @param evt reference to the show event (QShowEvent)
7453        """
7454        if self.__startup:
7455            if Preferences.getGeometry("MainMaximized"):
7456                self.setWindowState(
7457                    Qt.WindowStates(Qt.WindowState.WindowMaximized))
7458            self.__startup = False
7459
7460    ##########################################
7461    ## Support for desktop notifications below
7462    ##########################################
7463
7464    def showNotification(self, icon, heading, text,
7465                         kind=NotificationTypes.INFORMATION, timeout=None):
7466        """
7467        Public method to show a desktop notification.
7468
7469        @param icon icon to be shown in the notification
7470        @type QPixmap
7471        @param heading heading of the notification
7472        @type str
7473        @param text text of the notification
7474        @type str
7475        @param kind kind of notification to be shown
7476        @type NotificationTypes
7477        @param timeout time in seconds the notification should be shown
7478            (None = use configured timeout, 0 = indefinitely)
7479        @type int
7480        """
7481        if self.__notification is None:
7482            from .NotificationWidget import NotificationWidget
7483            self.__notification = NotificationWidget(parent=self)
7484        if timeout is None:
7485            timeout = Preferences.getUI("NotificationTimeout")
7486        self.__notification.showNotification(icon, heading, text, kind=kind,
7487                                             timeout=timeout)
7488
7489    #########################
7490    ## Support for IRC  below
7491    #########################
7492
7493    def autoConnectIrc(self):
7494        """
7495        Public method to initiate the IRC auto connection.
7496        """
7497        if self.irc is not None:
7498            self.irc.autoConnect()
7499
7500    def __ircAutoConnected(self):
7501        """
7502        Private slot handling the automatic connection of the IRC client.
7503        """
7504        self.__activateIRC()
7505
7506    ###############################################
7507    ## Support for Code Documentation Viewer  below
7508    ###############################################
7509
7510    def documentationViewer(self):
7511        """
7512        Public method to provide a reference to the code documentation viewer.
7513
7514        @return reference to the code documentation viewer
7515        @rtype CodeDocumentationViewer
7516        """
7517        return self.codeDocumentationViewer
7518
7519    ###############################################
7520    ## Support for Desktop session management below
7521    ###############################################
7522
7523    def __commitData(self, manager):
7524        """
7525        Private slot to commit unsaved data when instructed by the desktop
7526        session manager.
7527
7528        @param manager reference to the desktop session manager
7529        @type QSessionManager
7530        """
7531        if self.viewmanager.hasDirtyEditor():
7532            if manager.allowsInteraction():
7533                res = E5MessageBox.warning(
7534                    self,
7535                    self.tr("Unsaved Data Detected"),
7536                    self.tr("Some editors contain unsaved data. Shall these"
7537                            " be saved?"),
7538                    E5MessageBox.Abort |
7539                    E5MessageBox.Discard |
7540                    E5MessageBox.Save |
7541                    E5MessageBox.SaveAll,
7542                    E5MessageBox.SaveAll)
7543                if res == E5MessageBox.SaveAll:
7544                    manager.release()
7545                    self.viewmanager.saveAllEditors()
7546                elif res == E5MessageBox.Save:
7547                    manager.release()
7548                    ok = self.viewmanager.checkAllDirty()
7549                    if not ok:
7550                        manager.cancel()
7551                elif res == E5MessageBox.Discard:
7552                    # nothing to do
7553                    pass
7554                else:
7555                    # default action is to abort shutdown
7556                    manager.cancel()
7557            else:
7558                # We did not get permission to interact, play it safe and
7559                # save all data.
7560                self.viewmanager.saveAllEditors()
7561