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