def __createLayout(self, labelText):
        """Creates the dialog layout"""
        self.resize(600, 250)
        self.setSizeGripEnabled(True)

        # Top level layout
        layout = QVBoxLayout(self)

        layout.addWidget(QLabel(labelText))
        self.__newCaption = QTextEdit()
        self.__newCaption.setFont(getZoomedMonoFont())
        self.__newCaption.setAcceptRichText(False)
        layout.addWidget(self.__newCaption)

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

        self.__newCaption.setFocus()
Пример #2
0
    def __createLayout(self):
        """Creates the dialog layout"""
        self.resize(450, 150)
        self.setSizeGripEnabled(True)

        verticalLayout = QVBoxLayout(self)
        gridLayout = QGridLayout()

        # Link
        gridLayout.addWidget(QLabel('Link', self), 0, 0, 1, 1)
        self.linkEdit = QLineEdit(self)
        self.linkEdit.setClearButtonEnabled(True)
        self.linkEdit.setToolTip(
            'A link to a file or to an external web resource')
        gridLayout.addWidget(self.linkEdit, 0, 1, 1, 1)
        self.linkEdit.textChanged.connect(self.__validate)
        self.fileButton = QPushButton(self)
        self.fileButton.setText('...')
        self.fileButton.setToolTip('Select an existing or non existing file')
        gridLayout.addWidget(self.fileButton, 0, 2, 1, 1)
        self.fileButton.clicked.connect(self.__onSelectPath)
        self.createCheckBox = QCheckBox(
            'Create a markdown file if does not exist', self)
        self.createCheckBox.setChecked(False)
        gridLayout.addWidget(self.createCheckBox, 1, 1, 1, 1)
        self.createCheckBox.stateChanged.connect(self.__validate)

        # Anchor
        gridLayout.addWidget(QLabel('Anchor', self), 2, 0, 1, 1)
        self.anchorEdit = QLineEdit(self)
        self.anchorEdit.setClearButtonEnabled(True)
        gridLayout.addWidget(self.anchorEdit, 2, 1, 1, 1)
        self.anchorEdit.textChanged.connect(self.__validate)

        # Title
        titleLabel = QLabel('Title', self)
        titleLabel.setAlignment(Qt.AlignTop)
        gridLayout.addWidget(titleLabel, 3, 0, 1, 1)
        self.titleEdit = QTextEdit()
        self.titleEdit.setTabChangesFocus(True)
        self.titleEdit.setAcceptRichText(False)
        self.titleEdit.setFont(getZoomedMonoFont())
        self.titleEdit.setToolTip(
            'If provided then will be displayed in the rectangle')
        gridLayout.addWidget(self.titleEdit, 3, 1, 1, 1)

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

        verticalLayout.addLayout(gridLayout)
        verticalLayout.addWidget(buttonBox)

        self.linkEdit.setFocus()
Пример #3
0
    def createLogWindow(self):
        """Creates a dockable RO editor for logging"""
        self.logWidget = QTextEdit()
        self.logWidget.setReadOnly(True)
        self.logWidget.setFontFamily("Courier")
        self.logWidget.setFontPointSize(12.0)

        logDockWidget = QDockWidget("Log", self)
        logDockWidget.setObjectName("LogDockWidget")
        logDockWidget.setAllowedAreas(Qt.BottomDockWidgetArea)
        logDockWidget.setWidget(self.logWidget)

        self.addDockWidget(Qt.BottomDockWidgetArea, logDockWidget)
class ReplaceTextDialog(QDialog):
    """Replace text input dialog"""
    def __init__(self, windowTitle, labelText, parent=None):
        QDialog.__init__(self, parent)
        self.setWindowTitle(windowTitle)
        self.__createLayout(labelText)

    def __createLayout(self, labelText):
        """Creates the dialog layout"""
        self.resize(600, 250)
        self.setSizeGripEnabled(True)

        # Top level layout
        layout = QVBoxLayout(self)

        layout.addWidget(QLabel(labelText))
        self.__newCaption = QTextEdit()
        self.__newCaption.setFont(getZoomedMonoFont())
        self.__newCaption.setAcceptRichText(False)
        layout.addWidget(self.__newCaption)

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

        self.__newCaption.setFocus()

    def setText(self, txt):
        """Sets the text to be edited"""
        self.__newCaption.setPlainText(txt)

    def text(self):
        """Provides the new text"""
        return self.__newCaption.toPlainText()
