Example #1
0
class ThreadsViewer(QWidget):
    """Implements the threads viewer for a debugger"""
    def __init__(self, debugger, parent=None):
        QWidget.__init__(self, parent)

        self.__debugger = debugger
        self.__createLayout()

        if not Settings()['showThreadViewer']:
            self.__onShowHide(True)

    def __createLayout(self):
        """Creates the widget layout"""
        verticalLayout = QVBoxLayout(self)
        verticalLayout.setContentsMargins(0, 0, 0, 0)
        verticalLayout.setSpacing(0)

        self.headerFrame = QFrame()
        self.headerFrame.setObjectName('threadheader')
        self.headerFrame.setStyleSheet('QFrame#threadheader {' +
                                       getLabelStyle(self) + '}')
        self.headerFrame.setFixedHeight(HEADER_HEIGHT)

        self.__threadsLabel = QLabel("Threads")

        expandingSpacer = QSpacerItem(10, 10, QSizePolicy.Expanding)

        self.__showHideButton = QToolButton()
        self.__showHideButton.setAutoRaise(True)
        self.__showHideButton.setIcon(getIcon('less.png'))
        self.__showHideButton.setFixedSize(HEADER_BUTTON, HEADER_BUTTON)
        self.__showHideButton.setToolTip("Hide threads list")
        self.__showHideButton.setFocusPolicy(Qt.NoFocus)
        self.__showHideButton.clicked.connect(self.__onShowHide)

        headerLayout = QHBoxLayout()
        headerLayout.setContentsMargins(0, 0, 0, 0)
        headerLayout.addSpacing(3)
        headerLayout.addWidget(self.__threadsLabel)
        headerLayout.addSpacerItem(expandingSpacer)
        headerLayout.addWidget(self.__showHideButton)
        self.headerFrame.setLayout(headerLayout)

        self.__threadsList = QTreeWidget()
        self.__threadsList.setSortingEnabled(False)
        # I might not need that because of two reasons:
        # - the window has no focus
        # - the window has custom current indicator
        # self.__threadsList.setAlternatingRowColors( True )
        self.__threadsList.setRootIsDecorated(False)
        self.__threadsList.setItemsExpandable(False)
        self.__threadsList.setUniformRowHeights(True)
        self.__threadsList.setSelectionMode(QAbstractItemView.NoSelection)
        self.__threadsList.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.__threadsList.setItemDelegate(NoOutlineHeightDelegate(4))
        self.__threadsList.setFocusPolicy(Qt.NoFocus)

        self.__threadsList.itemClicked.connect(self.__onThreadClicked)
        self.__threadsList.setHeaderLabels(["", "Name", "State", "TID"])

        verticalLayout.addWidget(self.headerFrame)
        verticalLayout.addWidget(self.__threadsList)

    def __onShowHide(self, startup=False):
        """Triggered when show/hide button is clicked"""
        if startup or self.__threadsList.isVisible():
            self.__threadsList.setVisible(False)
            self.__showHideButton.setIcon(getIcon('more.png'))
            self.__showHideButton.setToolTip("Show threads list")

            self.__minH = self.minimumHeight()
            self.__maxH = self.maximumHeight()

            self.setMinimumHeight(self.headerFrame.height())
            self.setMaximumHeight(self.headerFrame.height())

            Settings()['showThreadViewer'] = False
        else:
            self.__threadsList.setVisible(True)
            self.__showHideButton.setIcon(getIcon('less.png'))
            self.__showHideButton.setToolTip("Hide threads list")

            self.setMinimumHeight(self.__minH)
            self.setMaximumHeight(self.__maxH)

            Settings()['showThreadViewer'] = True

    def __resizeColumns(self):
        """Resize the files list columns"""
        self.__threadsList.header().setStretchLastSection(True)
        self.__threadsList.header().resizeSections(
            QHeaderView.ResizeToContents)
        self.__threadsList.header().resizeSection(0, 22)
        self.__threadsList.header().setSectionResizeMode(0, QHeaderView.Fixed)

    def clear(self):
        """Clears the content"""
        self.__threadsList.clear()
        self.__threadsLabel.setText("Threads")

    def populate(self, currentThreadID, threadList):
        """Populates the thread list from the client"""
        self.clear()
        for thread in threadList:
            if thread['broken']:
                state = "Waiting at breakpoint"
            else:
                state = "Running"
            item = ThreadItem(thread['id'], thread['name'], state)
            if thread['id'] == currentThreadID:
                item.setCurrent(True)
            self.__threadsList.addTopLevelItem(item)

        self.__resizeColumns()
        self.__threadsLabel.setText("Threads (total: " + str(len(threadList)) +
                                    ")")

    def switchControl(self, isInIDE):
        """Switches the UI depending where the control flow is"""
        self.__threadsList.setEnabled(isInIDE)

    # Arguments: item, column
    def __onThreadClicked(self, item, _):
        """Triggered when a thread is clicked"""
        if item.isCurrent():
            return

        for index in range(self.__threadsList.topLevelItemCount()):
            listItem = self.__threadsList.topLevelItem(index)
            if listItem.isCurrent():
                listItem.setCurrent(False)
                break
        item.setCurrent(True)

        self.__debugger.remoteSetThread(item.getTID())
