Ejemplo n.º 1
0
class SVNPluginPropsDialog(QDialog):
    """SVN plugin properties dialog"""
    def __init__(self, plugin, client, path, parent=None):
        QDialog.__init__(self, parent)

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

        self.__createLayout()
        self.setWindowTitle("SVN Properties of " + path)
        self.__populate()
        self.__propsView.setFocus()

    def __populate(self):
        """Populate the properties list"""
        # Get the currently selected name
        selectedName = None
        selected = list(self.__propsView.selectedItems())
        if selected:
            selectedName = str(selected[0].text(0))

        self.__propsView.clear()
        properties = readProperties(self.__client, self.__path)
        if properties:
            for itemPath, itemProps in properties:
                if self.__path == itemPath or \
                   self.__path == itemPath + os.path.sep:
                    for name, value in itemProps.iteritems():
                        name = str(name).strip()
                        value = str(value).strip()
                        newItem = QTreeWidgetItem([name, value])
                        self.__propsView.addTopLevelItem(newItem)

        self.__resizePropsView()
        self.__sortPropsView()

        if selectedName:
            index = 0
            for index in range(0, self.__propsView.topLevelItemCount()):
                item = self.__propsView.topLevelItem(index)
                if selectedName == item.text(0):
                    item.setSelected(True)

    def __resizePropsView(self):
        """Resizes the properties table"""
        self.__propsView.header().setStretchLastSection(True)
        self.__propsView.header().resizeSections(QHeaderView.ResizeToContents)

    def __sortPropsView(self):
        """Sorts the properties table"""
        self.__propsView.sortItems(
            self.__propsView.sortColumn(),
            self.__propsView.header().sortIndicatorOrder())

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

        vboxLayout = QVBoxLayout(self)

        hLayout = QHBoxLayout()
        self.__propsView = QTreeWidget()
        self.__propsView.setAlternatingRowColors(True)
        self.__propsView.setRootIsDecorated(False)
        self.__propsView.setItemsExpandable(False)
        self.__propsView.setSortingEnabled(True)
        self.__propsView.setItemDelegate(NoOutlineHeightDelegate(4))
        self.__propsView.itemSelectionChanged.connect(
            self.__propsSelectionChanged)

        propsViewHeader = QTreeWidgetItem(["Property Name", "Property Value"])
        self.__propsView.setHeaderItem(propsViewHeader)
        self.__propsView.header().setSortIndicator(0, Qt.DescendingOrder)
        hLayout.addWidget(self.__propsView)

        self.__delButton = QToolButton()
        self.__delButton.setText("Delete")
        self.__delButton.setFocusPolicy(Qt.NoFocus)
        self.__delButton.setEnabled(False)
        self.__delButton.clicked.connect(self.__onDel)
        hLayout.addWidget(self.__delButton, 0, Qt.AlignBottom)
        vboxLayout.addLayout(hLayout)

        # Set property part
        setGroupbox = QGroupBox(self)
        setGroupbox.setTitle("Set Property")

        setLayout = QGridLayout(setGroupbox)
        setLayout.addWidget(QLabel("Name"), 0, 0, Qt.AlignTop | Qt.AlignRight)
        setLayout.addWidget(QLabel("Value"), 1, 0, Qt.AlignTop | Qt.AlignRight)

        self.__nameEdit = QLineEdit()
        self.__nameEdit.textChanged.connect(self.__nameChanged)
        setLayout.addWidget(self.__nameEdit, 0, 1)

        self.__valueEdit = QTextEdit()
        self.__valueEdit.setAcceptRichText(False)
        self.__valueEdit.textChanged.connect(self.__valueChanged)
        metrics = QFontMetrics(self.__valueEdit.font())
        rect = metrics.boundingRect("X")
        self.__valueEdit.setFixedHeight(rect.height() * 4 + 5)
        setLayout.addWidget(self.__valueEdit, 1, 1)

        self.__setButton = QToolButton()
        self.__setButton.setText("Set")
        self.__setButton.setFocusPolicy(Qt.NoFocus)
        self.__setButton.setEnabled(False)
        self.__setButton.clicked.connect(self.__onSet)
        setLayout.addWidget(self.__setButton, 1, 2,
                            Qt.AlignBottom | Qt.AlignHCenter)

        sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            setGroupbox.sizePolicy().hasHeightForWidth())
        setGroupbox.setSizePolicy(sizePolicy)
        vboxLayout.addWidget(setGroupbox)

        # 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)

    def __onSet(self):
        """Triggered when propery set is clicked"""
        name = self.__nameEdit.text().strip()
        value = self.__valueEdit.toPlainText().strip()
        try:
            commitInfo = self.__client.propset(name, value, self.__path)
            if commitInfo:
                logging.info(str(commitInfo))
            self.__populate()
            self.__plugin.notifyPathChanged(self.__path)
            self.__nameEdit.clear()
            self.__valueEdit.clear()
            self.__propsView.setFocus()
        except pysvn.ClientError as exc:
            message = exc.args[0]
            logging.error(message)
        except Exception as exc:
            logging.error(str(exc))
        except:
            logging.error("Unknown property setting error")

    def __propsSelectionChanged(self):
        """Selection of a property has changed"""
        selected = list(self.__propsView.selectedItems())
        self.__delButton.setEnabled(len(selected) > 0)

    def __onDel(self):
        """Triggered when a property del is clicked"""
        selected = list(self.__propsView.selectedItems())
        if len(selected) == 0:
            self.__delButton.setEnabled(False)
            return

        name = str(selected[0].text(0))
        res = QMessageBox.warning(
            self, "Deleting Property", "You are about to delete <b>" + name +
            "</b> SVN property from " + self.__path + ".\nAre you sure?",
            QMessageBox.StandardButtons(QMessageBox.Cancel | QMessageBox.Yes),
            QMessageBox.Cancel)
        if res != QMessageBox.Yes:
            return

        try:
            self.__client.propdel(name, self.__path)
            self.__populate()
            self.__plugin.notifyPathChanged(self.__path)
            self.__propsView.setFocus()
        except pysvn.ClientError as exc:
            message = exc.args[0]
            logging.error(message)
        except Exception as exc:
            logging.error(str(exc))
        except:
            logging.error("Unknown property deleting error")

    def __nameChanged(self, text):
        """Triggered when a property name to set is changed"""
        self.__updateSetButton()

    def __valueChanged(self):
        """Triggered when a property value to set is changed"""
        self.__updateSetButton()

    def __updateSetButton(self):
        """Updates the 'Set' button state"""
        name = self.__nameEdit.text().strip()
        value = self.__valueEdit.toPlainText().strip()
        self.__setButton.setEnabled(name != "" and value != "")
Ejemplo n.º 2
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)