Пример #5
0
    def __createLayout(self, varName, varType, varValue, isGlobal):
        """Creates the dialog layout"""
        varTypeParts = varType.split()
        if varTypeParts[0].lower() in ["string", "unicode", "qstring"]:
            length = str(len(varValue))
            lines = str(len(varValue.splitlines()))
            varType = varType.split("(")[0].strip() + \
                      " (lines: " + lines + ", characters: " + length + ")"

        self.resize(600, 250)
        self.setSizeGripEnabled(True)

        # Top level layout
        layout = QVBoxLayout(self)

        gridLayout = QGridLayout()
        gridLayout.setSpacing(4)
        varScopeLabel = QLabel("Scope:")
        gridLayout.addWidget(varScopeLabel, 0, 0, Qt.AlignCenter)
        if isGlobal:
            varScopeValue = FramedLabelWithDoubleClick("Global")
        else:
            varScopeValue = FramedLabelWithDoubleClick("Local")
        varScopeValue.setToolTip("Double click to copy")
        varScopeValue.setStyleSheet(getLabelStyle(self))
        font = varScopeValue.font()
        font.setFamily(GlobalData().skin['monoFont'].family())
        gridLayout.addWidget(varScopeValue, 0, 1)

        varNameLabel = QLabel("Name:")
        gridLayout.addWidget(varNameLabel, 1, 0, Qt.AlignCenter)
        varNameValue = FramedLabelWithDoubleClick(varName)
        varNameValue.setToolTip("Double click to copy")
        varNameValue.setStyleSheet(getLabelStyle(self))
        gridLayout.addWidget(varNameValue, 1, 1)

        varTypeLabel = QLabel("Type:")
        gridLayout.addWidget(varTypeLabel, 2, 0, Qt.AlignCenter)
        varTypeValue = FramedLabelWithDoubleClick(varType)
        varTypeValue.setToolTip("Double click to copy")
        varTypeValue.setStyleSheet(getLabelStyle(self))
        gridLayout.addWidget(varTypeValue, 2, 1)

        varValueLabel = QLabel("Value:")
        gridLayout.addWidget(varValueLabel, 3, 0, Qt.AlignTop)
        varValueValue = QTextEdit()
        varValueValue.setReadOnly(True)
        varValueValue.setFont(getZoomedMonoFont())
        # varValueValue.setLineWrapMode(QTextEdit.NoWrap)
        varValueValue.setAcceptRichText(False)
        varValueValue.setPlainText(varValue)
        gridLayout.addWidget(varValueValue, 3, 1)
        layout.addLayout(gridLayout)

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

        varValueValue.setFocus()