class VariablesViewer(QWidget):
    """Implements the variables viewer for a debugger"""

    # First group of filters
    FilterGlobalAndLocal = 0
    FilterGlobalOnly = 1
    FilterLocalOnly = 2

    def __init__(self, debugger, parent=None):
        QWidget.__init__(self, parent)

        self.__debugger = debugger
        self.__browser = VariablesBrowser(debugger, self)
        self.__createLayout()

        self.setTabOrder(self.__browser, self.__execStatement)
        self.setTabOrder(self.__execStatement, self.__execButton)

        self.__updateFilter()

    def __createLayout(self):
        """Creates the widget layout"""
        verticalLayout = QVBoxLayout(self)
        verticalLayout.setContentsMargins(0, 0, 0, 0)
        verticalLayout.setSpacing(0)

        self.__headerLabel = HeaderFitLabel(self)
        self.__headerLabel.setText('Variables')
        self.__headerLabel.setSizePolicy(QSizePolicy.Expanding,
                                         QSizePolicy.Fixed)
        self.__headerLabel.setMinimumWidth(10)

        self.__filterMenu = QMenu(self)
        self.__showAllAct = self.__filterMenu.addAction('Show all variables')
        self.__showAllAct.setData('showall')
        self.__filterMenu.addSeparator()
        self.__filters = []
        for title, settingName, _ in VARIABLE_FILTERS:
            action = self.__filterMenu.addAction(title)
            action.setCheckable(True)
            action.setData(settingName)
            self.__filters.append(action)
        self.__filterMenu.aboutToShow.connect(self.__filterMenuAboutToShow)
        self.__filterMenu.triggered.connect(self.__filterMenuTriggered)

        self.__filterButton = QToolButton(self)
        self.__filterButton.setIcon(getIcon('dbgvarflt.png'))
        self.__filterButton.setToolTip('Variable filter')
        self.__filterButton.setPopupMode(QToolButton.InstantPopup)
        self.__filterButton.setMenu(self.__filterMenu)
        self.__filterButton.setFocusPolicy(Qt.NoFocus)
        self.__filterButton.setFixedSize(self.__headerLabel.height(),
                                         self.__headerLabel.height())

        self.__execStatement = CDMComboBox(True)
        self.__execStatement.setSizePolicy(QSizePolicy.Expanding,
                                           QSizePolicy.Expanding)
        self.__execStatement.lineEdit().setToolTip("Execute statement")
        self.__execStatement.setFixedHeight(26)
        self.__execStatement.editTextChanged.connect(
            self.__execStatementChanged)
        self.__execStatement.enterClicked.connect(self.__onEnterInExec)
        self.__execButton = QPushButton("Exec")
        self.__execButton.setEnabled(False)
        self.__execButton.setFixedHeight(26)
        self.__execButton.clicked.connect(self.__onExec)

        self.headerToolbar = QToolBar(self)
        self.headerToolbar.setIconSize(QSize(18, 18))
        self.headerToolbar.setContentsMargins(1, 1, 1, 1)
        self.headerToolbar.addWidget(self.__headerLabel)
        self.headerToolbar.addWidget(self.__filterButton)

        execLayout = QGridLayout()
        execLayout.setContentsMargins(1, 1, 1, 1)
        execLayout.setSpacing(1)
        execLayout.addWidget(self.__execStatement, 0, 0)
        execLayout.addWidget(self.__execButton, 0, 1)

        verticalLayout.addWidget(self.headerToolbar)
        verticalLayout.addWidget(self.__browser)
        verticalLayout.addLayout(execLayout)

    def __filterMenuAboutToShow(self):
        """Debug variable filter menu is about to show"""
        for flt in self.__filters:
            flt.setChecked(Settings()[flt.data()])

    def __filterMenuTriggered(self, act):
        """A filter has been changed"""
        name = act.data()
        if name == 'showall':
            for _, settingName, _ in VARIABLE_FILTERS:
                Settings()[settingName] = True
        else:
            Settings()[name] = not Settings()[name]
        self.__updateFilter()

    def updateVariables(self, areGlobals, frameNumber, variables):
        """Triggered when a new set of variables is received"""
        self.__browser.showVariables(areGlobals, variables, frameNumber)
        self.__updateHeaderLabel()

    def updateVariable(self, areGlobals, variables):
        """Triggered when a new variable has been received"""
        self.__browser.showVariable(areGlobals, variables)
        self.__updateHeaderLabel()

    def __updateHeaderLabel(self):
        """Updates the header text"""
        shown, total = self.__browser.getShownAndTotalCounts()
        if shown == 0 and total == 0:
            self.__headerLabel.setText("Variables")
        else:
            self.__headerLabel.setText("Variables (" + str(shown) + " of " +
                                       str(total) + ")")

    def __updateFilter(self):
        """Updates the current filter"""
        self.__browser.filterChanged()
        self.__updateHeaderLabel()

    def clear(self):
        """Clears the content"""
        self.__browser.clear()
        self.__updateHeaderLabel()

    def clearAll(self):
        """Clears everything including the history"""
        self.clear()
        self.__execStatement.lineEdit().setText("")
        self.__execStatement.clear()

    def __execStatementChanged(self, text):
        """Triggered when a exec statement is changed"""
        text = str(text).strip()
        self.__execButton.setEnabled(text != "")

    def __onEnterInExec(self):
        """Enter/return clicked in exec"""
        self.__onExec()

    def __onExec(self):
        """Triggered when the Exec button is clicked"""
        text = self.__execStatement.currentText().strip()
        if text != "":
            currentFrame = GlobalData().mainWindow.getCurrentFrameNumber()
            self.__debugger.remoteExecuteStatement(text, currentFrame)
            self.__debugger.remoteClientVariables(1, currentFrame)  # globals
            self.__debugger.remoteClientVariables(0, currentFrame)  # locals

    def switchControl(self, isInIDE):
        """Switches the UI depending where the control flow is"""
        self.__browser.setEnabled(isInIDE)
        self.__filterButton.setEnabled(isInIDE)

        self.__execStatement.setEnabled(isInIDE)
        if isInIDE:
            text = self.__execStatement.currentText().strip()
            self.__execButton.setEnabled(text != "")
        else:
            self.__execButton.setEnabled(False)
Example #3
0
class StackViewer(QWidget):
    """Implements the stack viewer for a debugger"""
    def __init__(self, debugger, parent=None):
        QWidget.__init__(self, parent)

        self.__debugger = debugger
        self.currentStack = None
        self.currentFrame = 0
        self.__contextItem = None
        self.__createPopupMenu()
        self.__createLayout()

    def __createPopupMenu(self):
        """Creates the popup menu"""
        self.__framesMenu = QMenu()
        self.__setCurrentMenuItem = self.__framesMenu.addAction(
            "Set current (single click)", self.__onSetCurrent)
        self.__jumpMenuItem = self.__framesMenu.addAction(
            "Set current and jump to the source (double click)",
            self.__onSetCurrentAndJump)

    def __createLayout(self):
        """Creates the widget layout"""
        verticalLayout = QVBoxLayout(self)
        verticalLayout.setContentsMargins(0, 0, 0, 0)
        verticalLayout.setSpacing(0)

        self.__stackLabel = HeaderFitLabel(self)
        self.__stackLabel.setText('Stack')
        self.__stackLabel.setSizePolicy(QSizePolicy.Expanding,
                                        QSizePolicy.Fixed)
        self.__stackLabel.setMinimumWidth(10)

        self.__showHideButton = QToolButton()
        self.__showHideButton.setAutoRaise(True)
        self.__showHideButton.setIcon(getIcon('less.png'))
        self.__showHideButton.setFixedSize(self.__stackLabel.height(),
                                           self.__stackLabel.height())
        self.__showHideButton.setToolTip('Hide frames list')
        self.__showHideButton.setFocusPolicy(Qt.NoFocus)
        self.__showHideButton.clicked.connect(self.__onShowHide)

        self.headerToolbar = QToolBar(self)
        self.headerToolbar.setIconSize(QSize(16, 16))
        self.headerToolbar.setContentsMargins(1, 1, 1, 1)
        self.headerToolbar.addWidget(self.__stackLabel)
        self.headerToolbar.addWidget(self.__showHideButton)

        self.__framesList = QTreeWidget(self)
        self.__framesList.setSortingEnabled(False)
        # I might not need that because of two reasons:
        # - the window has no focus
        # - the window has custom current indicator
        # self.__framesList.setAlternatingRowColors(True)
        self.__framesList.setRootIsDecorated(False)
        self.__framesList.setItemsExpandable(False)
        self.__framesList.setUniformRowHeights(True)
        self.__framesList.setSelectionMode(QAbstractItemView.NoSelection)
        self.__framesList.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.__framesList.setItemDelegate(NoOutlineHeightDelegate(4))
        self.__framesList.setFocusPolicy(Qt.NoFocus)
        self.__framesList.setContextMenuPolicy(Qt.CustomContextMenu)

        self.__framesList.itemClicked.connect(self.__onFrameClicked)
        self.__framesList.itemDoubleClicked.connect(
            self.__onFrameDoubleClicked)
        self.__framesList.customContextMenuRequested.connect(
            self.__showContextMenu)

        self.__framesList.setHeaderLabels(
            ['', 'File:line', 'Function', 'Arguments', 'Full path'])

        verticalLayout.addWidget(self.headerToolbar)
        verticalLayout.addWidget(self.__framesList)

    def __onShowHide(self, startup=False):
        """Triggered when show/hide button is clicked"""
        if startup or self.__framesList.isVisible():
            self.__minH = self.minimumHeight()
            self.__maxH = self.maximumHeight()
            self.splitterSize = self.parent().sizes()[1]

            self.__framesList.setVisible(False)
            self.__showHideButton.setIcon(getIcon('more.png'))
            self.__showHideButton.setToolTip('Show frames list')

            self.setMinimumHeight(self.headerToolbar.height())
            self.setMaximumHeight(self.headerToolbar.height())
        else:
            self.__framesList.setVisible(True)
            self.__showHideButton.setIcon(getIcon('less.png'))
            self.__showHideButton.setToolTip('Hide frames list')

            self.setMinimumHeight(self.__minH)
            self.setMaximumHeight(self.__maxH)
            splitterSizes = self.parent().sizes()
            splitterSizes[1] = self.splitterSize
            self.parent().setSizes(splitterSizes)

    def clear(self):
        """Clears the content"""
        self.__framesList.clear()
        self.currentStack = None
        self.__stackLabel.setText("Stack")

    def __resizeColumns(self):
        """Resize the files list columns"""
        self.__framesList.header().setStretchLastSection(True)
        self.__framesList.header().resizeSections(QHeaderView.ResizeToContents)
        self.__framesList.header().resizeSection(0, 22)
        self.__framesList.header().setSectionResizeMode(0, QHeaderView.Fixed)

    def populate(self, stack):
        """Sets the new call stack and selects the first item in it"""
        self.clear()

        self.currentStack = stack
        self.currentFrame = 0
        frameNumber = 0
        for item in stack:
            fName = item[0]
            lineNo = item[1]
            funcName = ''
            funcArgs = ''
            if len(item) >= 3:
                funcName = item[2]
            if len(item) >= 4:
                funcArgs = item[3]

            if funcName.startswith('<'):
                funcName = ''
                funcArgs = ''

            item = StackFrameItem(fName, lineNo, funcName, funcArgs,
                                  frameNumber)
            self.__framesList.addTopLevelItem(item)
            frameNumber += 1
        self.__resizeColumns()
        self.__framesList.topLevelItem(0).setCurrent(True)
        self.__stackLabel.setText("Stack (total: " + str(len(stack)) + ")")

    def getFrameNumber(self):
        """Provides the current frame number"""
        return self.currentFrame

    def __onFrameClicked(self, item, column):
        """Triggered when a frame is clicked"""
        del column  # unused argument
        if item.isCurrent():
            return

        # Hide the current indicator
        self.__framesList.topLevelItem(self.currentFrame).setCurrent(False)

        # Show the new indicator
        self.currentFrame = item.getFrameNumber()
        for index in range(self.__framesList.topLevelItemCount()):
            item = self.__framesList.topLevelItem(index)
            if item.getFrameNumber() == self.currentFrame:
                item.setCurrent(True)
        self.__debugger.remoteClientVariables(1, self.currentFrame)  # globals
        self.__debugger.remoteClientVariables(0, self.currentFrame)  # locals

    def __onFrameDoubleClicked(self, item, column):
        """Triggered when a frame is double clicked"""
        del column  # unused argument
        # The frame has been switched already because the double click
        # signal always comes after the single click one
        fileName = item.getFilename()
        lineNumber = item.getLineNumber()

        editorsManager = GlobalData().mainWindow.editorsManager()
        editorsManager.openFile(fileName, lineNumber)
        editor = editorsManager.currentWidget().getEditor()
        editor.gotoLine(lineNumber)
        editorsManager.currentWidget().setFocus()

    def __showContextMenu(self, coord):
        """Shows the frames list context menu"""
        self.__contextItem = self.__framesList.itemAt(coord)
        if self.__contextItem is not None:
            self.__setCurrentMenuItem.setEnabled(
                not self.__contextItem.isCurrent())
            self.__framesMenu.popup(QCursor.pos())

    def __onSetCurrent(self):
        """Context menu item handler"""
        self.__onFrameClicked(self.__contextItem, 0)

    def __onSetCurrentAndJump(self):
        """Context menu item handler"""
        self.__onFrameClicked(self.__contextItem, 0)
        self.__onFrameDoubleClicked(self.__contextItem, 0)

    def switchControl(self, isInIDE):
        """Switches the UI depending where the control flow is"""
        self.__framesList.setEnabled(isInIDE)
