differ.py 6.83 KB
Newer Older
1
2
3
# -*- coding: utf-8 -*-

"""
4
Copyright (C) 2008-2016 Wolfgang Rohdewald <wolfgang@rohdewald.de>
5

6
Kajongg is free software you can redistribute it and/or modify
7
8
9
10
11
12
13
14
15
16
17
it under the terms of the GNU General Public License as published by
the Free Software Foundation either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program if not, write to the Free Software
18
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
20
"""

21
from qt import Qt, QAbstractTableModel, QModelIndex
22
from qt import QLabel, QDialog, QHBoxLayout, QVBoxLayout, QDialogButtonBox
23

24
from mi18n import i18n, i18nc
25
from statesaver import StateSaver
26
from guiutil import ListComboBox, MJTableView, decorateWindow
27
from guiutil import BlockSignals
Wolfgang Rohdewald's avatar
Wolfgang Rohdewald committed
28
29
from common import Debug
from modeltest import ModelTest
30

31

32
class DifferModel(QAbstractTableModel):
33

34
    """a model for our ruleset differ"""
35

36
37
38
39
40
    def __init__(self, diffs, view):
        super(DifferModel, self).__init__()
        self.diffs = diffs
        self.view = view

41
    def columnCount(self, unusedIndex=QModelIndex()):
42
        # pylint: disable=no-self-use
43
        """how many columns does this node have?"""
44
        return 3  # rule name, left values, right values
45

46
    def rowCount(self, parent):
47
        """how many items?"""
48
49
50
        if parent.isValid():
            # we have only top level items
            return 0
51
52
53
        return len(self.diffs)

    def data(self, index, role=Qt.DisplayRole):
54
        """get from model"""
Wolfgang Rohdewald's avatar
Wolfgang Rohdewald committed
55
56
57
        if not index.isValid():
            return
        if not 0 <= index.row() < len(self.diffs):
58
            return
59
60
61
        diff = self.diffs[index.row()]
        column = index.column()
        if role == Qt.DisplayRole:
62
            return diff[column]
63
        elif role == Qt.TextAlignmentRole:
64
            return int(Qt.AlignLeft | Qt.AlignVCenter)
65
66
67
68
69

    def headerData(self, section, orientation, role):
        """tell the view about the wanted headers"""
        if role == Qt.TextAlignmentRole:
            if orientation == Qt.Horizontal:
70
                return int(Qt.AlignLeft | Qt.AlignVCenter)
71
        if role != Qt.DisplayRole:
72
            return
73
74
        if orientation == Qt.Horizontal:
            if section == 0:
Wolfgang Rohdewald's avatar
Wolfgang Rohdewald committed
75
                return i18nc('Kajongg', 'Rule')
76
            if section == 1:
Wolfgang Rohdewald's avatar
Wolfgang Rohdewald committed
77
                return i18n(self.view.cbRuleset1.current.name)
78
            if section == 2:
Wolfgang Rohdewald's avatar
Wolfgang Rohdewald committed
79
                return i18n(self.view.cbRuleset2.current.name)
80
81
82


class RulesetDiffer(QDialog):
83

84
    """Shows differences between rulesets"""
85

86
87
88
89
90
91
    def __init__(self, leftRulesets, rightRulesets, parent=None):
        QDialog.__init__(self, parent)
        if not isinstance(leftRulesets, list):
            leftRulesets = list([leftRulesets])
        if not isinstance(rightRulesets, list):
            rightRulesets = list([rightRulesets])
92
        leftRulesets, rightRulesets = leftRulesets[:], rightRulesets[:]
93
94
95
96
97
98
99
        # remove rulesets from right which are also on the left side
        for left in leftRulesets:
            left.load()
        for right in rightRulesets:
            right.load()
        for left in leftRulesets:
            for right in rightRulesets[:]:
Wolfgang Rohdewald's avatar
Wolfgang Rohdewald committed
100
                if left == right and left.name == right.name:
101
102
103
104
                    # rightRulesets.remove(right) this is wrong because it
                    # removes the first ruleset with the same hash
                    rightRulesets = list(
                        x for x in rightRulesets if id(x) != id(right))
105
106
        self.leftRulesets = leftRulesets
        self.rightRulesets = rightRulesets
107
        self.model = None
Wolfgang Rohdewald's avatar
Wolfgang Rohdewald committed
108
        self.modelTest = None