Пример #6
0
class MainWindow(QMainWindow):
    """Main application window"""
    def __init__(self, verbose, fName, warning):
        QMainWindow.__init__(self)

        self.logWidget = None
        self.view = None
        self.scene = None
        self.fName = fName
        self.verbose = verbose
        self.cFlow = None

        self.resize(1400, 800)

        self.updateWindowTitle()
        self.statusBar()
        self.createToolbar()
        self.createLogWindow()
        self.createGraphicsView()

        self.setCentralWidget(self.view)

        if verbose:
            self.logMessage("Using cdmcfparser version " + VERSION)

        if warning:
            self.logMessage(warning)

        if fName:
            # To yeld the main message processing loop
            kickOffTimer = QTimer()
            kickOffTimer.singleShot(200, self.proceedWithFile)

    def createToolbar(self):
        """There are a few buttons on the main window toolbar.

           They are: open, reload, zoom out, zoom in, debug, clear log
        """
        openButton = QAction(QIcon('icons/open.png'), 'Open (Ctrl+O)', self)
        openButton.setShortcut('Ctrl+O')
        openButton.setStatusTip('Open python file')
        openButton.triggered.connect(self.openButtonClicked)

        reloadButton = QAction(QIcon('icons/reload.png'), 'Reload (F5)', self)
        reloadButton.setShortcut('F5')
        reloadButton.setStatusTip('Reload python file')
        reloadButton.triggered.connect(self.reloadButtonClicked)

        zoomoutButton = QAction(QIcon('icons/zoomOut.png'),
                                'Zoom Out (Ctrl+-)', self)
        zoomoutButton.setShortcut('Ctrl+-')
        zoomoutButton.setStatusTip('Zoom Out')
        zoomoutButton.triggered.connect(self.zoomOut)

        zoominButton = QAction(QIcon('icons/zoomIn.png'), 'Zoom In (Ctrl++)',
                               self)
        zoominButton.setShortcut('Ctrl++')
        zoominButton.setStatusTip('Zoom In')
        zoominButton.triggered.connect(self.zoomIn)

        clearLogButton = QAction(QIcon('icons/clear.png'),
                                 'Clear log (Ctrl+R)', self)
        clearLogButton.setShortcut('Ctrl+R')
        clearLogButton.setStatusTip('Clear log')
        clearLogButton.triggered.connect(self.clearButtonClicked)

        # A few separators
        separator = QAction(self)
        separator.setSeparator(True)
        separator1 = QAction(self)
        separator1.setSeparator(True)

        toolbar = self.addToolBar('Toolbar')
        toolbar.setIconSize(QSize(48, 48))
        toolbar.addAction(openButton)
        toolbar.addAction(reloadButton)
        toolbar.addAction(separator)
        toolbar.addAction(zoomoutButton)
        toolbar.addAction(zoominButton)
        toolbar.addAction(separator1)
        toolbar.addAction(clearLogButton)

    def createLogWindow(self):
        """Creates a dockable RO editor for logging"""
        self.logWidget = QTextEdit()
        self.logWidget.setReadOnly(True)
        self.logWidget.setFontFamily("Courier")
        self.logWidget.setFontPointSize(12.0)

        logDockWidget = QDockWidget("Log", self)
        logDockWidget.setObjectName("LogDockWidget")
        logDockWidget.setAllowedAreas(Qt.BottomDockWidgetArea)
        logDockWidget.setWidget(self.logWidget)

        self.addDockWidget(Qt.BottomDockWidgetArea, logDockWidget)

    def zoomIn(self):
        """zoom in the main window"""
        self.view.zoomIn()

    def zoomOut(self):
        """zoom out the main window"""
        self.view.zoomOut()

    def reloadButtonClicked(self):
        """reload button has been clicked"""
        self.proceedWithFile()

    def clearButtonClicked(self):
        """Deletes all the messages from the log window"""
        self.logWidget.clear()

    def createGraphicsView(self):
        """Creates the central widget"""
        self.scene = QGraphicsScene(self)

        self.view = CFGraphicsView(self)
        self.view.setScene(self.scene)

    def updateWindowTitle(self):
        """updates the main window title with the current so file"""
        if self.fName:
            self.setWindowTitle('Control flow for: ' + self.fName)
        else:
            self.setWindowTitle('Control flow for: no file selected')

    def logMessage(self, message):
        """Makes a log message visible in the user interface"""
        timestamp = datetime.datetime.now().strftime('%m-%d-%y %H:%M:%S.%f')
        self.logWidget.append(timestamp + " " + message)
        self.logWidget.update()

    def openButtonClicked(self):
        """Brings up an open dialogue"""
        # By some unknown reasons the following simple way of getting a file is
        # not working:
        # fileName = QFileDialog.getOpenFileName(self, 'Open file',
        #                                        QDir.currentPath())
        #
        # There is however a workaround. Here it is:
        dialog = QFileDialog(self)
        if dialog.exec_() != QDialog.Accepted:
            return

        fileNames = dialog.selectedFiles()
        fileName = str(fileNames[0])

        if not os.path.exists(fileName):
            QMessageBox.critical(
                self, 'Error',
                'The selected file (' + fileName + ') does not exist')
            return

        # Check that the file is a python one
        warning = isPythonFile(fileName)
        if warning is not None:
            QMessageBox.critical(self, 'Error', warning)
            return

        # set the new file name
        self.fName = fileName
        self.updateWindowTitle()

        # initiate the process
        self.proceedWithFile()

    def proceedWithFile(self, needToParse=True):
        """Taks the file from settings and processes it"""
        if needToParse:
            if self.verbose:
                self.logMessage("Parsing file " + self.fName)
            self.cFlow = getControlFlowFromFile(self.fName)
            if self.verbose:
                self.logMessage("Parsed file:")
                self.logMessage(formatFlow(str(self.cFlow)))

            if len(self.cFlow.errors) != 0:
                self.logMessage("No drawing due to parsing errors")
                return

            if len(self.cFlow.warnings) != 0:
                self.logMessage("Parser warnings: ")
                for warn in self.cFlow.warnings:
                    self.logMessage(str(warn[0]) + ": " + warn[1])
        else:
            if self.cFlow is None:
                self.logMessage("No control flow object")
                return
            if len(self.cFlow.errors) != 0:
                self.logMessage("No drawing due to parsing errors")
                return

        self.scene.clear()

        if self.verbose:
            self.logMessage("Layouting ...")
        try:
            # To pick up possibly changed settings
            importlib.reload(cflowsettings)
            cflowSettings = cflowsettings.getDefaultCflowSettings(self)
            if DEBUG:
                cflowSettings.debug = True

            # Top level canvas has no adress and no parent canvas
            canvas = vcanvas.VirtualCanvas(cflowSettings, None, None, None)
            canvas.layout(self.cFlow, CellElement.FILE_SCOPE)
            if self.verbose:
                self.logMessage("Layout is done:")
                self.logMessage(str(canvas))
                self.logMessage("Rendering ...")

            width, height = canvas.render()
            if self.verbose:
                self.logMessage("Rendering is done. Scene size: " +
                                str(width) + "x" + str(height) +
                                ". Drawing ...")

            self.scene.setSceneRect(0, 0, width, height)
            canvas.draw(self.scene, 0, 0)
        except Exception as exc:
            self.logMessage("Exception:\n" + str(exc))
            raise

        if self.verbose:
            self.logMessage("Drawing is done.")