Example #4
0
class SVNPluginCommitDialog(QDialog):
    """SVN Plugin commit dialog"""

    NODIFF = '<html><body bgcolor="#ffffe6"></body></html>'

    def __init__(self, plugin, pathsToCommit, pathsToIgnore, parent=None):
        QDialog.__init__(self, parent)

        self.__plugin = plugin

        self.__createLayout(pathsToCommit, pathsToIgnore)
        self.setWindowTitle("SVN commit")

        # Fill the lists
        for item in pathsToCommit:
            newItem = QTreeWidgetItem(["", item[0], STATUS[item[1]]])
            newItem.setCheckState(CHECK_COL, Qt.Checked)
            newItem.setToolTip(PATH_COL, item[0])
            newItem.setToolTip(STATUS_COL, STATUS[item[1]])
            self.__pathToCommitView.addTopLevelItem(newItem)

            diffButton = self.__createDiffButton()
            diffButton.path = item[0]
            diffButton.status = item[1]

            if os.path.isdir(item[0]) or item[1] in [IND_REPLACED] \
                or not isFileSearchable(item[0]):
                diffButton.setEnabled(False)
                diffButton.setToolTip("Diff is not available")
            else:
                diffButton.setEnabled(True)
                diffButton.setToolTip("Click to see diff")
            self.__pathToCommitView.setItemWidget(newItem, DIFF_COL,
                                                  diffButton)

        self.__resizeCommitPaths()
        self.__sortCommitPaths()

        for item in pathsToIgnore:
            newItem = QTreeWidgetItem([item[0], STATUS[item[1]]])
            newItem.setToolTip(0, item[0])
            newItem.setToolTip(1, STATUS[item[1]])
            self.__pathToIgnoreView.addTopLevelItem(newItem)
        self.__pathToIgnoreView.header().resizeSections(
            QHeaderView.ResizeToContents)

        self.__updateSelectAllStatus()
        self.__updateOKStatus()
        self.__message.setFocus()

    def __resizeCommitPaths(self):
        """Resizes the plugins table"""
        self.__pathToCommitView.header().setStretchLastSection(False)
        self.__pathToCommitView.header().resizeSections(
            QHeaderView.ResizeToContents)
        self.__pathToCommitView.header().resizeSection(CHECK_COL, 28)
        self.__pathToCommitView.header().setResizeMode(CHECK_COL,
                                                       QHeaderView.Fixed)

        # By some reasons, to have PATH_COL visually adjustable the only STATUS_COL
        # must be set to be stretchable, so there is a comment below.
        # self.__pathToCommitView.header().setResizeMode( PATH_COL, QHeaderView.Stretch )
        self.__pathToCommitView.header().setResizeMode(STATUS_COL,
                                                       QHeaderView.Stretch)
        self.__pathToCommitView.header().resizeSection(DIFF_COL, 24)
        self.__pathToCommitView.header().setResizeMode(DIFF_COL,
                                                       QHeaderView.Fixed)

    def __sortCommitPaths(self):
        """Sorts the commit paths table"""
        self.__pathToCommitView.sortItems(
            self.__pathToCommitView.sortColumn(),
            self.__pathToCommitView.header().sortIndicatorOrder())

    def __createDiffButton(self):
        """Creates a diff button for a path"""
        button = DiffButton()
        self.connect(button, SIGNAL('CustomClick'), self.onDiff)
        return button

    @staticmethod
    def __setLightPalette(frame):
        """Creates a lighter paletter for the widget background"""
        palette = frame.palette()
        background = palette.color(QPalette.Background)
        background.setRgb(min(background.red() + 30, 255),
                          min(background.green() + 30, 255),
                          min(background.blue() + 30, 255))
        palette.setColor(QPalette.Background, background)
        frame.setPalette(palette)

    @staticmethod
    def __configTable(table):
        """Sets common properties for a table"""
        table.setAlternatingRowColors(True)
        table.setRootIsDecorated(False)
        table.setItemsExpandable(False)
        table.setSortingEnabled(True)
        table.setItemDelegate(NoOutlineHeightDelegate(4))
        table.setUniformRowHeights(True)

    def __createLayout(self, pathsToCommit, pathsToIgnore):
        """Creates the dialog layout"""
        self.resize(640, 480)
        self.setSizeGripEnabled(True)

        vboxLayout = QVBoxLayout(self)

        # Paths to commit part
        commitHeaderFrame = QFrame()
        commitHeaderFrame.setFrameStyle(QFrame.StyledPanel)
        commitHeaderFrame.setAutoFillBackground(True)
        self.__setLightPalette(commitHeaderFrame)
        commitHeaderFrame.setFixedHeight(24)

        expandingCommitSpacer = QSpacerItem(10, 10, QSizePolicy.Expanding)

        self.__selectAllButton = QToolButton()
        self.__selectAllButton.setAutoRaise(True)
        self.__selectAllButton.setIcon(
            getIcon(pluginHomeDir + 'svnselectall.png'))
        self.__selectAllButton.setFixedSize(20, 20)
        self.__selectAllButton.setToolTip("Select all")
        self.__selectAllButton.setFocusPolicy(Qt.NoFocus)
        self.__selectAllButton.clicked.connect(self.__onSelectAll)

        commitHeaderLayout = QHBoxLayout()
        commitHeaderLayout.setContentsMargins(3, 0, 0, 0)
        commitHeaderLayout.addWidget(
            QLabel("Paths to commit (total: " + str(len(pathsToCommit)) + ")"))
        commitHeaderLayout.addSpacerItem(expandingCommitSpacer)
        commitHeaderLayout.addWidget(self.__selectAllButton)
        commitHeaderFrame.setLayout(commitHeaderLayout)

        vboxLayout.addWidget(commitHeaderFrame)

        self.__pathToCommitView = QTreeWidget()
        self.__configTable(self.__pathToCommitView)

        self.__pathToCommitHeader = QTreeWidgetItem(["", "Path", "Status", ""])
        self.__pathToCommitView.setHeaderItem(self.__pathToCommitHeader)
        self.__pathToCommitView.header().setSortIndicator(
            PATH_COL, Qt.AscendingOrder)
        self.__pathToCommitView.itemChanged.connect(self.__onCommitPathChanged)
        vboxLayout.addWidget(self.__pathToCommitView)

        # Paths to ignore part
        headerFrame = QFrame()
        headerFrame.setFrameStyle(QFrame.StyledPanel)
        headerFrame.setAutoFillBackground(True)
        self.__setLightPalette(headerFrame)
        headerFrame.setFixedHeight(24)

        ignoreLabel = QLabel("Ignored paths (total: " +
                             str(len(pathsToIgnore)) + ")")
        expandingSpacer = QSpacerItem(10, 10, QSizePolicy.Expanding)

        self.__showHideIgnoredButton = QToolButton()
        self.__showHideIgnoredButton.setAutoRaise(True)
        self.__showHideIgnoredButton.setIcon(getIcon('less.png'))
        self.__showHideIgnoredButton.setFixedSize(20, 20)
        self.__showHideIgnoredButton.setToolTip("Show ignored path list")
        self.__showHideIgnoredButton.setFocusPolicy(Qt.NoFocus)
        self.__showHideIgnoredButton.clicked.connect(self.__onShowHideIgnored)

        ignoredHeaderLayout = QHBoxLayout()
        ignoredHeaderLayout.setContentsMargins(3, 0, 0, 0)
        ignoredHeaderLayout.addWidget(ignoreLabel)
        ignoredHeaderLayout.addSpacerItem(expandingSpacer)
        ignoredHeaderLayout.addWidget(self.__showHideIgnoredButton)
        headerFrame.setLayout(ignoredHeaderLayout)

        vboxLayout.addWidget(headerFrame)

        self.__pathToIgnoreView = QTreeWidget()
        self.__configTable(self.__pathToIgnoreView)
        self.__pathToIgnoreView.setVisible(False)

        pathToIgnoreHeader = QTreeWidgetItem(["Path", "Status"])
        self.__pathToIgnoreView.setHeaderItem(pathToIgnoreHeader)
        self.__pathToIgnoreView.header().setSortIndicator(0, Qt.AscendingOrder)
        vboxLayout.addWidget(self.__pathToIgnoreView)

        # Message part
        vboxLayout.addWidget(QLabel("Message"))
        self.__message = QTextEdit()
        self.__message.setAcceptRichText(False)
        metrics = QFontMetrics(self.__message.font())
        rect = metrics.boundingRect("X")
        self.__message.setFixedHeight(rect.height() * 4 + 5)
        vboxLayout.addWidget(self.__message)

        # Diff part
        diffHeaderFrame = QFrame()
        diffHeaderFrame.setFrameStyle(QFrame.StyledPanel)
        diffHeaderFrame.setAutoFillBackground(True)
        self.__setLightPalette(diffHeaderFrame)
        diffHeaderFrame.setFixedHeight(24)

        diffLabel = QLabel("Diff")
        diffExpandingSpacer = QSpacerItem(10, 10, QSizePolicy.Expanding)

        self.__showHideDiffButton = QToolButton()
        self.__showHideDiffButton.setAutoRaise(True)
        self.__showHideDiffButton.setIcon(getIcon('less.png'))
        self.__showHideDiffButton.setFixedSize(20, 20)
        self.__showHideDiffButton.setToolTip("Show diff")
        self.__showHideDiffButton.setFocusPolicy(Qt.NoFocus)
        self.__showHideDiffButton.clicked.connect(self.__onShowHideDiff)

        diffLayout = QHBoxLayout()
        diffLayout.setContentsMargins(3, 0, 0, 0)
        diffLayout.addWidget(diffLabel)
        diffLayout.addSpacerItem(diffExpandingSpacer)
        diffLayout.addWidget(self.__showHideDiffButton)
        diffHeaderFrame.setLayout(diffLayout)

        self.__diffViewer = DiffTabWidget()
        self.__diffViewer.setHTML(self.NODIFF)
        self.__diffViewer.setVisible(False)

        vboxLayout.addWidget(diffHeaderFrame)
        vboxLayout.addWidget(self.__diffViewer)

        # Buttons at the bottom
        buttonBox = QDialogButtonBox(self)
        buttonBox.setOrientation(Qt.Horizontal)
        buttonBox.setStandardButtons(QDialogButtonBox.Ok
                                     | QDialogButtonBox.Cancel)
        self.__OKButton = buttonBox.button(QDialogButtonBox.Ok)
        self.__OKButton.setText("Commit")
        buttonBox.button(QDialogButtonBox.Cancel).setDefault(True)
        buttonBox.accepted.connect(self.userAccept)
        buttonBox.rejected.connect(self.close)
        vboxLayout.addWidget(buttonBox)

    def __onShowHideDiff(self):
        if self.__diffViewer.isVisible():
            self.__diffViewer.setVisible(False)
            self.__showHideDiffButton.setIcon(getIcon('less.png'))
            self.__showHideDiffButton.setToolTip("Show diff")
        else:
            self.__diffViewer.setVisible(True)
            self.__showHideDiffButton.setIcon(getIcon('more.png'))
            self.__showHideDiffButton.setToolTip("Hide diff")

    def __onShowHideIgnored(self):
        if self.__pathToIgnoreView.isVisible():
            self.__pathToIgnoreView.setVisible(False)
            self.__showHideIgnoredButton.setIcon(getIcon('less.png'))
            self.__showHideIgnoredButton.setToolTip("Show ignored path list")
        else:
            self.__pathToIgnoreView.setVisible(True)
            self.__showHideIgnoredButton.setIcon(getIcon('more.png'))
            self.__showHideIgnoredButton.setToolTip("Hide ignored path list")

    def userAccept(self):
        """ Triggered when the user clicks OK"""
        # Collect the list of checked paths
        self.commitMessage = self.__message.toPlainText().strip()

        self.commitPaths = []
        index = 0
        while index < self.__pathToCommitView.topLevelItemCount():
            item = self.__pathToCommitView.topLevelItem(index)
            if item.checkState(0) == Qt.Checked:
                path = str(item.text(1))
                if os.path.isdir(path) and not os.path.islink(path) and \
                    not path.endswith(os.path.sep):
                    path += os.path.sep
                self.commitPaths.append(path)
            index += 1
        self.accept()

    def __getCheckedCount(self):
        """Provides the number of selected items in the commit paths section"""
        index = 0
        checkedCount = 0
        while index < self.__pathToCommitView.topLevelItemCount():
            item = self.__pathToCommitView.topLevelItem(index)
            if item.checkState(0) == Qt.Checked:
                checkedCount += 1
            index += 1
        return checkedCount

    def __updateSelectAllStatus(self):
        """Updates the select all status button"""
        total = self.__pathToCommitView.topLevelItemCount()
        if total == 0 or total == self.__getCheckedCount():
            self.__selectAllButton.setEnabled(False)
        else:
            self.__selectAllButton.setEnabled(True)

    def __updateOKStatus(self):
        """Updates the OK button status"""
        self.__OKButton.setEnabled(self.__getCheckedCount() > 0)

    def __onCommitPathChanged(self, item, column):
        """Triggered when an item is changed"""
        self.__updateSelectAllStatus()
        self.__updateOKStatus()

    def __onSelectAll(self):
        """Triggered when select all button is clicked"""
        index = 0
        while index < self.__pathToCommitView.topLevelItemCount():
            item = self.__pathToCommitView.topLevelItem(index)
            item.setCheckState(0, Qt.Checked)
            index += 1
        self.__updateSelectAllStatus()
        self.__updateOKStatus()

    def onDiff(self, path, status):
        """Triggered when diff for the path is called"""
        if not path:
            return

        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))

        try:
            # Status is one of the following:
            # IND_ADDED, IND_DELETED, IND_MERGED, IND_MODIFIED_LR, IND_MODIFIED_L, IND_CONFLICTED
            repositoryContent = ""
            localContent = ""
            if status != IND_ADDED:
                client = self.__plugin.getSVNClient(
                    self.__plugin.getSettings())
                repositoryContent = client.cat(path)
            if status != IND_DELETED:
                with open(path) as f:
                    localContent = f.read()

            diff = difflib.unified_diff(repositoryContent.splitlines(),
                                        localContent.splitlines())
            nodiffMessage = path + " has no difference to the " \
                                   "repository at revision HEAD"
            if diff is None:
                QApplication.restoreOverrideCursor()
                logging.info(nodiffMessage)
                return

            # There are changes, so replace the text and tell about the changes
            diffAsText = '\n'.join(list(diff))
            if diffAsText.strip() == '':
                QApplication.restoreOverrideCursor()
                logging.info(nodiffMessage)
                return

            source = "+++ local " + os.path.basename(path)
            diffAsText = diffAsText.replace("+++ ", source, 1)
            diffAsText = diffAsText.replace("--- ",
                                            "--- repository at revision HEAD",
                                            1)

            self.__diffViewer.setHTML(
                parse_from_memory(diffAsText, False, True))
            if not self.__diffViewer.isVisible():
                self.__onShowHideDiff()
        except Exception as exc:
            logging.error(str(exc))
        except:
            logging.error("Unknown error while calculating difference for " +
                          path)

        QApplication.restoreOverrideCursor()
