File indexing completed on 2024-04-21 04:01:48
0001 # -*- coding: utf-8 -*- 0002 0003 """ 0004 Copyright (C) 2008-2016 Wolfgang Rohdewald <wolfgang@rohdewald.de> 0005 0006 SPDX-License-Identifier: GPL-2.0 0007 0008 """ 0009 0010 from qt import Qt, QAbstractTableModel, QModelIndex 0011 from qt import QLabel, QDialog, QHBoxLayout, QVBoxLayout, QDialogButtonBox 0012 0013 from mi18n import i18n, i18nc 0014 from statesaver import StateSaver 0015 from guiutil import ListComboBox, MJTableView, decorateWindow 0016 from guiutil import BlockSignals 0017 from common import Debug 0018 from modeltest import ModelTest 0019 0020 0021 class DifferModel(QAbstractTableModel): 0022 0023 """a model for our ruleset differ""" 0024 0025 def __init__(self, diffs, view): 0026 super().__init__() 0027 self.diffs = diffs 0028 self.view = view 0029 0030 def columnCount(self, unusedIndex=QModelIndex()): 0031 # pylint: disable=no-self-use 0032 """how many columns does this node have?""" 0033 return 3 # rule name, left values, right values 0034 0035 def rowCount(self, parent): 0036 """how many items?""" 0037 if parent.isValid(): 0038 # we have only top level items 0039 return 0 0040 return len(self.diffs) 0041 0042 def data(self, index, role=Qt.DisplayRole): 0043 """get from model""" 0044 if not index.isValid(): 0045 return None 0046 if not 0 <= index.row() < len(self.diffs): 0047 return None 0048 diff = self.diffs[index.row()] 0049 column = index.column() 0050 if role == Qt.DisplayRole: 0051 return diff[column] 0052 if role == Qt.TextAlignmentRole: 0053 return int(Qt.AlignLeft | Qt.AlignVCenter) 0054 return None 0055 0056 def headerData(self, section, orientation, role): 0057 """tell the view about the wanted headers""" 0058 if role == Qt.TextAlignmentRole: 0059 if orientation == Qt.Horizontal: 0060 return int(Qt.AlignLeft | Qt.AlignVCenter) 0061 if role != Qt.DisplayRole: 0062 return None 0063 if orientation == Qt.Horizontal: 0064 if section == 0: 0065 return i18nc('Kajongg', 'Rule') 0066 if section == 1: 0067 return i18n(self.view.cbRuleset1.current.name) 0068 if section == 2: 0069 return i18n(self.view.cbRuleset2.current.name) 0070 return None 0071 0072 0073 class RulesetDiffer(QDialog): 0074 0075 """Shows differences between rulesets""" 0076 0077 def __init__(self, leftRulesets, rightRulesets, parent=None): 0078 QDialog.__init__(self, parent) 0079 if not isinstance(leftRulesets, list): 0080 leftRulesets = list([leftRulesets]) 0081 if not isinstance(rightRulesets, list): 0082 rightRulesets = list([rightRulesets]) 0083 leftRulesets, rightRulesets = leftRulesets[:], rightRulesets[:] 0084 # remove rulesets from right which are also on the left side 0085 for left in leftRulesets: 0086 left.load() 0087 for right in rightRulesets: 0088 right.load() 0089 for left in leftRulesets: 0090 for right in rightRulesets[:]: 0091 if left == right and left.name == right.name: 0092 # rightRulesets.remove(right) this is wrong because it 0093 # removes the first ruleset with the same hash 0094 rightRulesets = [ 0095 x for x in rightRulesets if id(x) != id(right)] 0096 self.leftRulesets = leftRulesets 0097 self.rightRulesets = rightRulesets 0098 self.model = None 0099 self.modelTest = None 0100 self.view = MJTableView(self) 0101 self.buttonBox = QDialogButtonBox() 0102 self.buttonBox.setStandardButtons(QDialogButtonBox.Ok) 0103 self.buttonBox.accepted.connect(self.accept) 0104 self.buttonBox.rejected.connect(self.reject) 0105 0106 cbLayout = QHBoxLayout() 0107 self.cbRuleset1 = ListComboBox(self.leftRulesets) 0108 if len(self.leftRulesets) == 1: 0109 self.lblRuleset1 = QLabel(self.leftRulesets[0].name) 0110 cbLayout.addWidget(self.lblRuleset1) 0111 else: 0112 cbLayout.addWidget(self.cbRuleset1) 0113 self.cbRuleset2 = ListComboBox(self.rightRulesets) 0114 cbLayout.addWidget(self.cbRuleset2) 0115 cmdLayout = QHBoxLayout() 0116 cmdLayout.addWidget(self.buttonBox) 0117 layout = QVBoxLayout() 0118 layout.addLayout(cbLayout) 0119 layout.addWidget(self.view) 0120 layout.addLayout(cmdLayout) 0121 self.setLayout(layout) 0122 0123 decorateWindow(self, i18nc("@title:window", "Compare")) 0124 self.setObjectName('RulesetDiffer') 0125 0126 self.cbRuleset1.currentIndexChanged.connect(self.leftRulesetChanged) 0127 self.cbRuleset2.currentIndexChanged.connect(self.rulesetChanged) 0128 self.leftRulesetChanged() 0129 StateSaver(self) 0130 0131 def leftRulesetChanged(self): 0132 """slot to be called if the left ruleset changes""" 0133 if len(self.leftRulesets) == 1: 0134 self.orderRight() 0135 self.rulesetChanged() 0136 0137 def rulesetChanged(self): 0138 """slot to be called if the right ruleset changes""" 0139 self.model = DifferModel(self.formattedDiffs(), self) 0140 if Debug.modelTest: 0141 self.modelTest = ModelTest(self.model, self) 0142 self.view.setModel(self.model) 0143 0144 def orderRight(self): 0145 """order the right rulesets by similarity to current left ruleset. 0146 Similarity is defined by the length of the diff list.""" 0147 leftRuleset = self.cbRuleset1.current 0148 diffPairs = sorted((len(x.diff(leftRuleset)), x) 0149 for x in self.rightRulesets) 0150 combo = self.cbRuleset2 0151 with BlockSignals(combo): 0152 combo.items = [x[1] for x in diffPairs] 0153 combo.setCurrentIndex(0) 0154 0155 def formattedDiffs(self): 0156 """a list of tuples with 3 values: name, left, right""" 0157 formatted = [] 0158 leftRuleset = self.cbRuleset1.current 0159 rightRuleset = self.cbRuleset2.current 0160 assert rightRuleset, self.cbRuleset2.count() 0161 leftRuleset.load() 0162 rightRuleset.load() 0163 for rule1, rule2 in leftRuleset.diff(rightRuleset): 0164 name = i18n(rule1.name if rule1 else rule2.name) 0165 left = rule1.i18nStr() if rule1 else i18nc( 0166 'Kajongg-Rule', 'not defined') 0167 right = rule2.i18nStr() if rule2 else i18nc( 0168 'Kajongg-Rule', 'not defined') 0169 formatted.append((name, left, right)) 0170 if rule1 and rule2 and rule1.definition != rule2.definition: 0171 formatted.append(('', rule1.definition, rule2.definition)) 0172 return formatted