Пример #7
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 != "")
Пример #8
0
    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)
Пример #9
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()
Пример #10
0
    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)
Пример #11
0
class PluginsDialog(QDialog):
    """Codimension plugins dialog"""
    def __init__(self, pluginManager, parent=None):
        QDialog.__init__(self, parent)
        self.setWindowTitle("Plugin Manager")

        self.__pluginManager = pluginManager
        self.__configFuncs = {}  # int -> callable

        self.__createLayout()
        self.__populate()

        self.__pluginsView.setFocus()
        self.__inItemChange = False

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

        layout = QVBoxLayout()

        # Plugins list
        self.__pluginsView = QTreeWidget()
        self.__pluginsView.setAlternatingRowColors(True)
        self.__pluginsView.setRootIsDecorated(False)
        self.__pluginsView.setItemsExpandable(False)
        self.__pluginsView.setSortingEnabled(True)
        self.__pluginsView.setItemDelegate(NoOutlineHeightDelegate(4))
        self.__pluginsView.setUniformRowHeights(True)

        # Alert | system/user | Enable | Name | Version
        self.__pluginsHeader = QTreeWidgetItem(
            ["", "", "", "Name", "Version", ""])
        self.__pluginsView.setHeaderItem(self.__pluginsHeader)
        self.__pluginsView.header().setSortIndicator(NAME_COL,
                                                     Qt.AscendingOrder)
        self.__pluginsView.itemSelectionChanged.connect(
            self.__pluginSelectionChanged)
        self.__pluginsView.itemChanged.connect(self.__onItemChanged)

        layout.addWidget(self.__pluginsView)

        # Detailed information
        detailsLabel = QLabel("Detailed information")
        layout.addWidget(detailsLabel)
        self.__details = QTreeWidget()
        self.__details.setAlternatingRowColors(False)
        self.__details.setRootIsDecorated(False)
        self.__details.setItemsExpandable(False)
        self.__details.setSortingEnabled(False)
        self.__details.setItemDelegate(NoOutlineHeightDelegate(4))
        self.__details.setUniformRowHeights(True)

        detailsHeader = QTreeWidgetItem(["", ""])
        self.__details.setHeaderItem(detailsHeader)
        self.__details.setHeaderHidden(True)

        metrics = QFontMetrics(self.__details.font())
        rect = metrics.boundingRect("X")
        self.__details.setFixedHeight(rect.height() * 6 + 5)
        layout.addWidget(self.__details)

        # Errors/warnings
        errorsLabel = QLabel("Errors / warnings")
        layout.addWidget(errorsLabel)
        self.__errorsText = QTextEdit()
        self.__errorsText.setReadOnly(True)
        self.__errorsText.setAcceptRichText(False)
        metrics = QFontMetrics(self.__errorsText.font())
        rect = metrics.boundingRect("X")
        self.__errorsText.setFixedHeight(rect.height() * 4 + 5)
        layout.addWidget(self.__errorsText)

        # Buttons box
        buttonBox = QDialogButtonBox(self)
        buttonBox.setOrientation(Qt.Horizontal)
        buttonBox.setStandardButtons(QDialogButtonBox.Ok)
        self.__OKButton = buttonBox.button(QDialogButtonBox.Ok)
        self.__OKButton.setDefault(True)
        buttonBox.accepted.connect(self.close)
        buttonBox.rejected.connect(self.close)
        layout.addWidget(buttonBox)

        self.setLayout(layout)

    def __createConfigButton(self):
        """Creates a configure button for a plugin"""
        button = SettingsButton()
        button.CustomClick.connect(self.onPluginSettings)
        return button

    def __populate(self):
        """Populates the list with the plugins"""
        index = 0

        for category in self.__pluginManager.activePlugins:
            for cdmPlugin in self.__pluginManager.activePlugins[category]:
                newItem = PluginItem(self.__pluginManager, cdmPlugin, True,
                                     category)
                self.__pluginsView.addTopLevelItem(newItem)
                settingsButton = self.__createConfigButton()

                try:
                    configFunction = cdmPlugin.getObject().getConfigFunction()
                    if configFunction is None:
                        settingsButton.setToolTip(
                            "Plugin does not need configuring")
                        settingsButton.setEnabled(False)
                    else:
                        settingsButton.setToolTip("Click to configure")
                        settingsButton.setEnabled(True)
                        self.__configFuncs[index] = configFunction
                        settingsButton.index = index
                        index += 1
                except Exception:
                    settingsButton.setToolTip("Bad plugin interface. No "
                                              "configuration function "
                                              "received.")
                    settingsButton.setEnabled(False)

                self.__pluginsView.setItemWidget(newItem, SETTINGS_COL,
                                                 settingsButton)

        for category in self.__pluginManager.inactivePlugins:
            for cdmPlugin in self.__pluginManager.inactivePlugins[category]:
                newItem = PluginItem(self.__pluginManager, cdmPlugin, False,
                                     category)
                self.__pluginsView.addTopLevelItem(newItem)
                settingsButton = self.__createConfigButton()

                try:
                    configFunction = cdmPlugin.getObject().getConfigFunction()
                    if configFunction is None:
                        settingsButton.setToolTip(
                            "Plugin does not need configuring")
                        settingsButton.setEnabled(False)
                    else:
                        settingsButton.setToolTip(
                            "Enable plugin and then click to configure")
                        settingsButton.setEnabled(False)
                        self.__configFuncs[index] = configFunction
                        settingsButton.index = index
                        index += 1
                except:
                    settingsButton.setToolTip("Bad plugin interface. No "
                                              "configuration function "
                                              "received.")
                    settingsButton.setEnabled(False)

                self.__pluginsView.setItemWidget(newItem, SETTINGS_COL,
                                                 settingsButton)

        for cdmPlugin in self.__pluginManager.unknownPlugins:
            newItem = PluginItem(self.__pluginManager, cdmPlugin, False, None)
            self.__pluginsView.addTopLevelItem(newItem)
            settingsButton = self.__createConfigButton()
            settingsButton.setToolTip("Unknown plugins are not configurable")
            settingsButton.setEnabled(False)
            self.__pluginsView.setItemWidget(newItem, SETTINGS_COL,
                                             settingsButton)

        self.__sortPlugins()
        self.__resizePlugins()

    def __sortPlugins(self):
        """Sorts the plugins table"""
        self.__pluginsView.sortItems(
            self.__pluginsView.sortColumn(),
            self.__pluginsView.header().sortIndicatorOrder())

    def __resizePlugins(self):
        """Resizes the plugins table"""
        self.__pluginsView.header().setStretchLastSection(False)
        self.__pluginsView.header().resizeSections(
            QHeaderView.ResizeToContents)
        self.__pluginsView.header().resizeSection(STATE_COL, 28)
        self.__pluginsView.header().setSectionResizeMode(
            STATE_COL, QHeaderView.Fixed)
        self.__pluginsView.header().resizeSection(CONFLICT_COL, 28)
        self.__pluginsView.header().setSectionResizeMode(
            CONFLICT_COL, QHeaderView.Fixed)
        self.__pluginsView.header().resizeSection(TYPE_COL, 28)
        self.__pluginsView.header().setSectionResizeMode(
            TYPE_COL, QHeaderView.Fixed)

        self.__pluginsView.header().setSectionResizeMode(
            VERSION_COL, QHeaderView.Stretch)
        self.__pluginsView.header().resizeSection(SETTINGS_COL, 24)
        self.__pluginsView.header().setSectionResizeMode(
            SETTINGS_COL, QHeaderView.Fixed)

    def __pluginSelectionChanged(self):
        """Triggered when an item is selected"""
        selected = list(self.__pluginsView.selectedItems())
        if selected:
            self.__updateDetails(selected[0])
        else:
            self.__updateDetails(None)

    def __updateDetails(self, item):
        """Updates the content of the details and the error boxes"""
        self.__details.clear()
        self.__errorsText.setText("")

        if item is None:
            return

        self.__details.addTopLevelItem(
            QTreeWidgetItem(["Author", item.plugin.getAuthor()]))
        self.__details.addTopLevelItem(
            QTreeWidgetItem(["Path",
                             os.path.normpath(item.plugin.getPath())]))
        self.__details.addTopLevelItem(
            QTreeWidgetItem(["Description",
                             item.plugin.getDescription()]))
        self.__details.addTopLevelItem(
            QTreeWidgetItem(["Web site", item.plugin.getWebsite()]))

        copyright = item.plugin.getCopyright()
        if copyright is not None:
            if copyright.lower() != "unknown":
                self.__details.addTopLevelItem(
                    QTreeWidgetItem(["Copyright", copyright]))

        for name in item.plugin.getDetails():
            value = item.plugin.getDetails()[name]
            self.__details.addTopLevelItem(QTreeWidgetItem([name, value]))

        self.__errorsText.setText(item.plugin.conflictMessage)

    def __onItemChanged(self, item, column):
        """Triggered when an item is changed"""
        if self.__inItemChange:
            return

        if item.active:
            self.__inItemChange = True
            item.plugin.disable()
            item.active = False

            settingsButton = self.__pluginsView.itemWidget(item, SETTINGS_COL)
            settingsButton.setEnabled(False)
            if settingsButton.index != -1:
                settingsButton.setToolTip(
                    "Enable plugin and then click to configure")

            if item.category in self.__pluginManager.inactivePlugins:
                self.__pluginManager. \
                    inactivePlugins[item.category].append(item.plugin)
            else:
                self.__pluginManager. \
                    inactivePlugins[item.category] = [item.plugin]
            self.__pluginManager. \
                activePlugins[item.category].remove(item.plugin)
            self.__pluginManager.saveDisabledPlugins()
            self.__inItemChange = False
            self.__pluginManager.sendPluginDeactivated(item.plugin)
            return

        self.__inItemChange = True
        message = self.__pluginManager.checkConflict(item.plugin)
        if message is not None:
            item.setCheckState(STATE_COL, Qt.Unchecked)
            self.__errorsText.setText(message)
            self.__inItemChange = False
            return

        try:
            item.plugin.enable()
            item.active = True
            if item.category in self.__pluginManager.activePlugins:
                self.__pluginManager. \
                    activePlugins[item.category].append(item.plugin)
            else:
                self.__pluginManager. \
                    activePlugins[item.category] = [item.plugin]
            self.__pluginManager. \
                inactivePlugins[item.category].remove(item.plugin)
            self.__pluginManager.saveDisabledPlugins()
            self.__errorsText.setText("")
            item.setIcon(CONFLICT_COL, getIcon('empty.png'))
            item.setToolTip(CONFLICT_COL, "")

            settingsButton = self.__pluginsView.itemWidget(item, SETTINGS_COL)
            if settingsButton.index != -1:
                settingsButton.setToolTip("Click to configure")
                settingsButton.setEnabled(True)
            self.__pluginManager.sendPluginActivated(item.plugin)
        except Exception as exc:
            item.setCheckState(STATE_COL, Qt.Unchecked)
            self.__errorsText.setText(
                "Error activating the plugin, exception is generated:\n" +
                str(exc))
        except:
            item.setCheckState(STATE_COL, Qt.Unchecked)
            self.__errorsText.setText(
                "Error activating the plugin, unknown exception")

        self.__inItemChange = False

    def onPluginSettings(self, index):
        """Triggered when a configuring function is called"""
        if index not in self.__configFuncs:
            return

        try:
            self.__configFuncs[index]()
        except Exception as exc:
            logging.error("Error calling the plugin configuration function. "
                          "Message: " + str(exc))