Example #5
0
class IgnoredExceptionsViewer(QWidget):
    """Implements the client exceptions viewer for a debugger"""
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)

        self.__createPopupMenu()
        self.__createLayout()
        self.__ignored = []
        self.__currentItem = None

        GlobalData().project.sigProjectChanged.connect(self.__onProjectChanged)

        if not Settings()['showIgnoredExcViewer']:
            self.__onShowHide(True)

    def __createPopupMenu(self):
        """Creates the popup menu"""
        self.__excptMenu = QMenu()
        self.__removeMenuItem = self.__excptMenu.addAction(
            getIcon('ignexcptdel.png'), "Remove from ignore list",
            self.__onRemoveFromIgnore)

    def __createLayout(self):
        """Creates the widget layout"""
        verticalLayout = QVBoxLayout(self)
        verticalLayout.setContentsMargins(0, 0, 0, 0)
        verticalLayout.setSpacing(0)

        self.__excptLabel = QLabel("Ignored exception types", self)

        self.headerFrame = QFrame()
        self.headerFrame.setObjectName('ignexcpt')
        self.headerFrame.setStyleSheet('QFrame#ignexcpt {' +
                                       getLabelStyle(self.__excptLabel) + '}')
        self.headerFrame.setFixedHeight(HEADER_HEIGHT)

        expandingSpacer = QSpacerItem(10, 10, QSizePolicy.Expanding)

        self.__showHideButton = QToolButton()
        self.__showHideButton.setAutoRaise(True)
        self.__showHideButton.setIcon(getIcon('less.png'))
        self.__showHideButton.setFixedSize(HEADER_BUTTON, HEADER_BUTTON)
        self.__showHideButton.setToolTip("Hide ignored exceptions list")
        self.__showHideButton.setFocusPolicy(Qt.NoFocus)
        self.__showHideButton.clicked.connect(self.__onShowHide)

        headerLayout = QHBoxLayout()
        headerLayout.setContentsMargins(0, 0, 0, 0)
        headerLayout.addSpacing(3)
        headerLayout.addWidget(self.__excptLabel)
        headerLayout.addSpacerItem(expandingSpacer)
        headerLayout.addWidget(self.__showHideButton)
        self.headerFrame.setLayout(headerLayout)

        self.exceptionsList = QTreeWidget(self)
        self.exceptionsList.setSortingEnabled(False)
        self.exceptionsList.setAlternatingRowColors(True)
        self.exceptionsList.setRootIsDecorated(False)
        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.exceptionsList.customContextMenuRequested.connect(
            self.__showContextMenu)
        self.exceptionsList.itemSelectionChanged.connect(
            self.__onSelectionChanged)
        self.exceptionsList.setHeaderLabels(["Exception type"])

        self.__excTypeEdit = QLineEdit()
        self.__excTypeEdit.setFixedHeight(26)
        self.__excTypeEdit.textChanged.connect(self.__onNewFilterChanged)
        self.__excTypeEdit.returnPressed.connect(self.__onAddExceptionFilter)
        self.__addButton = QPushButton("Add")
        # self.__addButton.setFocusPolicy(Qt.NoFocus)
        self.__addButton.setEnabled(False)
        self.__addButton.clicked.connect(self.__onAddExceptionFilter)

        expandingSpacer2 = QWidget()
        expandingSpacer2.setSizePolicy(QSizePolicy.Expanding,
                                       QSizePolicy.Expanding)

        self.__removeButton = QAction(getIcon('delitem.png'),
                                      "Remove selected exception type", self)
        self.__removeButton.triggered.connect(self.__onRemoveFromIgnore)
        self.__removeButton.setEnabled(False)

        fixedSpacer1 = QWidget()
        fixedSpacer1.setFixedWidth(5)

        self.__removeAllButton = QAction(getIcon('ignexcptdelall.png'),
                                         "Remove all the exception types",
                                         self)
        self.__removeAllButton.triggered.connect(self.__onRemoveAllFromIgnore)
        self.__removeAllButton.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.addWidget(expandingSpacer2)
        self.toolbar.addAction(self.__removeButton)
        self.toolbar.addWidget(fixedSpacer1)
        self.toolbar.addAction(self.__removeAllButton)

        addLayout = QHBoxLayout()
        addLayout.setContentsMargins(1, 1, 1, 1)
        addLayout.setSpacing(1)
        addLayout.addWidget(self.__excTypeEdit)
        addLayout.addWidget(self.__addButton)

        verticalLayout.addWidget(self.headerFrame)
        verticalLayout.addWidget(self.toolbar)
        verticalLayout.addWidget(self.exceptionsList)
        verticalLayout.addLayout(addLayout)

    def clear(self):
        """Clears the content"""
        self.exceptionsList.clear()
        self.__excTypeEdit.clear()
        self.__addButton.setEnabled(False)
        self.__ignored = []
        self.__currentItem = None
        self.__updateTitle()

    def __onShowHide(self, startup=False):
        """Triggered when show/hide button is clicked"""
        if startup or self.exceptionsList.isVisible():
            self.exceptionsList.setVisible(False)
            self.__excTypeEdit.setVisible(False)
            self.__addButton.setVisible(False)
            self.__removeButton.setVisible(False)
            self.__removeAllButton.setVisible(False)
            self.__showHideButton.setIcon(getIcon('more.png'))
            self.__showHideButton.setToolTip("Show ignored exceptions list")

            self.__minH = self.minimumHeight()
            self.__maxH = self.maximumHeight()

            self.setMinimumHeight(self.headerFrame.height())
            self.setMaximumHeight(self.headerFrame.height())

            Settings()['showIgnoredExcViewer'] = False
        else:
            self.exceptionsList.setVisible(True)
            self.__excTypeEdit.setVisible(True)
            self.__addButton.setVisible(True)
            self.__removeButton.setVisible(True)
            self.__removeAllButton.setVisible(True)
            self.__showHideButton.setIcon(getIcon('less.png'))
            self.__showHideButton.setToolTip("Hide ignored exceptions list")

            self.setMinimumHeight(self.__minH)
            self.setMaximumHeight(self.__maxH)

            Settings()['showIgnoredExcViewer'] = True

    def __onSelectionChanged(self):
        """Triggered when the current item is changed"""
        selected = list(self.exceptionsList.selectedItems())
        if selected:
            self.__currentItem = selected[0]
            self.__removeButton.setEnabled(True)
        else:
            self.__currentItem = None
            self.__removeButton.setEnabled(False)

    def __showContextMenu(self, coord):
        """Shows the frames list context menu"""
        contextItem = self.exceptionsList.itemAt(coord)
        if contextItem is not None:
            self.__currentItem = contextItem
            self.__excptMenu.popup(QCursor.pos())

    def __updateTitle(self):
        """Updates the section title"""
        count = self.exceptionsList.topLevelItemCount()
        if count == 0:
            self.__excptLabel.setText("Ignored exception types")
        else:
            self.__excptLabel.setText("Ignored exception types (total: " +
                                      str(count) + ")")
        self.__removeAllButton.setEnabled(count != 0)

    def __onProjectChanged(self, what):
        """Triggered when a project is changed"""
        if what != CodimensionProject.CompleteProject:
            return

        self.clear()
        project = GlobalData().project
        if project.isLoaded():
            self.__ignored = list(project.exceptionFilters)
        else:
            self.__ignored = Settings()['ignoredExceptions']

        for exceptionType in self.__ignored:
            item = QTreeWidgetItem(self.exceptionsList)
            item.setText(0, exceptionType)
        self.__updateTitle()

    def __onNewFilterChanged(self, text):
        """Triggered when the text is changed"""
        text = str(text).strip()
        if text == "":
            self.__addButton.setEnabled(False)
            return
        if " " in text:
            self.__addButton.setEnabled(False)
            return

        if text in self.__ignored:
            self.__addButton.setEnabled(False)
            return

        self.__addButton.setEnabled(True)

    def __onAddExceptionFilter(self):
        """Adds an item into the ignored exceptions list"""
        text = self.__excTypeEdit.text().strip()
        self.addExceptionFilter(text)

    def addExceptionFilter(self, excType):
        """Adds a new item into the ignored exceptions list"""
        if excType == "":
            return
        if " " in excType:
            return
        if excType in self.__ignored:
            return

        item = QTreeWidgetItem(self.exceptionsList)
        item.setText(0, excType)

        project = GlobalData().project
        if project.isLoaded():
            project.addExceptionFilter(excType)
        else:
            Settings().addExceptionFilter(excType)
        self.__ignored.append(excType)
        self.__updateTitle()

    def __onRemoveFromIgnore(self):
        """Removes an item from the ignored exception types list"""
        if self.__currentItem is None:
            return

        text = self.__currentItem.text(0)

        # Find the item index and remove it
        index = 0
        while True:
            if self.exceptionsList.topLevelItem(index).text(0) == text:
                self.exceptionsList.takeTopLevelItem(index)
                break
            index += 1

        project = GlobalData().project
        if project.isLoaded():
            project.deleteExceptionFilter(text)
        else:
            Settings().deleteExceptionFilter(text)
        self.__ignored.remove(text)
        self.__updateTitle()

    def __onRemoveAllFromIgnore(self):
        """Triggered when all the ignored exceptions should be deleted"""
        self.clear()

        project = GlobalData().project
        if project.isLoaded():
            project.setExceptionFilters([])
        else:
            Settings().setExceptionFilters([])

    def isIgnored(self, exceptionType):
        """Returns True if this exception type should be ignored"""
        return exceptionType in self.__ignored
