1# -*- coding: utf-8 -*-
2
3# Copyright (c) 2006 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
4#
5
6"""
7Module implementing the Watch expression model.
8"""
9
10import copy
11
12from PyQt5.QtCore import pyqtSignal, Qt, QAbstractItemModel, QModelIndex
13
14
15class WatchPointModel(QAbstractItemModel):
16    """
17    Class implementing a custom model for watch expressions.
18
19    @signal dataAboutToBeChanged(QModelIndex, QModelIndex) emitted to indicate
20        a change of the data
21    """
22    dataAboutToBeChanged = pyqtSignal(QModelIndex, QModelIndex)
23
24    def __init__(self, parent=None):
25        """
26        Constructor
27
28        @param parent reference to the parent widget (QObject)
29        """
30        super().__init__(parent)
31
32        self.watchpoints = []
33        self.header = [
34            self.tr("Condition"),
35            self.tr("Special"),
36            self.tr('Temporary'),
37            self.tr('Enabled'),
38            self.tr('Ignore Count'),
39        ]
40        self.alignments = [Qt.Alignment(Qt.AlignmentFlag.AlignLeft),
41                           Qt.Alignment(Qt.AlignmentFlag.AlignLeft),
42                           Qt.Alignment(Qt.AlignmentFlag.AlignHCenter),
43                           Qt.Alignment(Qt.AlignmentFlag.AlignHCenter),
44                           Qt.Alignment(Qt.AlignmentFlag.AlignRight),
45                           ]
46
47    def columnCount(self, parent=None):
48        """
49        Public method to get the current column count.
50
51        @param parent index of the parent item (QModelIndex) (Unused)
52        @return column count (integer)
53        """
54        return len(self.header)
55
56    def rowCount(self, parent=None):
57        """
58        Public method to get the current row count.
59
60        @param parent index of the parent item (QModelIndex)
61        @return row count (integer)
62        """
63        # we do not have a tree, parent should always be invalid
64        if parent is None or not parent.isValid():
65            return len(self.watchpoints)
66        else:
67            return 0
68
69    def data(self, index, role):
70        """
71        Public method to get the requested data.
72
73        @param index index of the requested data (QModelIndex)
74        @param role role of the requested data (Qt.ItemDataRole)
75        @return the requested data
76        """
77        if not index.isValid():
78            return None
79
80        if (
81            role == Qt.ItemDataRole.DisplayRole and
82            index.column() in [0, 1, 4]
83        ):
84            return self.watchpoints[index.row()][index.column()]
85
86        if (
87            role == Qt.ItemDataRole.CheckStateRole and
88            index.column() in [2, 3]
89        ):
90            return self.watchpoints[index.row()][index.column()]
91
92        if (
93            role == Qt.ItemDataRole.ToolTipRole and
94            index.column() in [0, 1]
95        ):
96            return self.watchpoints[index.row()][index.column()]
97
98        if (
99            role == Qt.ItemDataRole.TextAlignmentRole and
100            index.column() < len(self.alignments)
101        ):
102            return self.alignments[index.column()]
103
104        return None
105
106    def flags(self, index):
107        """
108        Public method to get item flags.
109
110        @param index index of the requested flags (QModelIndex)
111        @return item flags for the given index (Qt.ItemFlags)
112        """
113        if not index.isValid():
114            return Qt.ItemFlag.ItemIsEnabled
115
116        return Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsSelectable
117
118    def headerData(self, section, orientation,
119                   role=Qt.ItemDataRole.DisplayRole):
120        """
121        Public method to get header data.
122
123        @param section section number of the requested header data (integer)
124        @param orientation orientation of the header (Qt.Orientation)
125        @param role role of the requested data (Qt.ItemDataRole)
126        @return header data
127        """
128        if (
129            orientation == Qt.Orientation.Horizontal and
130            role == Qt.ItemDataRole.DisplayRole
131        ):
132            if section >= len(self.header):
133                return ""
134            else:
135                return self.header[section]
136
137        return None
138
139    def index(self, row, column, parent=None):
140        """
141        Public method to create an index.
142
143        @param row row number for the index (integer)
144        @param column column number for the index (integer)
145        @param parent index of the parent item (QModelIndex)
146        @return requested index (QModelIndex)
147        """
148        if (
149            (parent and parent.isValid()) or
150            row < 0 or
151            row >= len(self.watchpoints) or
152            column < 0 or
153            column >= len(self.header)
154        ):
155            return QModelIndex()
156
157        return self.createIndex(row, column, self.watchpoints[row])
158
159    def parent(self, index):
160        """
161        Public method to get the parent index.
162
163        @param index index of item to get parent (QModelIndex)
164        @return index of parent (QModelIndex)
165        """
166        return QModelIndex()
167
168    def hasChildren(self, parent=None):
169        """
170        Public method to check for the presence of child items.
171
172        @param parent index of parent item (QModelIndex)
173        @return flag indicating the presence of child items (boolean)
174        """
175        if parent is None or not parent.isValid():
176            return len(self.watchpoints) > 0
177        else:
178            return False
179
180    ###########################################################################
181
182    def addWatchPoint(self, cond, special, properties):
183        """
184        Public method to add a new watch expression to the list.
185
186        @param cond expression of the watch expression
187        @type str
188        @param special special condition of the watch expression
189        @type str
190        @param properties properties of the watch expression
191            (tuple of temporary flag, enabled flag, ignore count)
192        @type tuple of (bool, bool, int)
193        """
194        wp = [cond, special] + list(properties)
195        cnt = len(self.watchpoints)
196        self.beginInsertRows(QModelIndex(), cnt, cnt)
197        self.watchpoints.append(wp)
198        self.endInsertRows()
199
200    def addWatchPoints(self, watchpoints):
201        """
202        Public method to add multiple watch expressions to the list.
203
204        @param watchpoints list of watch expressions with expression, special
205            condition, temporary flag, enabled flag and ignore count each
206        @type list of (str, str, bool, bool, int)
207        """
208        cnt = len(self.watchpoints)
209        self.beginInsertRows(QModelIndex(), cnt, cnt + len(watchpoints) - 1)
210        self.watchpoints += watchpoints
211        self.endInsertRows()
212
213    def setWatchPointByIndex(self, index, cond, special, properties):
214        """
215        Public method to set the values of a watch expression given by index.
216
217        @param index index of the watch expression (QModelIndex)
218        @param cond expression of the watch expression (string)
219        @param special special condition of the watch expression (string)
220        @param properties properties of the watch expression
221            (tuple of temporary flag (bool), enabled flag (bool),
222            ignore count (integer))
223        """
224        if index.isValid():
225            row = index.row()
226            index1 = self.createIndex(row, 0, self.watchpoints[row])
227            index2 = self.createIndex(
228                row, len(self.watchpoints[row]), self.watchpoints[row])
229            self.dataAboutToBeChanged.emit(index1, index2)
230            self.watchpoints[row] = [cond, special] + list(properties)
231            self.dataChanged.emit(index1, index2)
232
233    def setWatchPointEnabledByIndex(self, index, enabled):
234        """
235        Public method to set the enabled state of a watch expression given by
236        index.
237
238        @param index index of the watch expression (QModelIndex)
239        @param enabled flag giving the enabled state (boolean)
240        """
241        if index.isValid():
242            row = index.row()
243            col = 3
244            index1 = self.createIndex(row, col, self.watchpoints[row])
245            self.dataAboutToBeChanged.emit(index1, index1)
246            self.watchpoints[row][col] = enabled
247            self.dataChanged.emit(index1, index1)
248
249    def deleteWatchPointByIndex(self, index):
250        """
251        Public method to set the values of a watch expression given by index.
252
253        @param index index of the watch expression (QModelIndex)
254        """
255        if index.isValid():
256            row = index.row()
257            self.beginRemoveRows(QModelIndex(), row, row)
258            del self.watchpoints[row]
259            self.endRemoveRows()
260
261    def deleteWatchPoints(self, idxList):
262        """
263        Public method to delete a list of watch expressions given by their
264        indexes.
265
266        @param idxList list of watch expression indexes (list of QModelIndex)
267        """
268        rows = []
269        for index in idxList:
270            if index.isValid():
271                rows.append(index.row())
272        rows.sort(reverse=True)
273        for row in rows:
274            if row < len(self.breakpoints):
275                self.beginRemoveRows(QModelIndex(), row, row)
276                del self.watchpoints[row]
277                self.endRemoveRows()
278
279    def deleteAll(self):
280        """
281        Public method to delete all watch expressions.
282        """
283        if self.watchpoints:
284            self.beginRemoveRows(QModelIndex(), 0, len(self.watchpoints) - 1)
285            self.watchpoints = []
286            self.endRemoveRows()
287
288    def getWatchPointByIndex(self, index):
289        """
290        Public method to get the values of a watch expression given by index.
291
292        @param index index of the watch expression (QModelIndex)
293        @return watch expression (list of six values (expression,
294            special condition, temporary flag, enabled flag, ignore count))
295        @rtype tuple of (str, str, bool, bool, int)
296        """
297        if index.isValid():
298            return self.watchpoints[index.row()][:]  # return a copy
299        else:
300            return []
301
302    def getAllWatchpoints(self):
303        """
304        Public method to get the list of watchpoints.
305
306        @return list of watchpoints
307        @rtype list of list of [str, str, bool, bool, int]
308        """
309        return copy.deepcopy(self.watchpoints)
310
311    def getWatchPointIndex(self, cond, special=""):
312        """
313        Public method to get the index of a watch expression given by
314        expression.
315
316        @param cond expression of the watch expression (string)
317        @param special special condition of the watch expression (string)
318        @return index (QModelIndex)
319        """
320        for row in range(len(self.watchpoints)):
321            wp = self.watchpoints[row]
322            if wp[0] == cond:
323                if special and wp[1] != special:
324                    continue
325                return self.createIndex(row, 0, self.watchpoints[row])
326
327        return QModelIndex()
328