Пример #12
0
    def __createLayout(self):
        """Creates the dialog layout"""
        self.resize(640, 480)
        self.setSizeGripEnabled(True)

        layout = QVBoxLayout()

        # Plugins list
        self.__pluginsView = QTreeWidget()
        self.__pluginsView.setAlternatingRowColors(True)
        self.__pluginsView.setRootIsDecorated(False)
        self.__pluginsView.setItemsExpandable(False)
        self.__pluginsView.setSortingEnabled(True)
        self.__pluginsView.setItemDelegate(NoOutlineHeightDelegate(4))
        self.__pluginsView.setUniformRowHeights(True)

        # Alert | system/user | Enable | Name | Version
        self.__pluginsHeader = QTreeWidgetItem(
            ["", "", "", "Name", "Version", ""])
        self.__pluginsView.setHeaderItem(self.__pluginsHeader)
        self.__pluginsView.header().setSortIndicator(NAME_COL,
                                                     Qt.AscendingOrder)
        self.__pluginsView.itemSelectionChanged.connect(
            self.__pluginSelectionChanged)
        self.__pluginsView.itemChanged.connect(self.__onItemChanged)

        layout.addWidget(self.__pluginsView)

        # Detailed information
        detailsLabel = QLabel("Detailed information")
        layout.addWidget(detailsLabel)
        self.__details = QTreeWidget()
        self.__details.setAlternatingRowColors(False)
        self.__details.setRootIsDecorated(False)
        self.__details.setItemsExpandable(False)
        self.__details.setSortingEnabled(False)
        self.__details.setItemDelegate(NoOutlineHeightDelegate(4))
        self.__details.setUniformRowHeights(True)

        detailsHeader = QTreeWidgetItem(["", ""])
        self.__details.setHeaderItem(detailsHeader)
        self.__details.setHeaderHidden(True)

        metrics = QFontMetrics(self.__details.font())
        rect = metrics.boundingRect("X")
        self.__details.setFixedHeight(rect.height() * 6 + 5)
        layout.addWidget(self.__details)

        # Errors/warnings
        errorsLabel = QLabel("Errors / warnings")
        layout.addWidget(errorsLabel)
        self.__errorsText = QTextEdit()
        self.__errorsText.setReadOnly(True)
        self.__errorsText.setAcceptRichText(False)
        metrics = QFontMetrics(self.__errorsText.font())
        rect = metrics.boundingRect("X")
        self.__errorsText.setFixedHeight(rect.height() * 4 + 5)
        layout.addWidget(self.__errorsText)

        # Buttons box
        buttonBox = QDialogButtonBox(self)
        buttonBox.setOrientation(Qt.Horizontal)
        buttonBox.setStandardButtons(QDialogButtonBox.Ok)
        self.__OKButton = buttonBox.button(QDialogButtonBox.Ok)
        self.__OKButton.setDefault(True)
        buttonBox.accepted.connect(self.close)
        buttonBox.rejected.connect(self.close)
        layout.addWidget(buttonBox)

        self.setLayout(layout)