Example #6
0
class SVNPluginLogDialog(QDialog):
    """SVN plugin log dialog"""

    NODIFF = '<html><body bgcolor="#ffffe6"></body></html>'

    def __init__(self, plugin, client, path, logInfo, parent=None):
        QDialog.__init__(self, parent)

        self.__plugin = plugin
        self.__client = client
        self.__path = path
        self.__logInfo = logInfo

        self.__lhsSelected = None
        self.__rhsSelected = None

        self.__createLayout()
        self.setWindowTitle("SVN Log")

        lastIndex = len(self.__logInfo) - 1
        index = 0
        for log in self.__logInfo:
            newItem = LogItem(log)
            self.__logView.addTopLevelItem(newItem)

            if index != lastIndex:
                rev = log.revision.number
                nextRev = self.__logInfo[index + 1].revision.number
                diffButton = self.__createDiffButton(
                    log.revision, self.__logInfo[index + 1].revision)
                if rev is not None and nextRev is not None:
                    diffButton.setToolTip(
                        "Click to see diff to the older revision (r." +
                        str(rev) + " to r." + str(nextRev) + ")")
                else:
                    diffButton.setEnabled(False)
                    diffButton.setToolTip(
                        "Could not determine current or previous revision")
            else:
                diffButton = self.__createDiffButton(None, None)
                diffButton.setEnabled(False)
                diffButton.setToolTip(
                    "Diff to previous revision is not avalable for the first revision"
                )

            self.__logView.setItemWidget(newItem, DIFFTONEXT_COL, diffButton)
            index += 1

        self.__resizeLogView()
        self.__sortLogView()

        self.__logView.setFocus()

    def __createDiffButton(self, rev, prevRev):
        """Creates a diff button for a path"""
        button = DiffButton()
        button.rev = rev
        button.prevRev = prevRev
        self.connect(button, SIGNAL('CustomClick'), self.onDiffBetween)
        return button

    def __resizeLogView(self):
        """Resizes the plugins table"""
        self.__logView.header().setStretchLastSection(True)
        self.__logView.header().resizeSections(QHeaderView.ResizeToContents)
        self.__logView.header().resizeSection(SELECT_COL, 28)
        self.__logView.header().setResizeMode(SELECT_COL, QHeaderView.Fixed)

        self.__logView.header().resizeSection(DIFFTONEXT_COL, 24)
        self.__logView.header().setResizeMode(DIFFTONEXT_COL,
                                              QHeaderView.Fixed)

    def __sortLogView(self):
        """Sorts the log table"""
        self.__logView.sortItems(self.__logView.sortColumn(),
                                 self.__logView.header().sortIndicatorOrder())

    def __createLayout(self):
        """Creates the dialog layout"""
        self.resize(640, 480)
        self.setSizeGripEnabled(True)

        vboxLayout = QVBoxLayout(self)

        # Revisions to compare
        compareGroupbox = QGroupBox(self)
        compareGroupbox.setTitle("Revisions to compare")
        sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            compareGroupbox.sizePolicy().hasHeightForWidth())
        compareGroupbox.setSizePolicy(sizePolicy)

        revisionLayout = QHBoxLayout(compareGroupbox)

        self.__lhsRevisionLabel = QLabel()
        self.__lhsRevisionLabel.setFrameStyle(QFrame.StyledPanel)
        self.__lhsResetButton = QToolButton()
        self.__lhsResetButton.setIcon(
            getIcon(pluginHomeDir + 'svnclearrev.png'))
        self.__lhsResetButton.setFocusPolicy(Qt.NoFocus)
        self.__lhsResetButton.setEnabled(False)
        self.__lhsResetButton.setToolTip("Reset revision to compare")
        self.__lhsResetButton.clicked.connect(self.__onLHSReset)
        self.__rhsRevisionLabel = QLabel()
        self.__rhsRevisionLabel.setFrameStyle(QFrame.StyledPanel)
        self.__rhsResetButton = QToolButton()
        self.__rhsResetButton.setIcon(
            getIcon(pluginHomeDir + 'svnclearrev.png'))
        self.__rhsResetButton.setFocusPolicy(Qt.NoFocus)
        self.__rhsResetButton.setEnabled(False)
        self.__rhsResetButton.setToolTip("Reset revision to compare")
        self.__rhsResetButton.clicked.connect(self.__onRHSReset)

        lhsLayout = QHBoxLayout()
        lhsLayout.addWidget(self.__lhsRevisionLabel)
        lhsLayout.addWidget(self.__lhsResetButton)
        rhsLayout = QHBoxLayout()
        rhsLayout.addWidget(self.__rhsRevisionLabel)
        rhsLayout.addWidget(self.__rhsResetButton)
        bothLayout = QVBoxLayout()
        bothLayout.addLayout(lhsLayout)
        bothLayout.addLayout(rhsLayout)
        revisionLayout.addLayout(bothLayout)

        self.__diffButton = QToolButton()
        self.__diffButton.setText("Diff")
        self.__diffButton.setFocusPolicy(Qt.NoFocus)
        self.__diffButton.setEnabled(False)
        self.__diffButton.clicked.connect(self.__onDiff)
        revisionLayout.addWidget(self.__diffButton)
        vboxLayout.addWidget(compareGroupbox)

        # Log table
        logHeaderFrame = QFrame()
        logHeaderFrame.setFrameStyle(QFrame.StyledPanel)
        logHeaderFrame.setAutoFillBackground(True)
        self.__setLightPalette(logHeaderFrame)
        logHeaderFrame.setFixedHeight(24)

        logHeaderLayout = QHBoxLayout()
        logHeaderLayout.setContentsMargins(3, 0, 0, 0)
        logHeaderLayout.addWidget(QLabel("Subversion log of " + self.__path))
        logHeaderFrame.setLayout(logHeaderLayout)
        vboxLayout.addWidget(logHeaderFrame)

        self.__logView = QTreeWidget()
        self.__logView.setAlternatingRowColors(True)
        self.__logView.setRootIsDecorated(False)
        self.__logView.setItemsExpandable(False)
        self.__logView.setSortingEnabled(True)
        self.__logView.setItemDelegate(NoOutlineHeightDelegate(4))

        self.__logViewHeader = QTreeWidgetItem(
            ["", "", "Revision", "Date", "Author", "Message"])
        self.__logView.setHeaderItem(self.__logViewHeader)
        self.__logView.header().setSortIndicator(REVISION_COL,
                                                 Qt.AscendingOrder)
        self.__logView.itemChanged.connect(self.__onLogViewChanged)
        vboxLayout.addWidget(self.__logView)

        # Diff part
        diffHeaderFrame = QFrame()
        diffHeaderFrame.setFrameStyle(QFrame.StyledPanel)
        diffHeaderFrame.setAutoFillBackground(True)
        self.__setLightPalette(diffHeaderFrame)
        diffHeaderFrame.setFixedHeight(24)

        diffLabel = QLabel("Diff")
        diffExpandingSpacer = QSpacerItem(10, 10, QSizePolicy.Expanding)

        self.__showHideDiffButton = QToolButton()
        self.__showHideDiffButton.setAutoRaise(True)
        self.__showHideDiffButton.setIcon(getIcon('less.png'))
        self.__showHideDiffButton.setFixedSize(20, 20)
        self.__showHideDiffButton.setToolTip("Show diff")
        self.__showHideDiffButton.setFocusPolicy(Qt.NoFocus)
        self.__showHideDiffButton.clicked.connect(self.__onShowHideDiff)

        diffLayout = QHBoxLayout()
        diffLayout.setContentsMargins(3, 0, 0, 0)
        diffLayout.addWidget(diffLabel)
        diffLayout.addSpacerItem(diffExpandingSpacer)
        diffLayout.addWidget(self.__showHideDiffButton)
        diffHeaderFrame.setLayout(diffLayout)

        self.__diffViewer = DiffTabWidget()
        self.__diffViewer.setHTML(self.NODIFF)
        self.__diffViewer.setVisible(False)

        vboxLayout.addWidget(diffHeaderFrame)
        vboxLayout.addWidget(self.__diffViewer)

        # Buttons at the bottom
        buttonBox = QDialogButtonBox(self)
        buttonBox.setOrientation(Qt.Horizontal)
        buttonBox.setStandardButtons(QDialogButtonBox.Ok)
        buttonBox.button(QDialogButtonBox.Ok).setDefault(True)
        buttonBox.accepted.connect(self.close)
        vboxLayout.addWidget(buttonBox)

    @staticmethod
    def __setLightPalette(frame):
        """Creates a lighter palette for the widget background"""
        palette = frame.palette()
        background = palette.color(QPalette.Background)
        background.setRgb(min(background.red() + 30, 255),
                          min(background.green() + 30, 255),
                          min(background.blue() + 30, 255))
        palette.setColor(QPalette.Background, background)
        frame.setPalette(palette)

    def __onShowHideDiff(self):
        """On/off the diff section"""
        if self.__diffViewer.isVisible():
            self.__diffViewer.setVisible(False)
            self.__showHideDiffButton.setIcon(getIcon('less.png'))
            self.__showHideDiffButton.setToolTip("Show diff")
        else:
            self.__diffViewer.setVisible(True)
            self.__showHideDiffButton.setIcon(getIcon('more.png'))
            self.__showHideDiffButton.setToolTip("Hide diff")

    def onDiffBetween(self, rev, prevRev):
        """Called when diff is requested between revisions"""
        if not rev or not prevRev:
            return

        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        try:
            lhsContent = self.__client.cat(self.__path, prevRev)
            rhsContent = self.__client.cat(self.__path, rev)
        except Exception as exc:
            QApplication.restoreOverrideCursor()
            logging.error(str(exc))
            return
        except:
            QApplication.restoreOverrideCursor()
            logging.error("Unknown error while retrieving " + self.__path +
                          " content from the repository.")
            return
        QApplication.restoreOverrideCursor()

        diff = difflib.unified_diff(lhsContent.splitlines(),
                                    rhsContent.splitlines())
        nodiffMessage = self.__path + " has no difference from revision " + \
            str(prevRev.number) + " to revision " + str(rev.number)
        if diff is None:
            logging.info(nodiffMessage)
            return

        # There are changes, so replace the text and tell about the changes
        diffAsText = '\n'.join(list(diff))
        if diffAsText.strip() == '':
            logging.info(nodiffMessage)
            return

        lhs = "--- revision " + str(prevRev.number)
        diffAsText = diffAsText.replace("--- ", lhs, 1)
        rhs = "+++ revision " + str(rev.number)
        diffAsText = diffAsText.replace("+++ ", rhs, 1)

        self.__diffViewer.setHTML(parse_from_memory(diffAsText, False, True))
        if not self.__diffViewer.isVisible():
            self.__onShowHideDiff()

    def __onDiff(self):
        """Show diff between revisions"""
        self.onDiffBetween(self.__rhsSelected.revision,
                           self.__lhsSelected.revision)

    def __onLogViewChanged(self, item, column):
        """Revision selected for diff"""
        if item.checkState(SELECT_COL) == Qt.Checked:
            # An item has been selected
            if self.__lhsSelected is None:
                self.__lhsSelected = item.logInfo
                self.__normalizeSelected()
                return
            if self.__rhsSelected is None:
                self.__rhsSelected = item.logInfo
                self.__normalizeSelected()
                return

            # Both of the places have been occupied. Pick the one to update.
            if item.logInfo.date > self.__rhsSelected.date:
                self.__rhsSelected = item.logInfo
            else:
                self.__lhsSelected = item.logInfo
            self.__normalizeSelected()
        else:
            # An item has been de-selected
            if self.__lhsSelected is not None:
                if self.__lhsSelected.revision.number == item.logInfo.revision.number:
                    self.__lhsSelected = None
            elif self.__rhsSelected is not None:
                if self.__rhsSelected.revision.number == item.logInfo.revision.number:
                    self.__rhsSelected = None
            self.__normalizeSelected()
        return

    def __onLHSReset(self):
        """Revision removed from diff"""
        if self.__lhsSelected is not None:
            self.__deselectRevision(self.__lhsSelected.revision.number)
        self.__lhsSelected = None
        self.__lhsRevisionLabel.setText("")
        self.__diffButton.setEnabled(False)
        self.__lhsResetButton.setEnabled(False)

    def __onRHSReset(self):
        """Revision removed from diff"""
        if self.__rhsSelected is not None:
            self.__deselectRevision(self.__rhsSelected.revision.number)
        self.__rhsSelected = None
        self.__rhsRevisionLabel.setText("")
        self.__diffButton.setEnabled(False)
        self.__rhsResetButton.setEnabled(False)

    def __deselectRevision(self, revNumber):
        """Deselects a revision in the list"""
        index = 0
        while index < self.__logView.topLevelItemCount():
            item = self.__logView.topLevelItem(index)
            if item.logInfo.revision.number == revNumber:
                item.setCheckState(SELECT_COL, Qt.Unchecked)
                break
            index += 1

    def __normalizeSelected(self):
        """Puts the earliest revision first"""
        if self.__lhsSelected is not None and self.__rhsSelected is not None:
            # It might be necessary to exchange the versions
            if self.__rhsSelected.date < self.__lhsSelected.date:
                temp = self.__rhsSelected
                self.__rhsSelected = self.__lhsSelected
                self.__lhsSelected = temp
            self.__diffButton.setEnabled(True)
        else:
            self.__diffButton.setEnabled(False)

        if self.__lhsSelected is None:
            self.__lhsRevisionLabel.setText("")
            self.__lhsRevisionLabel.setToolTip("")
            self.__lhsResetButton.setEnabled(False)
        else:
            self.__lhsRevisionLabel.setText(
                str(self.__lhsSelected.revision.number) + " (" +
                timestampToString(self.__lhsSelected.date) + ")")
            self.__lhsRevisionLabel.setToolTip(str(self.__lhsSelected.message))
            self.__lhsResetButton.setEnabled(True)

        if self.__rhsSelected is None:
            self.__rhsRevisionLabel.setText("")
            self.__rhsRevisionLabel.setToolTip("")
            self.__rhsResetButton.setEnabled(False)
        else:
            self.__rhsRevisionLabel.setText(
                str(self.__rhsSelected.revision.number) + " (" +
                timestampToString(self.__rhsSelected.date) + ")")
            self.__rhsRevisionLabel.setToolTip(str(self.__rhsSelected.message))
            self.__rhsResetButton.setEnabled(True)
