def _update(self, action: QAction, initialize: bool): """updates the internal values from the given action.""" if not self._action: return self._disconnectAction() self.changed.disconnect(self._updateToolTipWithKeySequence) if initialize: self.setSeparator(action.isSeparator()) self.setMenuRole(action.menuRole()) if self.hasAttribute(ProxyActionAttribute.UpdateIcon) or initialize: self.setIcon(action.icon()) self.setIconText(action.iconText()) self.setIconVisibleInMenu(action.isIconVisibleInMenu()) if self.hasAttribute(ProxyActionAttribute.UpdateText) or initialize: self.setText(action.text()) self._toolTip = action.toolTip() self._updateToolTipWithKeySequence() self.setStatusTip(action.statusTip()) self.setWhatsThis(action.whatsThis()) self.setCheckable(action.isCheckable()) if not initialize: self.setChecked(action.isChecked()) self.setEnabled(action.isEnabled()) self.setVisible(action.isVisible()) self._connectAction() self.changed.connect(self._updateToolTipWithKeySequence)
def __init__(self): super().__init__() #: widget's runtime state self.__state = State.NoState self._imageMeta = [] self._imageCategories = {} self.__invalidated = False self.__pendingTask = None vbox = gui.vBox(self.controlArea) hbox = gui.hBox(vbox) self.recent_cb = QComboBox( sizeAdjustPolicy=QComboBox.AdjustToMinimumContentsLengthWithIcon, minimumContentsLength=16, ) self.recent_cb.activated[int].connect(self.__onRecentActivated) icons = standard_icons(self) browseaction = QAction( "Open/Load Images", self, iconText="\N{HORIZONTAL ELLIPSIS}", icon=icons.dir_open_icon, toolTip="Select a directory from which to load the images" ) browseaction.triggered.connect(self.__runOpenDialog) reloadaction = QAction( "Reload", self, icon=icons.reload_icon, toolTip="Reload current image set" ) reloadaction.triggered.connect(self.reload) self.__actions = namespace( browse=browseaction, reload=reloadaction, ) browsebutton = QPushButton( browseaction.iconText(), icon=browseaction.icon(), toolTip=browseaction.toolTip(), clicked=browseaction.trigger ) reloadbutton = QPushButton( reloadaction.iconText(), icon=reloadaction.icon(), clicked=reloadaction.trigger, default=True, ) hbox.layout().addWidget(self.recent_cb) hbox.layout().addWidget(browsebutton) hbox.layout().addWidget(reloadbutton) self.addActions([browseaction, reloadaction]) reloadaction.changed.connect( lambda: reloadbutton.setEnabled(reloadaction.isEnabled()) ) box = gui.vBox(vbox, "Info") self.infostack = QStackedWidget() self.info_area = QLabel( text="No image set selected", wordWrap=True ) self.progress_widget = QProgressBar( minimum=0, maximum=0 ) self.cancel_button = QPushButton( "Cancel", icon=icons.cancel_icon, ) self.cancel_button.clicked.connect(self.cancel) w = QWidget() vlayout = QVBoxLayout() vlayout.setContentsMargins(0, 0, 0, 0) hlayout = QHBoxLayout() hlayout.setContentsMargins(0, 0, 0, 0) hlayout.addWidget(self.progress_widget) hlayout.addWidget(self.cancel_button) vlayout.addLayout(hlayout) self.pathlabel = TextLabel() self.pathlabel.setTextElideMode(Qt.ElideMiddle) self.pathlabel.setAttribute(Qt.WA_MacSmallSize) vlayout.addWidget(self.pathlabel) w.setLayout(vlayout) self.infostack.addWidget(self.info_area) self.infostack.addWidget(w) box.layout().addWidget(self.infostack) self.__initRecentItemsModel() self.__invalidated = True self.__executor = ThreadExecutor(self) QApplication.postEvent(self, QEvent(RuntimeEvent.Init))
class ClientExceptionsViewer( QWidget ): " Implements the client exceptions viewer for a debugger " def __init__( self, parent, ignoredExceptionsViewer ): QWidget.__init__( self, parent ) self.__ignoredExceptionsViewer = ignoredExceptionsViewer self.__currentItem = None self.__createPopupMenu() self.__createLayout() GlobalData().project.projectChanged.connect( self.__onProjectChanged ) return def setFocus( self ): " Sets the widget focus " self.exceptionsList.setFocus() return def __createPopupMenu( self ): " Creates the popup menu " self.__excptMenu = QMenu() self.__addToIgnoreMenuItem = self.__excptMenu.addAction( "Add to ignore list", self.__onAddToIgnore ) self.__jumpToCodeMenuItem = self.__excptMenu.addAction( "Jump to code", self.__onJumpToCode ) return def __createLayout( self ): " Creates the widget layout " verticalLayout = QVBoxLayout( self ) verticalLayout.setContentsMargins( 0, 0, 0, 0 ) verticalLayout.setSpacing( 0 ) self.headerFrame = QFrame() self.headerFrame.setFrameStyle( QFrame.StyledPanel ) self.headerFrame.setAutoFillBackground( True ) headerPalette = self.headerFrame.palette() headerBackground = headerPalette.color( QPalette.Background ) headerBackground.setRgb( min( headerBackground.red() + 30, 255 ), min( headerBackground.green() + 30, 255 ), min( headerBackground.blue() + 30, 255 ) ) headerPalette.setColor( QPalette.Background, headerBackground ) self.headerFrame.setPalette( headerPalette ) self.headerFrame.setFixedHeight( 24 ) self.__excptLabel = QLabel( "Exceptions" ) fixedSpacer = QSpacerItem( 3, 3 ) headerLayout = QHBoxLayout() headerLayout.setContentsMargins( 0, 0, 0, 0 ) headerLayout.addSpacerItem( fixedSpacer ) headerLayout.addWidget( self.__excptLabel ) self.headerFrame.setLayout( headerLayout ) self.exceptionsList = QTreeWidget( self ) self.exceptionsList.setSortingEnabled( False ) self.exceptionsList.setAlternatingRowColors( True ) self.exceptionsList.setRootIsDecorated( True ) self.exceptionsList.setItemsExpandable( True ) self.exceptionsList.setUniformRowHeights( True ) self.exceptionsList.setSelectionMode( QAbstractItemView.SingleSelection ) self.exceptionsList.setSelectionBehavior( QAbstractItemView.SelectRows ) self.exceptionsList.setItemDelegate( NoOutlineHeightDelegate( 4 ) ) self.exceptionsList.setContextMenuPolicy( Qt.CustomContextMenu ) self.__addToIgnoreButton = QAction( PixmapCache().getIcon( 'add.png' ), "Add exception to the list of ignored", self ) self.__addToIgnoreButton.triggered.connect( self.__onAddToIgnore ) self.__addToIgnoreButton.setEnabled( False ) expandingSpacer = QWidget() expandingSpacer.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Expanding ) self.__jumpToCodeButton = QAction( PixmapCache().getIcon( 'gotoline.png' ), "Jump to the code", self ) self.__jumpToCodeButton.triggered.connect( self.__onJumpToCode ) self.__jumpToCodeButton.setEnabled( False ) self.__delAllButton = QAction( PixmapCache().getIcon( 'trash.png' ), "Delete all the client exceptions", self ) self.__delAllButton.triggered.connect( self.__onDelAll ) self.__delAllButton.setEnabled( False ) self.toolbar = QToolBar() self.toolbar.setOrientation( Qt.Horizontal ) self.toolbar.setMovable( False ) self.toolbar.setAllowedAreas( Qt.TopToolBarArea ) self.toolbar.setIconSize( QSize( 16, 16 ) ) self.toolbar.setFixedHeight( 28 ) self.toolbar.setContentsMargins( 0, 0, 0, 0 ) self.toolbar.addAction( self.__addToIgnoreButton ) self.toolbar.addAction( self.__jumpToCodeButton ) self.toolbar.addWidget( expandingSpacer ) self.toolbar.addAction( self.__delAllButton ) self.exceptionsList.itemDoubleClicked.connect( self.__onExceptionDoubleClicked ) self.exceptionsList.customContextMenuRequested.connect( self.__showContextMenu ) self.exceptionsList.itemSelectionChanged.connect( self.__onSelectionChanged ) self.exceptionsList.setHeaderLabels( [ "Exception" ] ) verticalLayout.addWidget( self.headerFrame ) verticalLayout.addWidget( self.toolbar ) verticalLayout.addWidget( self.exceptionsList ) return def clear( self ): " Clears the content " self.exceptionsList.clear() self.__updateExceptionsLabel() self.__addToIgnoreButton.setEnabled( False ) self.__jumpToCodeButton.setEnabled( False ) self.__delAllButton.setEnabled( False ) self.__currentItem = None self.emit( SIGNAL( 'ClientExceptionsCleared' ) ) return def __onExceptionDoubleClicked( self, item, column ): " Triggered when an exception is double clicked " if self.__currentItem is not None: if self.__currentItem.getType() == STACK_FRAME_ITEM: self.__onJumpToCode() return # This is an exception item itself. # Open a separate dialog window with th detailed info. return def __showContextMenu( self, coord ): " Shows the frames list context menu " self.__contextItem = self.exceptionsList.itemAt( coord ) self.__addToIgnoreMenuItem.setEnabled( self.__addToIgnoreButton.isEnabled() ) self.__jumpToCodeMenuItem.setEnabled( self.__jumpToCodeButton.isEnabled() ) if self.__contextItem is not None: self.__excptMenu.popup( QCursor.pos() ) return def __onAddToIgnore( self ): " Adds an exception into the ignore list " if self.__currentItem is not None: self.__ignoredExceptionsViewer.addExceptionFilter( str( self.__currentItem.getExceptionType() ) ) self.__addToIgnoreButton.setEnabled( False ) return def __onJumpToCode( self ): " Jumps to the corresponding source code line " if self.__currentItem is not None: if self.__currentItem.getType() == STACK_FRAME_ITEM: fileName = self.__currentItem.getFileName() if '<' not in fileName and '>' not in fileName: lineNumber = self.__currentItem.getLineNumber() editorsManager = GlobalData().mainWindow.editorsManager() editorsManager.openFile( fileName, lineNumber ) editor = editorsManager.currentWidget().getEditor() editor.gotoLine( lineNumber ) editorsManager.currentWidget().setFocus() return def __onDelAll( self ): " Triggered when all the exceptions should be deleted " self.clear() return def addException( self, exceptionType, exceptionMessage, stackTrace ): " Adds the exception to the view " for index in xrange( self.exceptionsList.topLevelItemCount() ): item = self.exceptionsList.topLevelItem( index ) if item.equal( exceptionType, exceptionMessage, stackTrace ): item.incrementCounter() self.exceptionsList.clearSelection() self.exceptionsList.setCurrentItem( item ) self.__updateExceptionsLabel() return item = ExceptionItem( self.exceptionsList, exceptionType, exceptionMessage, stackTrace ) self.exceptionsList.clearSelection() self.exceptionsList.setCurrentItem( item ) self.__updateExceptionsLabel() self.__delAllButton.setEnabled( True ) return def __updateExceptionsLabel( self ): " Updates the exceptions header label " total = self.getTotalCount() if total > 0: self.__excptLabel.setText( "Exceptions (total: " + str( total ) + ")" ) else: self.__excptLabel.setText( "Exceptions" ) return def getTotalCount( self ): " Provides the total number of exceptions " count = 0 for index in xrange( self.exceptionsList.topLevelItemCount() ): count += self.exceptionsList.topLevelItem( index ).getCount() return count def __onProjectChanged( self, what ): " Triggered when a project is changed " if what == CodimensionProject.CompleteProject: self.clear() return def __onSelectionChanged( self ): " Triggered when the current item is changed " selected = list( self.exceptionsList.selectedItems() ) if selected: self.__currentItem = selected[ 0 ] if self.__currentItem.getType() == STACK_FRAME_ITEM: fileName = self.__currentItem.getFileName() if '<' in fileName or '>' in fileName: self.__jumpToCodeButton.setEnabled( False ) else: self.__jumpToCodeButton.setEnabled( True ) self.__addToIgnoreButton.setEnabled( False ) else: self.__jumpToCodeButton.setEnabled( False ) excType = str( self.__currentItem.getExceptionType() ) if self.__ignoredExceptionsViewer.isIgnored( excType ) or \ " " in excType or excType.startswith( "unhandled" ): self.__addToIgnoreButton.setEnabled( False ) else: self.__addToIgnoreButton.setEnabled( True ) else: self.__currentItem = None self.__addToIgnoreButton.setEnabled( False ) self.__jumpToCodeButton.setEnabled( False ) return
def __init__(self): super().__init__() #: widget's runtime state self.__state = State.NoState self._imageMeta = [] self._imageCategories = {} self.__invalidated = False self.__pendingTask = None vbox = gui.vBox(self.controlArea) hbox = gui.hBox(vbox) self.recent_cb = QComboBox( sizeAdjustPolicy=QComboBox.AdjustToMinimumContentsLengthWithIcon, minimumContentsLength=16, ) self.recent_cb.activated[int].connect(self.__onRecentActivated) icons = standard_icons(self) browseaction = QAction( "Open/Load Images", self, iconText="\N{HORIZONTAL ELLIPSIS}", icon=icons.dir_open_icon, toolTip="Select a directory from which to load the images") browseaction.triggered.connect(self.__runOpenDialog) reloadaction = QAction("Reload", self, icon=icons.reload_icon, toolTip="Reload current image set") reloadaction.triggered.connect(self.reload) self.__actions = namespace( browse=browseaction, reload=reloadaction, ) browsebutton = QPushButton(browseaction.iconText(), icon=browseaction.icon(), toolTip=browseaction.toolTip(), clicked=browseaction.trigger) reloadbutton = QPushButton( reloadaction.iconText(), icon=reloadaction.icon(), clicked=reloadaction.trigger, default=True, ) hbox.layout().addWidget(self.recent_cb) hbox.layout().addWidget(browsebutton) hbox.layout().addWidget(reloadbutton) self.addActions([browseaction, reloadaction]) reloadaction.changed.connect( lambda: reloadbutton.setEnabled(reloadaction.isEnabled())) box = gui.vBox(vbox, "Info") self.infostack = QStackedWidget() self.info_area = QLabel(text="No image set selected", wordWrap=True) self.progress_widget = QProgressBar(minimum=0, maximum=0) self.cancel_button = QPushButton( "Cancel", icon=icons.cancel_icon, ) self.cancel_button.clicked.connect(self.cancel) w = QWidget() vlayout = QVBoxLayout() vlayout.setContentsMargins(0, 0, 0, 0) hlayout = QHBoxLayout() hlayout.setContentsMargins(0, 0, 0, 0) hlayout.addWidget(self.progress_widget) hlayout.addWidget(self.cancel_button) vlayout.addLayout(hlayout) self.pathlabel = TextLabel() self.pathlabel.setTextElideMode(Qt.ElideMiddle) self.pathlabel.setAttribute(Qt.WA_MacSmallSize) vlayout.addWidget(self.pathlabel) w.setLayout(vlayout) self.infostack.addWidget(self.info_area) self.infostack.addWidget(w) box.layout().addWidget(self.infostack) self.__initRecentItemsModel() self.__invalidated = True self.__executor = ThreadExecutor(self) QApplication.postEvent(self, QEvent(RuntimeEvent.Init))
class FunctionsViewer( QWidget ): """ The free functions (including nested) viewer widget """ def __init__( self, parent = None ): QWidget.__init__( self, parent ) self.filterEdit = None self.definitionButton = None self.findButton = None self.copyPathButton = None self.funcViewer = None self.__createLayout() # create the context menu self.__menu = QMenu( self ) self.__jumpMenuItem = self.__menu.addAction( PixmapCache().getIcon( 'definition.png' ), 'Jump to definition', self.__goToDefinition ) self.__menu.addSeparator() self.__findMenuItem = self.__menu.addAction( PixmapCache().getIcon( 'findusage.png' ), 'Find occurences', self.__findWhereUsed ) self.__menu.addSeparator() self.__disasmMenuItem = self.__menu.addAction( PixmapCache().getIcon( 'disasmmenu.png' ), 'Disassemble', self.__onDisassemble ) self.__menu.addSeparator() self.__copyMenuItem = self.__menu.addAction( PixmapCache().getIcon( 'copytoclipboard.png' ), 'Copy path to clipboard', self.funcViewer.copyToClipboard ) self.funcViewer.setContextMenuPolicy( Qt.CustomContextMenu ) self.funcViewer.customContextMenuRequested.connect( self.__handleShowContextMenu ) GlobalData().project.projectChanged.connect( self.__onProjectChanged ) self.connect( self.funcViewer, SIGNAL( "selectionChanged" ), self.__selectionChanged ) self.funcViewer.openingItem.connect( self.itemActivated ) self.connect( self.funcViewer, SIGNAL( "modelFilesChanged" ), self.modelFilesChanged ) self.filterEdit.lineEdit().setFocus() self.__contextItem = None return def setTooltips( self, switchOn ): " Triggers showing python objects tooltips " self.funcViewer.model().sourceModel().setTooltips( switchOn ) return def __createLayout( self ): " Helper to create the viewer layout " self.funcViewer = FunctionsBrowser() # Toolbar part - buttons self.definitionButton = QAction( PixmapCache().getIcon( 'definition.png' ), 'Jump to highlighted item definition', self ) self.definitionButton.triggered.connect( self.__goToDefinition ) self.findButton = QAction( PixmapCache().getIcon( 'findusage.png' ), 'Find highlighted item occurences', self ) self.findButton.triggered.connect( self.__findWhereUsed ) self.copyPathButton = QAction( PixmapCache().getIcon( 'copytoclipboard.png' ), 'Copy path to clipboard', self ) self.copyPathButton.triggered.connect( self.funcViewer.copyToClipboard ) self.findNotUsedButton = QAction( PixmapCache().getIcon( 'notused.png' ), 'Unused function analysis', self ) self.findNotUsedButton.triggered.connect( self.__findNotUsed ) self.findNotUsedButton.setEnabled( False ) self.toolbar = QToolBar( self ) self.toolbar.setMovable( False ) self.toolbar.setAllowedAreas( Qt.TopToolBarArea ) self.toolbar.setIconSize( QSize( 16, 16 ) ) self.toolbar.setFixedHeight( 28 ) self.toolbar.setContentsMargins( 0, 0, 0, 0 ) self.toolbar.addAction( self.definitionButton ) self.toolbar.addAction( self.findButton ) self.toolbar.addAction( self.copyPathButton ) filterLabel = QLabel( " Filter " ) self.toolbar.addWidget( filterLabel ) self.filterEdit = CDMComboBox( True, self.toolbar ) self.filterEdit.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Expanding ) self.filterEdit.lineEdit().setToolTip( "Space separated regular expressions" ) self.toolbar.addWidget( self.filterEdit ) self.toolbar.addAction( self.findNotUsedButton ) self.filterEdit.editTextChanged.connect( self.__filterChanged ) self.filterEdit.itemAdded.connect( self.__filterItemAdded ) self.filterEdit.enterClicked.connect( self.__enterInFilter ) layout = QVBoxLayout() layout.setContentsMargins( 0, 0, 0, 0 ) layout.setSpacing( 0 ) layout.addWidget( self.toolbar ) layout.addWidget( self.funcViewer ) self.setLayout( layout ) return def __filterChanged( self, text ): " Triggers when the filter text changed " self.funcViewer.setFilter( text ) self.funcViewer.updateCounter() return def __selectionChanged( self, index ): " Handles the changed selection " if index is None: self.__contextItem = None else: self.__contextItem = self.funcViewer.model().item( index ) self.__updateButtons() return def getItemCount( self ): " Provides the number of items in the model - total, not only visible " return self.funcViewer.model().sourceModel().rowCount() def itemActivated( self, path, line ): " Handles the item activation " self.filterEdit.addItem( self.filterEdit.lineEdit().text() ) return def __filterItemAdded( self ): " The filter item has been added " project = GlobalData().project if project.fileName != "": project.setFindFuncHistory( self.filterEdit.getItems() ) return def __enterInFilter( self ): " ENTER key has been clicked in the filter " # check if there any records displayed if self.funcViewer.model().rowCount() == 0: return # Move the focus to the list and select the first row self.funcViewer.clearSelection() flags = QItemSelectionModel.SelectCurrent | QItemSelectionModel.Rows self.funcViewer.setSelection( QRect( 0, 0, self.funcViewer.width(), 1 ), flags ) self.funcViewer.setFocus() return def __onProjectChanged( self, what ): " Triggered when a project is changed " if what == CodimensionProject.CompleteProject: self.__contextItem = None self.__updateButtons() self.filterEdit.clear() project = GlobalData().project if project.isLoaded(): self.filterEdit.editTextChanged.disconnect( self.__filterChanged ) self.filterEdit.addItems( project.findFuncHistory ) self.filterEdit.editTextChanged.connect( self.__filterChanged ) self.findNotUsedButton.setEnabled( self.getItemCount() > 0 ) else: self.findNotUsedButton.setEnabled( False ) self.filterEdit.clearEditText() return def __handleShowContextMenu( self, coord ): """ Show the context menu """ index = self.funcViewer.indexAt( coord ) if not index.isValid(): return # This will update the __contextItem self.__selectionChanged( index ) if self.__contextItem is None: return self.__jumpMenuItem.setEnabled( self.definitionButton.isEnabled() ) self.__findMenuItem.setEnabled( self.findButton.isEnabled() ) self.__copyMenuItem.setEnabled( self.copyPathButton.isEnabled() ) canDisassemble = self.__contextItem.canGetDisassembler() self.__disasmMenuItem.setEnabled( canDisassemble ) self.__menu.popup( QCursor.pos() ) return def __goToDefinition( self ): " Jump to definition context menu handler " if self.__contextItem is not None: self.funcViewer.openItem( self.__contextItem ) return def __findWhereUsed( self ): """ Find where used context menu handler """ if self.__contextItem is not None: GlobalData().mainWindow.findWhereUsed( \ self.__contextItem.getPath(), self.__contextItem.sourceObj ) return def __findNotUsed( self ): " Runs the unused function analysis " GlobalData().mainWindow.onNotUsedFunctions() return def __updateButtons( self ): " Updates the toolbar buttons depending on what is selected " self.definitionButton.setEnabled( False ) self.findButton.setEnabled( False ) self.copyPathButton.setEnabled( False ) if self.__contextItem is None: return if self.__contextItem.itemType == DecoratorItemType: self.definitionButton.setEnabled( True ) self.copyPathButton.setEnabled( True ) return if self.__contextItem.itemType in [ FunctionItemType, ClassItemType, AttributeItemType, GlobalItemType ]: self.definitionButton.setEnabled( True ) self.findButton.setEnabled( True ) self.copyPathButton.setEnabled( True ) return def onFileUpdated( self, fileName, uuid ): " Triggered when the file is updated " self.funcViewer.onFileUpdated( fileName ) self.findNotUsedButton.setEnabled( GlobalData().project.isLoaded() and \ self.getItemCount() > 0 ) return def modelFilesChanged( self ): " Triggered when the source model has a file or files added or deleted " self.findNotUsedButton.setEnabled( GlobalData().project.isLoaded() and \ self.getItemCount() > 0 ) return def __onDisassemble( self ): " Disassembling has been requested " if self.__contextItem is not None: self.funcViewer.getDisassembled( self.__contextItem )
class FileOutlineViewer( QWidget ): """ The file outline viewer widget """ def __init__( self, editorsManager, parent = None ): QWidget.__init__( self, parent ) self.__editorsManager = editorsManager self.__mainWindow = parent self.__editorsManager.currentChanged.connect( self.__onTabChanged ) self.connect( self.__editorsManager, SIGNAL( "tabClosed" ), self.__onTabClosed ) self.connect( self.__editorsManager, SIGNAL( 'bufferSavedAs' ), self.__onSavedBufferAs ) self.connect( self.__editorsManager, SIGNAL( 'fileTypeChanged' ), self.__onFileTypeChanged ) self.__outlineBrowsers = {} # UUID -> OutlineAttributes self.__currentUUID = None self.__updateTimer = QTimer( self ) self.__updateTimer.setSingleShot( True ) self.__updateTimer.timeout.connect( self.__updateView ) self.findButton = None self.outlineViewer = None self.toolbar = None self.__createLayout() self.__modifiedFormat = Settings().modifiedFormat # create the context menu self.__menu = QMenu( self ) self.__findMenuItem = self.__menu.addAction( PixmapCache().getIcon( 'findusage.png' ), 'Find where used', self.__findWhereUsed ) return def setTooltips( self, switchOn ): " Sets the tooltips mode " for key in self.__outlineBrowsers: self.__outlineBrowsers[ key ].browser.setTooltips( switchOn ) return def __connectOutlineBrowser( self, browser ): " Connects a new buffer signals " browser.setContextMenuPolicy( Qt.CustomContextMenu ) browser.customContextMenuRequested.connect( self.__handleShowContextMenu ) self.connect( browser, SIGNAL( "selectionChanged" ), self.__selectionChanged ) return def __createLayout( self ): " Helper to create the viewer layout " # Toolbar part - buttons self.findButton = QAction( PixmapCache().getIcon( 'findusage.png' ), 'Find where highlighted item is used', self ) self.findButton.setVisible( False ) self.findButton.triggered.connect( self.__findWhereUsed ) self.showParsingErrorsButton = QAction( PixmapCache().getIcon( 'showparsingerrors.png' ), 'Show lexer/parser errors', self ) self.showParsingErrorsButton.triggered.connect( self.__showParserError ) self.showParsingErrorsButton.setEnabled( False ) self.toolbar = QToolBar( self ) self.toolbar.setMovable( False ) self.toolbar.setAllowedAreas( Qt.TopToolBarArea ) self.toolbar.setIconSize( QSize( 16, 16 ) ) self.toolbar.setFixedHeight( 28 ) self.toolbar.setContentsMargins( 0, 0, 0, 0 ) self.toolbar.addAction( self.findButton ) self.toolbar.addAction( self.showParsingErrorsButton ) # Prepare members for reuse self.__noneLabel = QLabel( "\nNot a python file" ) self.__noneLabel.setFrameShape( QFrame.StyledPanel ) self.__noneLabel.setAlignment( Qt.AlignHCenter ) headerFont = self.__noneLabel.font() headerFont.setPointSize( headerFont.pointSize() + 2 ) self.__noneLabel.setFont( headerFont ) self.__noneLabel.setAutoFillBackground( True ) noneLabelPalette = self.__noneLabel.palette() noneLabelPalette.setColor( QPalette.Background, GlobalData().skin.nolexerPaper ) self.__noneLabel.setPalette( noneLabelPalette ) self.__layout = QVBoxLayout() self.__layout.setContentsMargins( 0, 0, 0, 0 ) self.__layout.setSpacing( 0 ) self.__layout.addWidget( self.toolbar ) self.__layout.addWidget( self.__noneLabel ) self.setLayout( self.__layout ) return def __selectionChanged( self, index ): " Handles the changed selection " if index is None: self.__outlineBrowsers[ self.__currentUUID ].contentItem = None else: self.__outlineBrowsers[ self.__currentUUID ].contentItem = \ self.__outlineBrowsers[ self.__currentUUID ].browser.model().item( index ) self.__updateButtons() return def __handleShowContextMenu( self, coord ): """ Show the context menu """ browser = self.__outlineBrowsers[ self.__currentUUID ].browser index = browser.indexAt( coord ) if not index.isValid(): return # This will update the contextItem self.__selectionChanged( index ) contextItem = self.__outlineBrowsers[ self.__currentUUID ].contentItem if contextItem is None: return self.__findMenuItem.setEnabled( self.findButton.isEnabled() ) self.__menu.popup( QCursor.pos() ) return def __goToDefinition( self ): " Jump to definition context menu handler " contextItem = self.__outlineBrowsers[ self.__currentUUID ].contentItem if contextItem is not None: self.__outlineBrowsers[ self.__currentUUID ].browser.openItem( contextItem ) return def __findWhereUsed( self ): """ Find where used context menu handler """ contextItem = self.__outlineBrowsers[ self.__currentUUID ].contentItem if contextItem is not None: GlobalData().mainWindow.findWhereUsed( contextItem.getPath(), contextItem.sourceObj ) return def __updateButtons( self ): " Updates the toolbar buttons depending on what is selected " self.findButton.setEnabled( False ) contextItem = self.__outlineBrowsers[ self.__currentUUID ].contentItem if contextItem is None: return if contextItem.itemType in [ FunctionItemType, ClassItemType, AttributeItemType, GlobalItemType ]: self.findButton.setEnabled( True ) return def __onTabChanged( self, index ): " Triggered when another tab becomes active " # If the timer is still active that means the tab was switched before # the handler had a chance to work. Therefore update the previous tab # first if so. if self.__updateTimer.isActive(): self.__updateTimer.stop() self.__updateView() # Now, switch the outline browser to the new tab if index == -1: widget = self.__editorsManager.currentWidget() else: widget = self.__editorsManager.getWidgetByIndex( index ) if widget is None: if self.__currentUUID is not None: self.__outlineBrowsers[ self.__currentUUID ].browser.hide() self.__currentUUID = None self.__noneLabel.show() self.showParsingErrorsButton.setEnabled( False ) return if widget.getType() not in [ MainWindowTabWidgetBase.PlainTextEditor, MainWindowTabWidgetBase.VCSAnnotateViewer ]: if self.__currentUUID is not None: self.__outlineBrowsers[ self.__currentUUID ].browser.hide() self.__currentUUID = None self.__noneLabel.show() self.showParsingErrorsButton.setEnabled( False ) return # This is text editor, detect the file type if widget.getFileType() not in [ PythonFileType, Python3FileType ]: if self.__currentUUID is not None: self.__outlineBrowsers[ self.__currentUUID ].browser.hide() self.__currentUUID = None self.__noneLabel.show() self.showParsingErrorsButton.setEnabled( False ) return # This is a python file, check if we already have the parsed info in # the cache uuid = widget.getUUID() if uuid in self.__outlineBrowsers: # We have it, hide the current and show the existed if self.__currentUUID is not None: self.__outlineBrowsers[ self.__currentUUID ].browser.hide() self.__currentUUID = None else: self.__noneLabel.hide() self.__currentUUID = uuid self.__outlineBrowsers[ self.__currentUUID ].browser.show() info = self.__outlineBrowsers[ self.__currentUUID ].info self.showParsingErrorsButton.setEnabled( info.isOK != True ) return # It is first time we are here, create a new editor = widget.getEditor() editor.SCEN_CHANGE.connect( self.__onBufferChanged ) editor.cursorPositionChanged.connect( self.__cursorPositionChanged ) info = getBriefModuleInfoFromMemory( editor.text() ) self.showParsingErrorsButton.setEnabled( info.isOK != True ) shortFileName = widget.getShortName() browser = OutlineBrowser( uuid, shortFileName, info, self ) browser.setHeaderHighlight( info.isOK != True ) self.__connectOutlineBrowser( browser ) self.__layout.addWidget( browser ) if self.__currentUUID is not None: self.__outlineBrowsers[ self.__currentUUID ].browser.hide() self.__currentUUID = None else: self.__noneLabel.hide() self.__currentUUID = uuid attributes = OutlineAttributes() attributes.browser = browser attributes.contextItem = None attributes.info = info attributes.shortFileName = shortFileName attributes.changed = False self.__outlineBrowsers[ self.__currentUUID ] = attributes self.__outlineBrowsers[ self.__currentUUID ].browser.show() return def getCurrentUsedInfo( self ): " Provides the info used to show the current outline window " if self.__currentUUID in self.__outlineBrowsers: return self.__outlineBrowsers[ self.__currentUUID ].info return None def __cursorPositionChanged( self, xpos, ypos ): " Triggered when a cursor position is changed " if self.__updateTimer.isActive(): # If a file is very large and the cursor is moved # straight after changes this will delay the update till # the real pause. self.__updateTimer.stop() self.__updateTimer.start( 1500 ) return def __onBufferChanged( self ): " Triggered when a change in the buffer is identified " if self.__currentUUID is None: return widget = self.__editorsManager.getWidgetByUUID( self.__currentUUID ) if widget is None: return if widget.getEditor().ignoreBufferChangedSignal: return if self.__mainWindow.debugMode: return self.__updateTimer.stop() if self.__currentUUID in self.__outlineBrowsers: if self.__outlineBrowsers[ self.__currentUUID ].changed == False: self.__outlineBrowsers[ self.__currentUUID ].changed = True browser = self.__outlineBrowsers[ self.__currentUUID ].browser fName = self.__outlineBrowsers[ self.__currentUUID ].shortFileName title = self.__modifiedFormat % fName browser.model().sourceModel().updateRootData( 0, title ) self.__updateTimer.start( 1500 ) return def __updateView( self ): " Updates the view when a file is changed " self.__updateTimer.stop() info = self.getCurrentBufferInfo() if info is None: return self.showParsingErrorsButton.setEnabled( info.isOK != True ) browser = self.__outlineBrowsers[ self.__currentUUID ].browser fName = self.__outlineBrowsers[ self.__currentUUID ].shortFileName browser.setHeaderHighlight( info.isOK != True ) if not info.isOK: title = self.__modifiedFormat % fName browser.model().sourceModel().updateRootData( 0, title ) return browser.model().sourceModel().updateRootData( 0, fName ) self.__outlineBrowsers[ self.__currentUUID ].changed = False browser.updateFileItem( browser.model().sourceModel().rootItem, info ) self.__outlineBrowsers[ self.__currentUUID ].info = info return def getCurrentBufferInfo( self ): " Provides the current buffer parsed info " if self.__currentUUID is None: return None widget = self.__editorsManager.getWidgetByUUID( self.__currentUUID ) if widget is None: return None editor = widget.getEditor() info = getBriefModuleInfoFromMemory( editor.text() ) return info def __onTabClosed( self, uuid ): " Triggered when a tab is closed " if uuid in self.__outlineBrowsers: del self.__outlineBrowsers[ uuid ] return def __onSavedBufferAs( self, fileName, uuid ): " Triggered when a file is saved with a new name " if uuid in self.__outlineBrowsers: baseName = os.path.basename( fileName ) if detectFileType( fileName ) not in [ PythonFileType, Python3FileType ]: # It's not a python file anymore if uuid == self.__currentUUID: self.__outlineBrowsers[ uuid ].browser.hide() self.__noneLabel.show() self.__currentUUID = None del self.__outlineBrowsers[ uuid ] self.showParsingErrorsButton.setEnabled( False ) self.findButton.setEnabled( False ) return # Still python file with a different name browser = self.__outlineBrowsers[ uuid ].browser self.__outlineBrowsers[ uuid ].shortFileName = baseName if self.__outlineBrowsers[ uuid ].changed: title = self.__modifiedFormat % baseName else: title = baseName browser.model().sourceModel().updateRootData( 0, title ) return def __onFileTypeChanged( self, fileName, uuid, newFileType ): " Triggered when the current buffer file type is changed, e.g. .cgi " if newFileType in [ PythonFileType, Python3FileType ]: # The file became a python one if uuid not in self.__outlineBrowsers: self.__onTabChanged( -1 ) else: if uuid in self.__outlineBrowsers: # It's not a python file any more if uuid == self.__currentUUID: self.__outlineBrowsers[ uuid ].browser.hide() self.__noneLabel.show() self.__currentUUID = None del self.__outlineBrowsers[ uuid ] self.showParsingErrorsButton.setEnabled( False ) self.findButton.setEnabled( False ) return def __showParserError( self ): " Shows the parser errors window " if self.__currentUUID is None: return try: fName = self.__outlineBrowsers[ self.__currentUUID ].shortFileName widget = self.__editorsManager.getWidgetByUUID( self.__currentUUID ) if widget is None: return editor = widget.getEditor() info = getBriefModuleInfoFromMemory( editor.text() ) dialog = ParserErrorsDialog( fName, info ) dialog.exec_() except Exception, ex: logging.error( str( ex ) ) return