class TextEditorTabWidget(QWidget): """Plain text editor tab widget""" sigReloadRequest = pyqtSignal() reloadAllNonModifiedRequest = pyqtSignal() sigTabRunChanged = pyqtSignal(bool) def __init__(self, parent, debugger): QWidget.__init__(self, parent) extendInstance(self, MainWindowTabWidgetBase) MainWindowTabWidgetBase.__init__(self) self.__navigationBar = None self.__editor = TextEditor(self, debugger) self.__fileName = "" self.__shortName = "" self.__createLayout() self.__editor.redoAvailable.connect(self.__redoAvailable) self.__editor.undoAvailable.connect(self.__undoAvailable) self.__editor.modificationChanged.connect(self.modificationChanged) self.__editor.sigCFlowSyncRequested.connect(self.cflowSyncRequested) self.__editor.languageChanged.connect(self.__languageChanged) self.__diskModTime = None self.__diskSize = None self.__reloadDlgShown = False self.__debugMode = False self.__vcsStatus = None def onTextZoomChanged(self): """Triggered when a text zoom is changed""" self.__editor.onTextZoomChanged() def onFlowZoomChanged(self): """Triggered when a flow zoom is changed""" self.__flowUI.onFlowZoomChanged() def getNavigationBar(self): """Provides a reference to the navigation bar""" return self.__navigationBar def shouldAcceptFocus(self): """True if it can accept the focus""" return self.__outsideChangesBar.isHidden() def readFile(self, fileName): """Reads the text from a file""" self.__editor.readFile(fileName) self.setFileName(fileName) self.__editor.restoreBreakpoints() # Memorize the modification date path = os.path.realpath(fileName) self.__diskModTime = os.path.getmtime(path) self.__diskSize = os.path.getsize(path) def writeFile(self, fileName): """Writes the text to a file""" if self.__editor.writeFile(fileName): # Memorize the modification date path = os.path.realpath(fileName) self.__diskModTime = os.path.getmtime(path) self.__diskSize = os.path.getsize(path) self.setFileName(fileName) self.__editor.restoreBreakpoints() return True return False def __createLayout(self): """Creates the toolbar and layout""" # Buttons printButton = QAction(getIcon('printer.png'), 'Print (Ctrl+P)', self) printButton.triggered.connect(self.__onPrint) printPreviewButton = QAction(getIcon('printpreview.png'), 'Print preview', self) printPreviewButton.triggered.connect(self.__onPrintPreview) printPreviewButton.setEnabled(False) printPreviewButton.setVisible(False) printSpacer = QWidget() printSpacer.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) printSpacer.setFixedHeight(8) # Imports diagram and its menu importsMenu = QMenu(self) importsDlgAct = importsMenu.addAction(getIcon('detailsdlg.png'), 'Fine tuned imports diagram') importsDlgAct.triggered.connect(self.onImportDgmTuned) self.importsDiagramButton = QToolButton(self) self.importsDiagramButton.setIcon(getIcon('importsdiagram.png')) self.importsDiagramButton.setToolTip('Generate imports diagram') self.importsDiagramButton.setPopupMode(QToolButton.DelayedPopup) self.importsDiagramButton.setMenu(importsMenu) self.importsDiagramButton.setFocusPolicy(Qt.NoFocus) self.importsDiagramButton.clicked.connect(self.onImportDgm) self.importsDiagramButton.setEnabled(False) # Run script and its menu runScriptMenu = QMenu(self) runScriptDlgAct = runScriptMenu.addAction(getIcon('detailsdlg.png'), 'Set run/debug parameters') runScriptDlgAct.triggered.connect(self.onRunScriptDlg) self.runScriptButton = QToolButton(self) self.runScriptButton.setIcon(getIcon('run.png')) self.runScriptButton.setToolTip('Run script') self.runScriptButton.setPopupMode(QToolButton.DelayedPopup) self.runScriptButton.setMenu(runScriptMenu) self.runScriptButton.setFocusPolicy(Qt.NoFocus) self.runScriptButton.clicked.connect(self.onRunScript) self.runScriptButton.setEnabled(False) # Profile script and its menu profileScriptMenu = QMenu(self) profileScriptDlgAct = profileScriptMenu.addAction( getIcon('detailsdlg.png'), 'Set profile parameters') profileScriptDlgAct.triggered.connect(self.onProfileScriptDlg) self.profileScriptButton = QToolButton(self) self.profileScriptButton.setIcon(getIcon('profile.png')) self.profileScriptButton.setToolTip('Profile script') self.profileScriptButton.setPopupMode(QToolButton.DelayedPopup) self.profileScriptButton.setMenu(profileScriptMenu) self.profileScriptButton.setFocusPolicy(Qt.NoFocus) self.profileScriptButton.clicked.connect(self.onProfileScript) self.profileScriptButton.setEnabled(False) # Debug script and its menu debugScriptMenu = QMenu(self) debugScriptDlgAct = debugScriptMenu.addAction( getIcon('detailsdlg.png'), 'Set run/debug parameters') debugScriptDlgAct.triggered.connect(self.onDebugScriptDlg) self.debugScriptButton = QToolButton(self) self.debugScriptButton.setIcon(getIcon('debugger.png')) self.debugScriptButton.setToolTip('Debug script') self.debugScriptButton.setPopupMode(QToolButton.DelayedPopup) self.debugScriptButton.setMenu(debugScriptMenu) self.debugScriptButton.setFocusPolicy(Qt.NoFocus) self.debugScriptButton.clicked.connect(self.onDebugScript) self.debugScriptButton.setEnabled(False) # Disassembling disasmScriptMenu = QMenu(self) disasmScriptMenu.addAction(getIcon(''), 'Disassembly (no optimization)', self.__editor._onDisasm0) disasmScriptMenu.addAction(getIcon(''), 'Disassembly (optimization level 1)', self.__editor._onDisasm1) disasmScriptMenu.addAction(getIcon(''), 'Disassembly (optimization level 2)', self.__editor._onDisasm2) self.disasmScriptButton = QToolButton(self) self.disasmScriptButton.setIcon(getIcon('disassembly.png')) self.disasmScriptButton.setToolTip('Disassembly script') self.disasmScriptButton.setPopupMode(QToolButton.DelayedPopup) self.disasmScriptButton.setMenu(disasmScriptMenu) self.disasmScriptButton.setFocusPolicy(Qt.NoFocus) self.disasmScriptButton.clicked.connect(self.__editor._onDisasm0) self.disasmScriptButton.setEnabled(False) # Dead code self.deadCodeScriptButton = QAction(getIcon('deadcode.png'), 'Find dead code', self) self.deadCodeScriptButton.triggered.connect(self.__onDeadCode) self.deadCodeScriptButton.setEnabled(False) undoSpacer = QWidget() undoSpacer.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) undoSpacer.setFixedHeight(8) self.__undoButton = QAction(getIcon('undo.png'), 'Undo (Ctrl+Z)', self) self.__undoButton.setShortcut('Ctrl+Z') self.__undoButton.triggered.connect(self.__editor.onUndo) self.__undoButton.setEnabled(False) self.__redoButton = QAction(getIcon('redo.png'), 'Redo (Ctrl+Y)', self) self.__redoButton.setShortcut('Ctrl+Y') self.__redoButton.triggered.connect(self.__editor.onRedo) self.__redoButton.setEnabled(False) spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.removeTrailingSpacesButton = QAction(getIcon('trailingws.png'), 'Remove trailing spaces', self) self.removeTrailingSpacesButton.triggered.connect( self.onRemoveTrailingWS) self.expandTabsButton = QAction(getIcon('expandtabs.png'), 'Expand tabs (4 spaces)', self) self.expandTabsButton.triggered.connect(self.onExpandTabs) # The toolbar toolbar = QToolBar(self) toolbar.setOrientation(Qt.Vertical) toolbar.setMovable(False) toolbar.setAllowedAreas(Qt.RightToolBarArea) toolbar.setIconSize(QSize(16, 16)) toolbar.setFixedWidth(30) toolbar.setContentsMargins(0, 0, 0, 0) toolbar.addAction(printPreviewButton) toolbar.addAction(printButton) toolbar.addWidget(printSpacer) toolbar.addWidget(self.importsDiagramButton) toolbar.addWidget(self.runScriptButton) toolbar.addWidget(self.profileScriptButton) toolbar.addWidget(self.debugScriptButton) toolbar.addWidget(self.disasmScriptButton) toolbar.addAction(self.deadCodeScriptButton) toolbar.addWidget(undoSpacer) toolbar.addAction(self.__undoButton) toolbar.addAction(self.__redoButton) toolbar.addWidget(spacer) toolbar.addAction(self.removeTrailingSpacesButton) toolbar.addAction(self.expandTabsButton) self.importsBar = ImportListWidget(self.__editor) self.importsBar.hide() self.__outsideChangesBar = OutsideChangeWidget(self.__editor) self.__outsideChangesBar.sigReloadRequest.connect(self.__onReload) self.__outsideChangesBar.reloadAllNonModifiedRequest.connect( self.reloadAllNonModified) self.__outsideChangesBar.hide() hLayout = QHBoxLayout() hLayout.setContentsMargins(0, 0, 0, 0) hLayout.setSpacing(0) vLayout = QVBoxLayout() vLayout.setContentsMargins(0, 0, 0, 0) vLayout.setSpacing(0) self.__navigationBar = NavigationBar(self.__editor, self) vLayout.addWidget(self.__navigationBar) vLayout.addWidget(self.__editor) hLayout.addLayout(vLayout) hLayout.addWidget(toolbar) widget = QWidget() widget.setLayout(hLayout) self.__splitter = QSplitter(Qt.Horizontal, self) self.__flowUI = FlowUIWidget(self.__editor, self) self.__mdView = MDWidget(self.__editor, self) self.__renderLayout = QVBoxLayout() self.__renderLayout.setContentsMargins(0, 0, 0, 0) self.__renderLayout.setSpacing(0) self.__renderLayout.addWidget(self.__flowUI) self.__renderLayout.addWidget(self.__mdView) self.__renderWidget = QWidget() self.__renderWidget.setLayout(self.__renderLayout) self.__splitter.addWidget(widget) self.__splitter.addWidget(self.__renderWidget) containerLayout = QHBoxLayout() containerLayout.setContentsMargins(0, 0, 0, 0) containerLayout.setSpacing(0) containerLayout.addWidget(self.__splitter) self.setLayout(containerLayout) self.__renderWidget.setVisible(False) self.__splitter.setSizes(Settings()['flowSplitterSizes']) self.__splitter.splitterMoved.connect(self.flowSplitterMoved) Settings().sigFlowSplitterChanged.connect(self.otherFlowSplitterMoved) def flowSplitterMoved(self, pos, index): """Splitter has been moved""" del pos # unused argument del index # unused argument Settings()['flowSplitterSizes'] = list(self.__splitter.sizes()) def otherFlowSplitterMoved(self): """Other window has changed the splitter position""" self.__splitter.setSizes(Settings()['flowSplitterSizes']) def updateStatus(self): """Updates the toolbar buttons status""" self.__updateRunDebugButtons() isPythonFile = isPythonMime(self.__editor.mime) self.importsDiagramButton.setEnabled( isPythonFile and GlobalData().graphvizAvailable) self.__editor.diagramsMenu.setEnabled( self.importsDiagramButton.isEnabled()) self.__editor.toolsMenu.setEnabled(self.runScriptButton.isEnabled()) def onNavigationBar(self): """Triggered when navigation bar focus is requested""" if self.__navigationBar.isVisible(): self.__navigationBar.setFocusToLastCombo() return True def __onPrint(self): """Triggered when the print button is pressed""" self.__editor._onShortcutPrint() def __onPrintPreview(self): """Triggered when the print preview button is pressed""" pass def __onDeadCode(self): """Triggered when vulture analysis is requested""" GlobalData().mainWindow.tabDeadCodeClicked() def __redoAvailable(self, available): """Reports redo ops available""" self.__redoButton.setEnabled(available) def __undoAvailable(self, available): """Reports undo ops available""" self.__undoButton.setEnabled(available) def __languageChanged(self, _=None): """Language changed""" isPython = self.__editor.isPythonBuffer() isMarkdown = self.__editor.isMarkdownBuffer() self.disasmScriptButton.setEnabled(isPython) self.__renderWidget.setVisible(not Settings()['floatingRenderer'] and (isPython or isMarkdown)) # Arguments: modified def modificationChanged(self, _=None): """Triggered when the content is changed""" self.__updateRunDebugButtons() def __updateRunDebugButtons(self): """Enables/disables the run and debug buttons as required""" enable = isPythonMime(self.__editor.mime) and \ not self.isModified() and \ not self.__debugMode and \ os.path.isabs(self.__fileName) if enable != self.runScriptButton.isEnabled(): self.runScriptButton.setEnabled(enable) self.profileScriptButton.setEnabled(enable) self.debugScriptButton.setEnabled(enable) self.deadCodeScriptButton.setEnabled(enable) self.sigTabRunChanged.emit(enable) def isTabRunEnabled(self): """Tells the status of run-like buttons""" return self.runScriptButton.isEnabled() def replaceAll(self, newText): """Replaces the current buffer content with a new text""" # Unfortunately, the setText() clears the undo history so it cannot be # used. The selectAll() and replacing selected text do not suite # because after undo the cursor does not jump to the previous position. # So, there is an ugly select -> replace manipulation below... with self.__editor: origLine, origPos = self.__editor.cursorPosition self.__editor.setSelection(0, 0, origLine, origPos) self.__editor.removeSelectedText() self.__editor.insert(newText) self.__editor.setCurrentPosition(len(newText)) line, pos = self.__editor.cursorPosition lastLine = self.__editor.lines() self.__editor.setSelection(line, pos, lastLine - 1, len(self.__editor.text(lastLine - 1))) self.__editor.removeSelectedText() self.__editor.cursorPosition = origLine, origPos # These two for the proper cursor positioning after redo self.__editor.insert("s") self.__editor.cursorPosition = origLine, origPos + 1 self.__editor.deleteBack() self.__editor.cursorPosition = origLine, origPos def onRemoveTrailingWS(self): """Triggers when the trailing spaces should be wiped out""" self.__editor.removeTrailingWhitespaces() def onExpandTabs(self): """Expands tabs if there are any""" self.__editor.expandTabs(4) def setFocus(self): """Overridden setFocus""" if self.__outsideChangesBar.isHidden(): self.__editor.setFocus() else: self.__outsideChangesBar.setFocus() def onImportDgmTuned(self): """Runs the settings dialog first""" if self.isModified(): what = ImportsDiagramDialog.SingleBuffer if not os.path.isabs(self.getFileName()): logging.warning("Imports diagram can only be generated for " "a file. Save the editor buffer " "and try again.") return else: what = ImportsDiagramDialog.SingleFile dlg = ImportsDiagramDialog(what, self.getFileName(), self) if dlg.exec_() == QDialog.Accepted: # Should proceed with the diagram generation self.__generateImportDiagram(what, dlg.options) # Arguments: action def onImportDgm(self, _=None): """Runs the generation process with default options""" if self.isModified(): what = ImportsDiagramDialog.SingleBuffer if not os.path.isabs(self.getFileName()): logging.warning("Imports diagram can only be generated for " "a file. Save the editor buffer " "and try again.") return else: what = ImportsDiagramDialog.SingleFile self.__generateImportDiagram(what, ImportDiagramOptions()) def __generateImportDiagram(self, what, options): """Show the generation progress and display the diagram""" if self.isModified(): progressDlg = ImportsDiagramProgress(what, options, self.getFileName(), self.__editor.text) tooltip = "Generated for modified buffer (" + \ self.getFileName() + ")" else: progressDlg = ImportsDiagramProgress(what, options, self.getFileName()) tooltip = "Generated for file " + self.getFileName() if progressDlg.exec_() == QDialog.Accepted: GlobalData().mainWindow.openDiagram(progressDlg.scene, tooltip) def onOpenImport(self): """Triggered when Ctrl+I is received""" if isPythonMime(self.__editor.mime): # Take all the file imports and resolve them fileImports = getImportsList(self.__editor.text) if not fileImports: GlobalData().mainWindow.showStatusBarMessage( "There are no imports") else: self.__onImportList(self.__fileName, fileImports) def __onImportList(self, fileName, imports): """Works with a list of imports""" # It has already been checked that the file is a Python one resolvedList, errors = resolveImports(fileName, imports) del errors # errors are OK here if resolvedList: # Display the import selection widget self.importsBar.showResolvedImports(resolvedList) else: GlobalData().mainWindow.showStatusBarMessage( "Could not resolve any imports") def resizeEvent(self, event): """Resizes the import selection dialogue if necessary""" self.__editor.hideCompleter() QWidget.resizeEvent(self, event) self.resizeBars() def resizeBars(self): """Resize the bars if they are shown""" if not self.importsBar.isHidden(): self.importsBar.resize() if not self.__outsideChangesBar.isHidden(): self.__outsideChangesBar.resize() self.__editor.resizeCalltip() def showOutsideChangesBar(self, allEnabled): """Shows the bar for the editor for the user to choose the action""" self.setReloadDialogShown(True) self.__outsideChangesBar.showChoice(self.isModified(), allEnabled) def __onReload(self): """Triggered when a request to reload the file is received""" self.sigReloadRequest.emit() def reload(self): """Called (from the editors manager) to reload the file""" # Re-read the file with updating the file timestamp self.readFile(self.__fileName) # Hide the bars, just in case both of them if not self.importsBar.isHidden(): self.importsBar.hide() if not self.__outsideChangesBar.isHidden(): self.__outsideChangesBar.hide() # Set the shown flag self.setReloadDialogShown(False) def reloadAllNonModified(self): """Request to reload all the non-modified files""" self.reloadAllNonModifiedRequest.emit() @staticmethod def onRunScript(action=None): """Runs the script""" del action # unused argument GlobalData().mainWindow.onRunTab() @staticmethod def onRunScriptDlg(): """Shows the run parameters dialogue""" GlobalData().mainWindow.onRunTabDlg() @staticmethod def onProfileScript(action=None): """Profiles the script""" del action # unused argument GlobalData().mainWindow.onProfileTab() @staticmethod def onProfileScriptDlg(): """Shows the profile parameters dialogue""" GlobalData().mainWindow.onProfileTabDlg() @staticmethod def onDebugScript(action=None): """Starts debugging""" del action # unused argument GlobalData().mainWindow.onDebugTab() @staticmethod def onDebugScriptDlg(): """Shows the debug parameters dialogue""" GlobalData().mainWindow.onDebugTabDlg() def getCFEditor(self): """Provides a reference to the control flow widget""" return self.__flowUI def cflowSyncRequested(self, absPos, line, pos): """Highlight the item closest to the absPos""" self.__flowUI.highlightAtAbsPos(absPos, line, pos) def passFocusToFlow(self): """Sets the focus to the graphics part""" if isPythonMime(self.__editor.mime): self.__flowUI.setFocus() return True return False def getMDView(self): """Provides a reference to the MD rendered view""" return self.__mdView # Mandatory interface part is below def getEditor(self): """Provides the editor widget""" return self.__editor def isModified(self): """Tells if the file is modified""" return self.__editor.document().isModified() def getRWMode(self): """Tells if the file is read only""" if not os.path.exists(self.__fileName): return None return 'RW' if QFileInfo(self.__fileName).isWritable() else 'RO' def getMime(self): """Provides the buffer mime""" return self.__editor.mime @staticmethod def getType(): """Tells the widget type""" return MainWindowTabWidgetBase.PlainTextEditor def getLanguage(self): """Tells the content language""" editorLanguage = self.__editor.language() if editorLanguage: return editorLanguage return self.__editor.mime if self.__editor.mime else 'n/a' def getFileName(self): """Tells what file name of the widget content""" return self.__fileName def setFileName(self, name): """Sets the file name""" self.__fileName = name self.__shortName = os.path.basename(name) def getEol(self): """Tells the EOL style""" return self.__editor.getEolIndicator() def getLine(self): """Tells the cursor line""" line, _ = self.__editor.cursorPosition return line def getPos(self): """Tells the cursor column""" _, pos = self.__editor.cursorPosition return pos def getEncoding(self): """Tells the content encoding""" if self.__editor.explicitUserEncoding: return self.__editor.explicitUserEncoding return self.__editor.encoding def getShortName(self): """Tells the display name""" return self.__shortName def setShortName(self, name): """Sets the display name""" self.__shortName = name def isDiskFileModified(self): """Return True if the loaded file is modified""" if not os.path.isabs(self.__fileName): return False if not os.path.exists(self.__fileName): return True path = os.path.realpath(self.__fileName) return self.__diskModTime != os.path.getmtime(path) or \ self.__diskSize != os.path.getsize(path) def doesFileExist(self): """Returns True if the loaded file still exists""" return os.path.exists(self.__fileName) def setReloadDialogShown(self, value=True): """Memorizes if the reloading dialogue has already been displayed""" self.__reloadDlgShown = value def getReloadDialogShown(self): """Tells if the reload dialog has already been shown""" return self.__reloadDlgShown and \ not self.__outsideChangesBar.isVisible() def updateModificationTime(self, fileName): """Updates the modification time""" path = os.path.realpath(fileName) self.__diskModTime = os.path.getmtime(path) self.__diskSize = os.path.getsize(path) def setDebugMode(self, debugOn, disableEditing): """Called to switch debug/development""" self.__debugMode = debugOn self.__editor.setDebugMode(debugOn, disableEditing) if debugOn: if disableEditing: # Undo/redo self.__undoButton.setEnabled(False) self.__redoButton.setEnabled(False) # Spaces/tabs/line self.removeTrailingSpacesButton.setEnabled(False) self.expandTabsButton.setEnabled(False) else: # Undo/redo self.__undoButton.setEnabled( self.__editor.document().isUndoAvailable()) self.__redoButton.setEnabled( self.__editor.document().isRedoAvailable()) # Spaces/tabs self.removeTrailingSpacesButton.setEnabled(True) self.expandTabsButton.setEnabled(True) # Run/debug buttons self.__updateRunDebugButtons() def isLineBreakable(self, line=None, enforceRecalc=False, enforceSure=False): """True if a breakpoint could be placed on the current line""" return self.__editor.isLineBreakable() def getVCSStatus(self): """Provides the VCS status""" return self.__vcsStatus def setVCSStatus(self, newStatus): """Sets the new VCS status""" self.__vcsStatus = newStatus # Floating renderer support def popRenderingWidgets(self): """Pops the rendering widgets""" self.__renderLayout.removeWidget(self.__flowUI) self.__renderLayout.removeWidget(self.__mdView) self.__renderWidget.setVisible(False) return [self.__flowUI, self.__mdView] def pushRenderingWidgets(self, widgets): """Returns back the rendering widgets""" for widget in widgets: self.__renderLayout.addWidget(widget) self.__languageChanged() # Sets the widget visibility
class VCSAnnotateViewerTabWidget(QWidget, MainWindowTabWidgetBase): " VCS annotate viewer tab widget " textEditorZoom = pyqtSignal(int) escapePressed = pyqtSignal() def __init__(self, parent): MainWindowTabWidgetBase.__init__(self) QWidget.__init__(self, parent) self.__viewer = VCSAnnotateViewer(self) self.__viewer.escapePressed.connect(self.__onEsc) self.__fileName = "" self.__shortName = "" self.__fileType = UnknownFileType self.__createLayout() self.__viewer.zoomTo(Settings().zoom) return def __onEsc(self): " Triggered when Esc is pressed " self.escapePressed.emit() return def __createLayout(self): " Creates the toolbar and layout " # Buttons printButton = QAction(PixmapCache().getIcon('printer.png'), 'Print', self) printButton.triggered.connect(self.__onPrint) printButton.setEnabled(False) printButton.setVisible(False) printPreviewButton = QAction(PixmapCache().getIcon('printpreview.png'), 'Print preview', self) printPreviewButton.triggered.connect(self.__onPrintPreview) printPreviewButton.setEnabled(False) printPreviewButton.setVisible(False) spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.lineCounterButton = QAction( PixmapCache().getIcon('linecounter.png'), 'Line counter', self) self.lineCounterButton.triggered.connect(self.onLineCounter) # The toolbar toolbar = QToolBar(self) toolbar.setOrientation(Qt.Vertical) toolbar.setMovable(False) toolbar.setAllowedAreas(Qt.RightToolBarArea) toolbar.setIconSize(QSize(16, 16)) toolbar.setFixedWidth(28) toolbar.setContentsMargins(0, 0, 0, 0) toolbar.addAction(printPreviewButton) toolbar.addAction(printButton) toolbar.addWidget(spacer) toolbar.addAction(self.lineCounterButton) self.__importsBar = ImportListWidget(self.__viewer) self.__importsBar.hide() hLayout = QHBoxLayout() hLayout.setContentsMargins(0, 0, 0, 0) hLayout.setSpacing(0) hLayout.addWidget(self.__viewer) hLayout.addWidget(toolbar) self.setLayout(hLayout) return def updateStatus(self): " Updates the toolbar buttons status " if self.__fileType == UnknownFileType: self.__fileType = self.getFileType() isPythonFile = self.__fileType in [PythonFileType, Python3FileType] self.lineCounterButton.setEnabled(isPythonFile) return def onZoomReset(self): " Triggered when the zoom reset button is pressed " if self.__viewer.zoom != 0: self.textEditorZoom.emit(0) return True def onZoomIn(self): " Triggered when the zoom in button is pressed " if self.__viewer.zoom < 20: self.textEditorZoom.emit(self.__viewer.zoom + 1) return True def onZoomOut(self): " Triggered when the zoom out button is pressed " if self.__viewer.zoom > -10: self.textEditorZoom.emit(self.__viewer.zoom - 1) return True def __onPrint(self): " Triggered when the print button is pressed " pass def __onPrintPreview(self): " triggered when the print preview button is pressed " pass def onLineCounter(self): " Triggered when line counter button is clicked " LineCounterDialog(None, self.__viewer).exec_() return def setFocus(self): " Overridden setFocus " self.__viewer.setFocus() return def onOpenImport(self): " Triggered when Ctrl+I is received " if self.__fileType not in [PythonFileType, Python3FileType]: return True # Python file, we may continue importLine, lineNo = isImportLine(self.__viewer) basePath = os.path.dirname(self.__fileName) if importLine: lineImports, importWhat = getImportsInLine(self.__viewer.text(), lineNo + 1) currentWord = str(self.__viewer.getCurrentWord(".")) if currentWord in lineImports: # The cursor is on some import path = resolveImport(basePath, currentWord) if path != '': GlobalData().mainWindow.openFile(path, -1) return True GlobalData().mainWindow.showStatusBarMessage( "The import '" + currentWord + "' is not resolved.", 0) return True # We are not on a certain import. # Check if it is a line with exactly one import if len(lineImports) == 1: path = resolveImport(basePath, lineImports[0]) if path == '': GlobalData().mainWindow.showStatusBarMessage( "The import '" + lineImports[0] + "' is not resolved.", 0) return True # The import is resolved. Check where we are. if currentWord in importWhat: # We are on a certain imported name in a resolved import # So, jump to the definition line line = getImportedNameDefinitionLine(path, currentWord) GlobalData().mainWindow.openFile(path, line) return True GlobalData().mainWindow.openFile(path, -1) return True # Take all the imports in the line and resolve them. self.__onImportList(basePath, lineImports) return True # Here: the cursor is not on the import line. Take all the file imports # and resolve them fileImports = getImportsList(self.__viewer.text()) if not fileImports: GlobalData().mainWindow.showStatusBarMessage( "There are no imports.", 0) return True if len(fileImports) == 1: path = resolveImport(basePath, fileImports[0]) if path == '': GlobalData().mainWindow.showStatusBarMessage( "The import '" + fileImports[0] + "' is not resolved.", 0) return True GlobalData().mainWindow.openFile(path, -1) return True self.__onImportList(basePath, fileImports) return True def __onImportList(self, basePath, imports): " Works with a list of imports " # It has already been checked that the file is a Python one resolvedList = resolveImports(basePath, imports) if not resolvedList: GlobalData().mainWindow.showStatusBarMessage( "No imports are resolved.", 0) return # Display the import selection widget self.__importsBar.showResolvedList(resolvedList) return def resizeEvent(self, event): " Resizes the import selection dialogue if necessary " QWidget.resizeEvent(self, event) self.resizeBars() return def resizeBars(self): " Resize the bars if they are shown " if not self.__importsBar.isHidden(): self.__importsBar.resize() self.__viewer.resizeCalltip() return def onPylint(self): return True def onPymetrics(self): return True def onRunScript(self, action=False): return True def onRunScriptSettings(self): return True def onProfileScript(self, action=False): return True def onProfileScriptSettings(self): return True def onImportDgm(self, action=None): return True def onImportDgmTuned(self): return True def shouldAcceptFocus(self): return True def setAnnotatedContent(self, shortName, text, lineRevisions, revisionInfo): " Sets the content " self.setShortName(shortName) self.__viewer.setAnnotatedContent(shortName, text, lineRevisions, revisionInfo) return def writeFile(self, fileName): " Writes the text to a file " return self.__viewer.writeFile(fileName) def updateModificationTime(self, fileName): return # Mandatory interface part is below def getEditor(self): " Provides the editor widget " return self.__viewer def isModified(self): " Tells if the file is modified " return False def getRWMode(self): " Tells if the file is read only " return "RO" def getFileType(self): " Provides the file type " if self.__fileType == UnknownFileType: if self.__shortName: self.__fileType = detectFileType(self.__shortName) return self.__fileType def setFileType(self, typeToSet): """ Sets the file type explicitly. It needs e.g. for .cgi files which can change its type """ self.__fileType = typeToSet return def getType(self): " Tells the widget type " return MainWindowTabWidgetBase.VCSAnnotateViewer def getLanguage(self): " Tells the content language " if self.__fileType == UnknownFileType: self.__fileType = self.getFileType() if self.__fileType != UnknownFileType: return getFileLanguage(self.__fileType) return self.__viewer.getLanguage() def getFileName(self): " Tells what file name of the widget content " return "n/a" def setFileName(self, name): " Sets the file name " raise Exception( "Setting a file name for annotate results is not applicable") def getEol(self): " Tells the EOL style " return self.__viewer.getEolIndicator() def getLine(self): " Tells the cursor line " line, pos = self.__viewer.getCursorPosition() return int(line) def getPos(self): " Tells the cursor column " line, pos = self.__viewer.getCursorPosition() return int(pos) def getEncoding(self): " Tells the content encoding " return self.__viewer.encoding def setEncoding(self, newEncoding): " Sets the new editor encoding " self.__viewer.setEncoding(newEncoding) return def getShortName(self): " Tells the display name " return self.__shortName def setShortName(self, name): " Sets the display name " self.__shortName = name self.__fileType = detectFileType(name) return
class VCSAnnotateViewerTabWidget( QWidget, MainWindowTabWidgetBase ): " VCS annotate viewer tab widget " textEditorZoom = pyqtSignal( int ) escapePressed = pyqtSignal() def __init__( self, parent ): MainWindowTabWidgetBase.__init__( self ) QWidget.__init__( self, parent ) self.__viewer = VCSAnnotateViewer( self ) self.__viewer.escapePressed.connect( self.__onEsc ) self.__fileName = "" self.__shortName = "" self.__fileType = UnknownFileType self.__createLayout() self.__viewer.zoomTo( Settings().zoom ) return def __onEsc( self ): " Triggered when Esc is pressed " self.escapePressed.emit() return def __createLayout( self ): " Creates the toolbar and layout " # Buttons printButton = QAction( PixmapCache().getIcon( 'printer.png' ), 'Print', self ) printButton.triggered.connect( self.__onPrint ) printButton.setEnabled( False ) printButton.setVisible( False ) printPreviewButton = QAction( PixmapCache().getIcon( 'printpreview.png' ), 'Print preview', self ) printPreviewButton.triggered.connect( self.__onPrintPreview ) printPreviewButton.setEnabled( False ) printPreviewButton.setVisible( False ) spacer = QWidget() spacer.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Expanding ) self.lineCounterButton = QAction( PixmapCache().getIcon( 'linecounter.png' ), 'Line counter', self ) self.lineCounterButton.triggered.connect( self.onLineCounter ) # The toolbar toolbar = QToolBar( self ) toolbar.setOrientation( Qt.Vertical ) toolbar.setMovable( False ) toolbar.setAllowedAreas( Qt.RightToolBarArea ) toolbar.setIconSize( QSize( 16, 16 ) ) toolbar.setFixedWidth( 28 ) toolbar.setContentsMargins( 0, 0, 0, 0 ) toolbar.addAction( printPreviewButton ) toolbar.addAction( printButton ) toolbar.addWidget( spacer ) toolbar.addAction( self.lineCounterButton ) self.__importsBar = ImportListWidget( self.__viewer ) self.__importsBar.hide() hLayout = QHBoxLayout() hLayout.setContentsMargins( 0, 0, 0, 0 ) hLayout.setSpacing( 0 ) hLayout.addWidget( self.__viewer ) hLayout.addWidget( toolbar ) self.setLayout( hLayout ) return def updateStatus( self ): " Updates the toolbar buttons status " if self.__fileType == UnknownFileType: self.__fileType = self.getFileType() isPythonFile = self.__fileType in [ PythonFileType, Python3FileType ] self.lineCounterButton.setEnabled( isPythonFile ) return def onZoomReset( self ): " Triggered when the zoom reset button is pressed " if self.__viewer.zoom != 0: self.textEditorZoom.emit( 0 ) return True def onZoomIn( self ): " Triggered when the zoom in button is pressed " if self.__viewer.zoom < 20: self.textEditorZoom.emit( self.__viewer.zoom + 1 ) return True def onZoomOut( self ): " Triggered when the zoom out button is pressed " if self.__viewer.zoom > -10: self.textEditorZoom.emit( self.__viewer.zoom - 1 ) return True def __onPrint( self ): " Triggered when the print button is pressed " pass def __onPrintPreview( self ): " triggered when the print preview button is pressed " pass def onLineCounter( self ): " Triggered when line counter button is clicked " LineCounterDialog( None, self.__viewer ).exec_() return def setFocus( self ): " Overridden setFocus " self.__viewer.setFocus() return def onOpenImport( self ): " Triggered when Ctrl+I is received " if self.__fileType not in [ PythonFileType, Python3FileType ]: return True # Python file, we may continue importLine, lineNo = isImportLine( self.__viewer ) basePath = os.path.dirname( self.__fileName ) if importLine: lineImports, importWhat = getImportsInLine( self.__viewer.text(), lineNo + 1 ) currentWord = str( self.__viewer.getCurrentWord( "." ) ) if currentWord in lineImports: # The cursor is on some import path = resolveImport( basePath, currentWord ) if path != '': GlobalData().mainWindow.openFile( path, -1 ) return True GlobalData().mainWindow.showStatusBarMessage( "The import '" + currentWord + "' is not resolved.", 0 ) return True # We are not on a certain import. # Check if it is a line with exactly one import if len( lineImports ) == 1: path = resolveImport( basePath, lineImports[ 0 ] ) if path == '': GlobalData().mainWindow.showStatusBarMessage( "The import '" + lineImports[ 0 ] + "' is not resolved.", 0 ) return True # The import is resolved. Check where we are. if currentWord in importWhat: # We are on a certain imported name in a resolved import # So, jump to the definition line line = getImportedNameDefinitionLine( path, currentWord ) GlobalData().mainWindow.openFile( path, line ) return True GlobalData().mainWindow.openFile( path, -1 ) return True # Take all the imports in the line and resolve them. self.__onImportList( basePath, lineImports ) return True # Here: the cursor is not on the import line. Take all the file imports # and resolve them fileImports = getImportsList( self.__viewer.text() ) if not fileImports: GlobalData().mainWindow.showStatusBarMessage( "There are no imports.", 0 ) return True if len( fileImports ) == 1: path = resolveImport( basePath, fileImports[ 0 ] ) if path == '': GlobalData().mainWindow.showStatusBarMessage( "The import '" + fileImports[ 0 ] + "' is not resolved.", 0 ) return True GlobalData().mainWindow.openFile( path, -1 ) return True self.__onImportList( basePath, fileImports ) return True def __onImportList( self, basePath, imports ): " Works with a list of imports " # It has already been checked that the file is a Python one resolvedList = resolveImports( basePath, imports ) if not resolvedList: GlobalData().mainWindow.showStatusBarMessage( "No imports are resolved.", 0 ) return # Display the import selection widget self.__importsBar.showResolvedList( resolvedList ) return def resizeEvent( self, event ): " Resizes the import selection dialogue if necessary " QWidget.resizeEvent( self, event ) self.resizeBars() return def resizeBars( self ): " Resize the bars if they are shown " if not self.__importsBar.isHidden(): self.__importsBar.resize() self.__viewer.resizeCalltip() return def onPylint( self ): return True def onPymetrics( self ): return True def onRunScript( self, action = False ): return True def onRunScriptSettings( self ): return True def onProfileScript( self, action = False ): return True def onProfileScriptSettings( self ): return True def onImportDgm( self, action = None ): return True def onImportDgmTuned( self ): return True def shouldAcceptFocus( self ): return True def setAnnotatedContent( self, shortName, text, lineRevisions, revisionInfo ): " Sets the content " self.setShortName( shortName ) self.__viewer.setAnnotatedContent( shortName, text, lineRevisions, revisionInfo ) return def writeFile( self, fileName ): " Writes the text to a file " return self.__viewer.writeFile( fileName ) def updateModificationTime( self, fileName ): return # Mandatory interface part is below def getEditor( self ): " Provides the editor widget " return self.__viewer def isModified( self ): " Tells if the file is modified " return False def getRWMode( self ): " Tells if the file is read only " return "RO" def getFileType( self ): " Provides the file type " if self.__fileType == UnknownFileType: if self.__shortName: self.__fileType = detectFileType( self.__shortName ) return self.__fileType def setFileType( self, typeToSet ): """ Sets the file type explicitly. It needs e.g. for .cgi files which can change its type """ self.__fileType = typeToSet return def getType( self ): " Tells the widget type " return MainWindowTabWidgetBase.VCSAnnotateViewer def getLanguage( self ): " Tells the content language " if self.__fileType == UnknownFileType: self.__fileType = self.getFileType() if self.__fileType != UnknownFileType: return getFileLanguage( self.__fileType ) return self.__viewer.getLanguage() def getFileName( self ): " Tells what file name of the widget content " return "n/a" def setFileName( self, name ): " Sets the file name " raise Exception( "Setting a file name for annotate results is not applicable" ) def getEol( self ): " Tells the EOL style " return self.__viewer.getEolIndicator() def getLine( self ): " Tells the cursor line " line, pos = self.__viewer.getCursorPosition() return int( line ) def getPos( self ): " Tells the cursor column " line, pos = self.__viewer.getCursorPosition() return int( pos ) def getEncoding( self ): " Tells the content encoding " return self.__viewer.encoding def setEncoding( self, newEncoding ): " Sets the new editor encoding " self.__viewer.setEncoding( newEncoding ) return def getShortName( self ): " Tells the display name " return self.__shortName def setShortName( self, name ): " Sets the display name " self.__shortName = name self.__fileType = detectFileType( name ) return
class VCSAnnotateViewerTabWidget(QWidget, MainWindowTabWidgetBase): """VCS annotate viewer tab widget""" sigEscapePressed = pyqtSignal() def __init__(self, parent): MainWindowTabWidgetBase.__init__(self) QWidget.__init__(self, parent) self.__viewer = VCSAnnotateViewer(self) self.__viewer.sigEscapePressed.connect(self.__onEsc) self.__fileName = "" self.__shortName = "" self.__createLayout() self.__viewer.zoomTo(Settings()['zoom']) def __onEsc(self): """Triggered when Esc is pressed""" self.sigEscapePressed.emit() def __createLayout(self): """Creates the toolbar and layout""" # Buttons printButton = QAction(getIcon('printer.png'), 'Print', self) printButton.triggered.connect(self.__onPrint) printButton.setEnabled(False) printButton.setVisible(False) printPreviewButton = QAction(getIcon('printpreview.png'), 'Print preview', self) printPreviewButton.triggered.connect(self.__onPrintPreview) printPreviewButton.setEnabled(False) printPreviewButton.setVisible(False) spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) # The toolbar toolbar = QToolBar(self) toolbar.setOrientation(Qt.Vertical) toolbar.setMovable(False) toolbar.setAllowedAreas(Qt.RightToolBarArea) toolbar.setIconSize(QSize(16, 16)) toolbar.setFixedWidth(28) toolbar.setContentsMargins(0, 0, 0, 0) toolbar.addAction(printPreviewButton) toolbar.addAction(printButton) toolbar.addWidget(spacer) self.__importsBar = ImportListWidget(self.__viewer) self.__importsBar.hide() hLayout = QHBoxLayout() hLayout.setContentsMargins(0, 0, 0, 0) hLayout.setSpacing(0) hLayout.addWidget(self.__viewer) hLayout.addWidget(toolbar) self.setLayout(hLayout) def updateStatus(self): """Updates the toolbar buttons status""" pass def __onPrint(self): """Triggered when the print button is pressed""" pass def __onPrintPreview(self): """triggered when the print preview button is pressed""" pass def setFocus(self): """Overridden setFocus""" self.__viewer.setFocus() def onOpenImport(self): """Triggered when Ctrl+I is received""" if isPythonMime(self.__editor.mime): # Take all the file imports and resolve them fileImports = getImportsList(self.__editor.text) if not fileImports: GlobalData().mainWindow.showStatusBarMessage( "There are no imports") else: self.__onImportList(self.__fileName, fileImports) def __onImportList(self, fileName, imports): """Works with a list of imports""" # It has already been checked that the file is a Python one resolvedList, errors = resolveImports(fileName, imports) del errors # errors are OK here if resolvedList: # Display the import selection widget self.__importsBar.showResolvedImports(resolvedList) else: GlobalData().mainWindow.showStatusBarMessage( "Could not resolve any imports") def resizeEvent(self, event): """Resizes the import selection dialogue if necessary""" QWidget.resizeEvent(self, event) self.resizeBars() def resizeBars(self): """Resize the bars if they are shown""" if not self.__importsBar.isHidden(): self.__importsBar.resize() self.__viewer.resizeCalltip() def onRunScript(self, action=False): return True def onRunScriptSettings(self): return True def onProfileScript(self, action=False): return True def onProfileScriptSettings(self): return True def onImportDgm(self, action=None): return True def onImportDgmTuned(self): return True def shouldAcceptFocus(self): return True def setAnnotatedContent(self, shortName, text, lineRevisions, revisionInfo): """Sets the content""" self.setShortName(shortName) self.__viewer.setAnnotatedContent(shortName, text, lineRevisions, revisionInfo) def writeFile(self, fileName): """Writes the text to a file""" return self.__viewer.writeFile(fileName) def updateModificationTime(self, fileName): """No need to implement""" return # Mandatory interface part is below def getEditor(self): """Provides the editor widget""" return self.__viewer def isModified(self): """Tells if the file is modified""" return False def getRWMode(self): """Tells if the file is read only""" return "RO" def getMime(self): """Provides the file type""" return self.__viewer.mime def getType(self): """Tells the widget type""" return MainWindowTabWidgetBase.VCSAnnotateViewer def getLanguage(self): """Tells the content language""" lang = self.__viewer.language() if lang: return lang return self.__viewer.mime if self.__viewer.mime else 'n/a' def setFileName(self, name): """Sets the file name""" raise Exception("Setting a file name for " "annotate results is not applicable") def getEol(self): """Tells the EOL style""" return self.__viewer.getEolIndicator() def getLine(self): """Tells the cursor line""" line, _ = self.__viewer.cursorPosition return line def getPos(self): """Tells the cursor column""" _, pos = self.__viewer.cursorPosition return pos def getEncoding(self): """Tells the content encoding""" return self.__viewer.encoding def setEncoding(self, newEncoding): """Sets the new editor encoding""" self.__viewer.setEncoding(newEncoding) def getShortName(self): """Tells the display name""" return self.__shortName def setShortName(self, name): """Sets the display name""" self.__shortName = name