Пример #1
0
    def __init__(self, unmerged_files, parent=None):
        QDialog.__init__(self, parent=parent)

        self._ui = Ui_Dialog()
        self._ui.setupUi(self)

        self._parent = parent

        self.tree_items = {}
        self._status_items = []
        self._solutions = {}
        self._current_path = ""
        self._radio_choices = {self._ui.deleteRadioButton: "delete",
                               self._ui.addRadioButton: "add",
                               self._ui.addCustomRadioButton: "add_custom"}

        self._u_files = unmerged_files

        u_files = self._u_files

        statuses = set([u_info["git_status"] for u_info in u_files.values()])
        for status in statuses:

            status_item = QTreeWidgetItem(self._ui.treeWidget)
            status_item.setText(0, QString(GIT_STATUSES[status]))
            status_item.setExpanded(True)

            self._status_items.append(status_item)

            for u_path in [u_path for u_path in u_files
                           if u_files[u_path]["git_status"] == status]:
                file_item = QTreeWidgetItem(status_item)
                file_item.setText(0, QString(u_path))
                self.tree_items[file_item] = u_path

        self.connect_signals()

        # Hide every widget of the conflict details layout.
        self.show_all_details(False)

        document = self._ui.conflictTextEdit.document()
        syntax_highlight = SimpleGitMessagesHighlighter(document)