Пример #13
0
class DocLinkAnchorDialog(QDialog):
    """Replace text input dialog"""
    def __init__(self, windowTitle, cmlDocComment, fileName, parent):
        QDialog.__init__(self, parent)

        # Name of a file from which the doc link is created/edited
        self.__fileName = fileName
        self.setWindowTitle(windowTitle + ' documentation link and/or anchor')

        self.__createLayout()
        self.__invalidInputColor = GlobalData().skin['invalidInputPaper']
        self.__validInputColor = self.linkEdit.palette().color(
            self.linkEdit.backgroundRole())

        if cmlDocComment is not None:
            self.__populate(cmlDocComment)

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

        verticalLayout = QVBoxLayout(self)
        gridLayout = QGridLayout()

        # Link
        gridLayout.addWidget(QLabel('Link', self), 0, 0, 1, 1)
        self.linkEdit = QLineEdit(self)
        self.linkEdit.setClearButtonEnabled(True)
        self.linkEdit.setToolTip(
            'A link to a file or to an external web resource')
        gridLayout.addWidget(self.linkEdit, 0, 1, 1, 1)
        self.linkEdit.textChanged.connect(self.__validate)
        self.fileButton = QPushButton(self)
        self.fileButton.setText('...')
        self.fileButton.setToolTip('Select an existing or non existing file')
        gridLayout.addWidget(self.fileButton, 0, 2, 1, 1)
        self.fileButton.clicked.connect(self.__onSelectPath)
        self.createCheckBox = QCheckBox(
            'Create a markdown file if does not exist', self)
        self.createCheckBox.setChecked(False)
        gridLayout.addWidget(self.createCheckBox, 1, 1, 1, 1)
        self.createCheckBox.stateChanged.connect(self.__validate)

        # Anchor
        gridLayout.addWidget(QLabel('Anchor', self), 2, 0, 1, 1)
        self.anchorEdit = QLineEdit(self)
        self.anchorEdit.setClearButtonEnabled(True)
        gridLayout.addWidget(self.anchorEdit, 2, 1, 1, 1)
        self.anchorEdit.textChanged.connect(self.__validate)

        # Title
        titleLabel = QLabel('Title', self)
        titleLabel.setAlignment(Qt.AlignTop)
        gridLayout.addWidget(titleLabel, 3, 0, 1, 1)
        self.titleEdit = QTextEdit()
        self.titleEdit.setTabChangesFocus(True)
        self.titleEdit.setAcceptRichText(False)
        self.titleEdit.setToolTip(
            'If provided then will be displayed in the rectangle')
        gridLayout.addWidget(self.titleEdit, 3, 1, 1, 1)

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

        verticalLayout.addLayout(gridLayout)
        verticalLayout.addWidget(buttonBox)

        self.linkEdit.setFocus()

    def setTitle(self, txt):
        """Sets the title text to be edited"""
        self.titleEdit.setPlainText(txt)

    def title(self):
        """Provides the new title text"""
        return self.titleEdit.toPlainText()

    def needToCreate(self):
        return self.createCheckBox.isEnabled(
        ) and self.createCheckBox.isChecked()

    def __populate(self, cmlDocComment):
        """Populates the fields from the comment"""
        if cmlDocComment.link:
            self.linkEdit.setText(cmlDocComment.link)
        if cmlDocComment.anchor:
            self.anchorEdit.setText(cmlDocComment.anchor)
        if cmlDocComment.title:
            self.setTitle(cmlDocComment.getTitle())

    def __onSelectPath(self):
        """Select file or directory"""
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        selectedPath = QFileDialog.getOpenFileName(self,
                                                   'Select documentation file',
                                                   self.linkEdit.text(),
                                                   options=options)
        if isinstance(selectedPath, tuple):
            selectedPath = selectedPath[0]
        if selectedPath:
            self.linkEdit.setText(os.path.normpath(selectedPath))

    def __setLinkValid(self):
        """Sets the link edit valid"""
        self.linkEdit.setToolTip(
            'A link to a file or to an external web resource')
        setLineEditBackground(self.linkEdit, self.__validInputColor,
                              self.__validInputColor)

    def __setLinkInvalid(self, msg):
        """Sets the link edit invalid"""
        self.linkEdit.setToolTip(msg)
        setLineEditBackground(self.linkEdit, self.__invalidInputColor,
                              self.__invalidInputColor)

    def __validateLink(self):
        """Validates the link field content"""
        txt = self.linkEdit.text().strip()
        if txt == '' or txt.startswith('http://') or txt.startswith(
                'https://'):
            self.__setLinkValid()
            self.createCheckBox.setEnabled(False)
            return True

        if txt.endswith(os.path.sep):
            self.__setLinkInvalid('A link must be a file, not a directory')
            return False

        # Not a link; it is supposed to be a file or a creatable file
        # However the invalid values will also be acceptable
        self.createCheckBox.setEnabled(True)
        fromFile = None
        if self.__fileName:
            if os.path.isabs(self.__fileName):
                fromFile = self.__fileName
        fName, anchor, errMsg = preResolveLinkPath(
            txt, fromFile, self.createCheckBox.isChecked())
        del anchor

        if fName:
            self.__setLinkValid()
            return True

        self.__setLinkInvalid(errMsg)
        return not self.createCheckBox.isChecked()

    def __setAnchorValid(self):
        """Sets the anchor edit valid"""
        self.anchorEdit.setToolTip(
            'Anchor may not contain neither spaces nor tabs')
        setLineEditBackground(self.anchorEdit, self.__invalidInputColor,
                              self.__validInputColor)

    def __setAnchorInvalid(self):
        """Sets the anchor edit invalid"""
        self.anchorEdit.setToolTip(
            'Anchor is used to refer to it from the other files')
        setLineEditBackground(self.anchorEdit, self.__validInputColor,
                              self.__validInputColor)

    def __validateAnchor(self):
        """Validates the anchor field"""
        txt = self.anchorEdit.text().strip()
        if ' ' in txt or '\t' in txt:
            self.__setAnchorValid()
            return False
        self.__setAnchorInvalid()
        return True

    def __validate(self, _=None):
        """Validates the input fields and sets the OK button enable"""
        self.__OKButton.setToolTip('')
        valid = self.__validateAnchor() and self.__validateLink()
        if valid:
            if not self.linkEdit.text().strip() and not self.anchorEdit.text(
            ).strip():
                valid = False
                self.__OKButton.setToolTip(
                    'At least one of the items: link or anchor must be provided'
                )

        self.__OKButton.setEnabled(valid)
        return valid