109
        self.view = MJTableView(self)
110
111
        self.buttonBox = QDialogButtonBox()
        self.buttonBox.setStandardButtons(QDialogButtonBox.Ok)
112
113
        self.buttonBox.accepted.connect(self.accept)
        self.buttonBox.rejected.connect(self.reject)
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131

        cbLayout = QHBoxLayout()
        self.cbRuleset1 = ListComboBox(self.leftRulesets)
        if len(self.leftRulesets) == 1:
            self.lblRuleset1 = QLabel(self.leftRulesets[0].name)
            cbLayout.addWidget(self.lblRuleset1)
        else:
            cbLayout.addWidget(self.cbRuleset1)
        self.cbRuleset2 = ListComboBox(self.rightRulesets)
        cbLayout.addWidget(self.cbRuleset2)
        cmdLayout = QHBoxLayout()
        cmdLayout.addWidget(self.buttonBox)
        layout = QVBoxLayout()
        layout.addLayout(cbLayout)
        layout.addWidget(self.view)
        layout.addLayout(cmdLayout)
        self.setLayout(layout)

Antoni Bella Pérez's avatar
Antoni Bella Pérez committed
132
        decorateWindow(self, i18nc("@title:window", "Compare"))
133
        self.setObjectName('RulesetDiffer')
Wolfgang Rohdewald's avatar
Wolfgang Rohdewald committed
134

135
136
        self.cbRuleset1.currentIndexChanged.connect(self.leftRulesetChanged)
        self.cbRuleset2.currentIndexChanged.connect(self.rulesetChanged)
137
        self.leftRulesetChanged()
138
        StateSaver(self)
139

140
    def leftRulesetChanged(self):
Wolfgang Rohdewald's avatar
Wolfgang Rohdewald committed
141
        """slot to be called if the left ruleset changes"""
142
143
144
        if len(self.leftRulesets) == 1:
            self.orderRight()
        self.rulesetChanged()
Wolfgang Rohdewald's avatar
Wolfgang Rohdewald committed
145

146
    def rulesetChanged(self):
Wolfgang Rohdewald's avatar
Wolfgang Rohdewald committed
147
        """slot to be called if the right ruleset changes"""
148
        self.model = DifferModel(self.formattedDiffs(), self)
Wolfgang Rohdewald's avatar
Wolfgang Rohdewald committed
149
150
        if Debug.modelTest:
            self.modelTest = ModelTest(self.model, self)
151
        self.view.setModel(self.model)
Wolfgang Rohdewald's avatar
Wolfgang Rohdewald committed
152

153
    def orderRight(self):
Wolfgang Rohdewald's avatar
Wolfgang Rohdewald committed
154
        """order the right rulesets by similarity to current left ruleset.
155
        Similarity is defined by the length of the diff list."""
156
        leftRuleset = self.cbRuleset1.current
157
        diffPairs = sorted([(len(x.diff(leftRuleset)), x)
158
                            for x in self.rightRulesets])
159
        combo = self.cbRuleset2
160
        with BlockSignals(combo):
161
            combo.items = [x[1] for x in diffPairs]
162
        combo.setCurrentIndex(0)
Wolfgang Rohdewald's avatar
Wolfgang Rohdewald committed
163

164
165
166
167
168
    def formattedDiffs(self):
        """a list of tuples with 3 values: name, left, right"""
        formatted = []
        leftRuleset = self.cbRuleset1.current
        rightRuleset = self.cbRuleset2.current
169
        assert rightRuleset, self.cbRuleset2.count()
170
171
        leftRuleset.load()
        rightRuleset.load()
172
        for rule1, rule2 in leftRuleset.diff(rightRuleset):
Wolfgang Rohdewald's avatar
Wolfgang Rohdewald committed
173
174
            name = i18n(rule1.name if rule1 else rule2.name)
            left = rule1.i18nStr() if rule1 else i18nc(
175
                'Kajongg-Rule', 'not defined')
Wolfgang Rohdewald's avatar
Wolfgang Rohdewald committed
176
            right = rule2.i18nStr() if rule2 else i18nc(
177
                'Kajongg-Rule', 'not defined')
178
179
            formatted.append((name, left, right))
            if rule1 and rule2 and rule1.definition != rule2.definition:
180
                formatted.append(('', rule1.definition, rule2.definition))
181
        return formatted