Пример #2
0
class ConflictsDialog(QDialog):

    def __init__(self, unmerged_files, parent=None):
        QDialog.__init__(self, parent=parent)

        self._ui = Ui_Dialog()
        self._ui.setupUi(self)

        self._parent = parent

        self.tree_items = {}
        self._status_items = []
        self._solutions = {}
        self._current_path = ""
        self._radio_choices = {self._ui.deleteRadioButton: "delete",
                               self._ui.addRadioButton: "add",
                               self._ui.addCustomRadioButton: "add_custom"}

        self._u_files = unmerged_files

        u_files = self._u_files

        statuses = set([u_info["git_status"] for u_info in u_files.values()])
        for status in statuses:

            status_item = QTreeWidgetItem(self._ui.treeWidget)
            status_item.setText(0, QString(GIT_STATUSES[status]))
            status_item.setExpanded(True)

            self._status_items.append(status_item)

            for u_path in [u_path for u_path in u_files
                           if u_files[u_path]["git_status"] == status]:
                file_item = QTreeWidgetItem(status_item)
                file_item.setText(0, QString(u_path))
                self.tree_items[file_item] = u_path

        self.connect_signals()

        # Hide every widget of the conflict details layout.
        self.show_all_details(False)

        document = self._ui.conflictTextEdit.document()
        syntax_highlight = SimpleGitMessagesHighlighter(document)

    def connect_signals(self):
        connect(self._ui.treeWidget,
                SIGNAL("itemClicked(QTreeWidgetItem *, int)"),
                self.item_clicked)

        for button in self._radio_choices:
            connect(button, SIGNAL("clicked()"), self.radio_button_clicked)

        # Hide every widget of the conflict details layout.
        self.show_all_details(False)

        connect(self._ui.applySolutionsButton, SIGNAL("clicked()"),
                self.apply_solutions)

    def show_all_details(self, show):
        conDetLayout = self._ui.conflictDetailsGridLayout

        for item_id in xrange(conDetLayout.count()):
            conDetLayout.itemAt(item_id).widget().setVisible(show)

        # The none radio button should always be hidden.
        self._ui.noneRadioButton.hide()

    def radio_button_clicked(self):
        writable = self.sender() == self._ui.addCustomRadioButton
        self._ui.conflictTextEdit.setEnabled(writable)

    def set_choice(self):
        """
            When called, this method uses the current path and the radio
            buttons to find out what choice must be saved.
        """
        checked_radios = [radio for radio in self._radio_choices
                          if radio.isChecked()]
        assert len(checked_radios) < 2, \
                "There should not be more than one checked radio button."

        if checked_radios:
            choice = self._radio_choices[checked_radios[0]]
            cstm_content = ""
            if choice == "add_custom":
                cstm_content = unicode(self._ui.conflictTextEdit.toPlainText())

            self._solutions[self._current_path] = (choice, cstm_content)

    def item_clicked(self, item, column):
        """
            When a QTreeWidgetItem is clicked, display the details of the
            related unmerged file.

            :param item:
                The clicked QTreeWidgetItem.

            :param column:
                The clicked column (unused).
        """
        # Before changing the item, store the solutions if the user made some
        self.set_choice()

        if item.childCount():
            # This is a top level item (a git status item)
            # Hide every widget of the conflict details layout.
            self.show_all_details(False)
        else:
            # This is a file item
            # Show every widget of the conflict details layout.
            self.show_all_details(True)

            u_path = self.tree_items[item]
            self._current_path = u_path
            self._ui.filepathLabel.setText(QString(u_path))

            # Reset the conflictTextEdit
            self._ui.conflictTextEdit.setEnabled(False)

            # Reset the solution radio buttons
            self._ui.noneRadioButton.setChecked(True)

            u_info = self._u_files[u_path]
            git_status = u_info["git_status"]

            add_available_states = ('DU', 'UD', 'UA', 'AU')
            delete_available_states = ('DD', 'DU', 'UD', 'UA', 'AU')

            if git_status in add_available_states:
                self._ui.addRadioButton.show()
            else:
                self._ui.addRadioButton.hide()

            if git_status in delete_available_states:
                self._ui.deleteRadioButton.show()
            else:
                self._ui.deleteRadioButton.hide()

            if git_status == 'DD':
                conflict_content = \
                        "<font color=#FF0000>The file isn't present after " + \
                        "the merge conflict."
                self._ui.conflictTextEdit.setText(QString(conflict_content))
                self._ui.addCustomRadioButton.hide()
            else:
                unmerged_content = u_info["unmerged_content"]
                conflict_content = QString(unmerged_content)
                self._ui.conflictTextEdit.setText(conflict_content)
                self._ui.addCustomRadioButton.show()

            if git_status == 'AU':
                diff_text_edit_content = \
                        "<font color=#FF0000>The file isn't present in " + \
                        "the conflicting commit tree."
                self._ui.diffTextEdit.setText(QString(diff_text_edit_content))
            else:
                diff = u_info["diff"]
                self._ui.diffTextEdit.setText(QString(diff))

            if git_status in ('UA', 'DU', 'DD'):
                # The file wasn't present in the tree before the merge conflict
                orig_text_edit_content = \
                        "<font color=#FF0000>The file wasn't present in " + \
                        "the tree before the merge conflict."
                self._ui.origTextEdit.setHtml(QString(orig_text_edit_content))
            else:
                orig_content = u_info["orig_content"]
                orig_text_edit_content = orig_content
                self._ui.origTextEdit.setText(QString(orig_text_edit_content))

            # If the user already chose a solution, check the right radiobutton
            if u_path in self._solutions:
                pre_choice = self._solutions[u_path][0]
                for radio, choice in self._radio_choices.items():
                    if pre_choice == choice:
                        writable = radio == self._ui.addCustomRadioButton
                        self._ui.conflictTextEdit.setEnabled(writable)
                        radio.setChecked(True)

                        if writable:
                            text = self._solutions[u_path][1]
                            self._ui.conflictTextEdit.setText(QString(text))


    def apply_solutions(self):
        """
            When the "Apply solutions" button is clicked, check that all
            conflicts have been marked as resolved, and pass it on to
            the model.
        """
        # Saved the current path edited
        self.set_choice()

        unsolved_items_parents = set([])

        for u_file in self._u_files:
            tree_item = [item for item in self.tree_items
                         if self.tree_items[item] == u_file][0]

            if u_file not in self._solutions:
                tree_item.setBackgroundColor(0, Qt.red)
                unsolved_items_parents.add(tree_item.parent())
            else:
                tree_item.setBackgroundColor(0, Qt.transparent)

        # Collapse the parent if all the items are solved
        for top_item in self._status_items:
            if top_item not in unsolved_items_parents:
                top_item.setExpanded(False)

        if unsolved_items_parents:
            return

        ret = True
        for line in [infos[1] for infos in self._solutions.values()]:
            if contains_chevron(line):
                msgBox = QMessageBox(self)
                msgBox.setText("Some of the files contain git chevrons.")
                msgBox.setInformativeText("Do you want to apply anyway?")
                msgBox.setStandardButtons(msgBox.Cancel | msgBox.Apply)
                msgBox.setDefaultButton(msgBox.Cancel)
                ret = (msgBox.exec_() == msgBox.Apply)
                break

        if  ret:
            self.accept()

    def get_solutions(self):
        """
            Return the selected solutions.
        """
        return self._solutions