class WatchPointViewer(QWidget):
    """Implements the watch point viewer for a debugger"""
    def __init__(self, parent, wpointModel):
        QWidget.__init__(self, parent)

        self.__currentItem = None

        self.__createPopupMenu()
        self.__createLayout(wpointModel)

        GlobalData().project.sigProjectChanged.connect(self.__onProjectChanged)

        if Settings()['showWatchPointViewer'] == False:
            self.__onShowHide(True)

    def __createPopupMenu(self):
        """Creates the popup menu"""
        #        self.__excptMenu = QMenu()
        #        self.__removeMenuItem = self.__excptMenu.addAction(
        #                    "Remove from ignore list", self.__onRemoveFromIgnore )
        return

    def __createLayout(self, wpointModel):
        """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.__watchpointLabel = QLabel("Watchpoints")

        expandingSpacer = QSpacerItem(10, 10, QSizePolicy.Expanding)
        fixedSpacer = QSpacerItem(3, 3)

        self.__showHideButton = QToolButton()
        self.__showHideButton.setAutoRaise(True)
        self.__showHideButton.setIcon(getIcon('less.png'))
        self.__showHideButton.setFixedSize(20, 20)
        self.__showHideButton.setToolTip("Hide ignored exceptions list")
        self.__showHideButton.setFocusPolicy(Qt.NoFocus)
        self.__showHideButton.clicked.connect(self.__onShowHide)

        headerLayout = QHBoxLayout()
        headerLayout.setContentsMargins(1, 1, 1, 1)
        headerLayout.addSpacerItem(fixedSpacer)
        headerLayout.addWidget(self.__watchpointLabel)
        headerLayout.addSpacerItem(expandingSpacer)
        headerLayout.addWidget(self.__showHideButton)
        self.headerFrame.setLayout(headerLayout)

        self.__wpointsList = WatchPointView(self, wpointModel)

        self.__enableButton = QToolButton()
        self.__enableButton.setIcon(getIcon('add.png'))
        self.__enableButton.setFixedSize(24, 24)
        self.__enableButton.setToolTip("Enable/disable the watchpoint")
        self.__enableButton.setFocusPolicy(Qt.NoFocus)
        self.__enableButton.setEnabled(False)
        self.__enableButton.clicked.connect(self.__onEnableDisable)

        expandingSpacer = QSpacerItem(10, 10, QSizePolicy.Expanding)

        self.__jumpToCodeButton = QToolButton()
        self.__jumpToCodeButton.setIcon(getIcon('gotoline.png'))
        self.__jumpToCodeButton.setFixedSize(24, 24)
        self.__jumpToCodeButton.setToolTip("Jump to the code")
        self.__jumpToCodeButton.setFocusPolicy(Qt.NoFocus)
        self.__jumpToCodeButton.setEnabled(False)
        self.__jumpToCodeButton.clicked.connect(self.__onJumpToCode)

        toolbarLayout = QHBoxLayout()
        toolbarLayout.addWidget(self.__enableButton)
        toolbarLayout.addSpacerItem(expandingSpacer)
        toolbarLayout.addWidget(self.__jumpToCodeButton)

        self.__wpointsList.sigSelectionChanged.connect(
            self.__onSelectionChanged)

        verticalLayout.addWidget(self.headerFrame)
        verticalLayout.addLayout(toolbarLayout)
        verticalLayout.addWidget(self.__wpointsList)

    def clear(self):
        """Clears the content"""
        #        self.__wpointsList.clear()
        self.__updateTitle()
        self.__jumpToCodeButton.setEnabled(False)
        self.__currentItem = None

    def __onJumpToCode(self):
        """Jumps to the corresponding source code line"""
        return

    def __onShowHide(self, startup=False):
        """Triggered when show/hide button is clicked"""
        if startup or self.__wpointsList.isVisible():
            self.__wpointsList.setVisible(False)
            self.__enableButton.setVisible(False)
            self.__jumpToCodeButton.setVisible(False)
            self.__showHideButton.setIcon(getIcon('more.png'))
            self.__showHideButton.setToolTip("Show watchpoints list")

            self.__minH = self.minimumHeight()
            self.__maxH = self.maximumHeight()

            self.setMinimumHeight(self.headerFrame.height())
            self.setMaximumHeight(self.headerFrame.height())

            Settings()['showWatchPointViewer'] = False
        else:
            self.__wpointsList.setVisible(True)
            self.__enableButton.setVisible(True)
            self.__jumpToCodeButton.setVisible(True)
            self.__showHideButton.setIcon(getIcon('less.png'))
            self.__showHideButton.setToolTip("Hide watchpoints list")

            self.setMinimumHeight(self.__minH)
            self.setMaximumHeight(self.__maxH)

            Settings()['showWatchPointViewer'] = True

    def __onSelectionChanged(self, index):
        """Triggered when the current item is changed"""
        if index.isValid():
            pass
        else:
            pass
        return
        selected = list(self.__exceptionsList.selectedItems())
        if selected:
            self.__currentItem = selected[0]
            self.__removeButton.setEnabled(True)
        else:
            self.__currentItem = None
            self.__removeButton.setEnabled(False)

    def __updateTitle(self):
        """Updates the section title"""
        count = self.getTotalCount()
        if count == 0:
            self.__watchpointLabel.setText("Watchpoints")
        else:
            self.__watchpointLabel.setText("Watchpoints (total: " +
                                           str(count) + ")")

    def getTotalCount(self):
        """Provides the total number of watch points"""
        count = 0
        return count

    def __onProjectChanged(self, what):
        """Triggered when a project is changed"""
        if what == CodimensionProject.CompleteProject:
            self.clear()

    def __onEnableDisable(self):
        """Triggered when a breakpoint should be enabled/disabled"""
        pass