|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2016 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing the Undo stack for the hex edit widget. |
|
8 """ |
|
9 |
|
10 import enum |
|
11 |
|
12 from PyQt5.QtWidgets import QUndoStack, QUndoCommand |
|
13 |
|
14 |
|
15 class HexEditCommand(enum.Enum): |
|
16 """ |
|
17 Class implementing the edit commands. |
|
18 """ |
|
19 INSERT = 0 |
|
20 REMOVEAT = 1 |
|
21 OVERWRITE = 2 |
|
22 |
|
23 |
|
24 class HexEditUndoCommand(QUndoCommand): |
|
25 """ |
|
26 Class implementing the Undo command. |
|
27 """ |
|
28 def __init__(self, chunks, cmd, pos, newByte, parent=None): |
|
29 """ |
|
30 Constructor |
|
31 |
|
32 @param chunks reference to the data container |
|
33 @type HexEditChunks |
|
34 @param cmd edit command |
|
35 @type HexEditCommand |
|
36 @param pos edit position |
|
37 @type int |
|
38 @param newByte new byte value |
|
39 @type int (range 0 to 255) |
|
40 @param parent reference to the parent command |
|
41 @type QUndoCommand |
|
42 """ |
|
43 super().__init__(parent) |
|
44 |
|
45 self.__chunks = chunks |
|
46 self._pos = pos |
|
47 self._newByte = newByte |
|
48 self._cmd = cmd |
|
49 |
|
50 self.__wasChanged = False |
|
51 self.__oldByte = 0 |
|
52 |
|
53 def undo(self): |
|
54 """ |
|
55 Public method to undo the command. |
|
56 """ |
|
57 if self._cmd == HexEditCommand.INSERT: |
|
58 self.__chunks.removeAt(self._pos) |
|
59 elif self._cmd == HexEditCommand.OVERWRITE: |
|
60 self.__chunks.overwrite(self._pos, self.__oldByte) |
|
61 self.__chunks.setDataChanged(self._pos, self.__wasChanged) |
|
62 elif self._cmd == HexEditCommand.REMOVEAT: |
|
63 self.__chunks.insert(self._pos, self.__oldByte) |
|
64 self.__chunks.setDataChanged(self._pos, self.__wasChanged) |
|
65 |
|
66 def redo(self): |
|
67 """ |
|
68 Public method to redo the command. |
|
69 """ |
|
70 if self._cmd == HexEditCommand.INSERT: |
|
71 self.__chunks.insert(self._pos, self._newByte) |
|
72 elif self._cmd == HexEditCommand.OVERWRITE: |
|
73 self.__oldByte = self.__chunks[self._pos] |
|
74 self.__wasChanged = self.__chunks.dataChanged(self._pos) |
|
75 self.__chunks.overwrite(self._pos, self._newByte) |
|
76 elif self._cmd == HexEditCommand.REMOVEAT: |
|
77 self.__oldByte = self.__chunks[self._pos] |
|
78 self.__wasChanged = self.__chunks.dataChanged(self._pos) |
|
79 self.__chunks.removeAt(self._pos) |
|
80 |
|
81 def mergeWith(self, command): |
|
82 """ |
|
83 Public method to merge this command with another one. |
|
84 |
|
85 @param command reference to the command to merge with |
|
86 @type QUndoCommand |
|
87 @return flag indicating a successful merge |
|
88 @rtype bool |
|
89 """ |
|
90 result = False |
|
91 |
|
92 if ( |
|
93 self._cmd != HexEditCommand.REMOVEAT and |
|
94 command._cmd == HexEditCommand.OVERWRITE and |
|
95 command._pos == self._pos |
|
96 ): |
|
97 self._newByte = command._newByte |
|
98 result = True |
|
99 |
|
100 return result |
|
101 |
|
102 def id(self): |
|
103 """ |
|
104 Public method to get the ID of this undo command class. |
|
105 |
|
106 @return ID of the undo command class |
|
107 @rtype int |
|
108 """ |
|
109 return 4242 |
|
110 |
|
111 |
|
112 class HexEditUndoStack(QUndoStack): |
|
113 """ |
|
114 Class implementing an Undo stack for the hex edit widget. |
|
115 """ |
|
116 def __init__(self, chunks, parent=None): |
|
117 """ |
|
118 Constructor |
|
119 |
|
120 @param chunks reference to the data container |
|
121 @type HexEditChunks |
|
122 @param parent reference to the parent object |
|
123 @type QObject |
|
124 """ |
|
125 super().__init__(parent) |
|
126 |
|
127 self.__chunks = chunks |
|
128 self.__parent = parent |
|
129 |
|
130 def insert(self, pos, data): |
|
131 """ |
|
132 Public method to insert a byte. |
|
133 |
|
134 @param pos position to insert at |
|
135 @type int |
|
136 @param data byte to be inserted |
|
137 @type int (range 0 to 255) |
|
138 """ |
|
139 if pos >= 0 and pos <= self.__chunks.size(): |
|
140 uc = HexEditUndoCommand( |
|
141 self.__chunks, HexEditCommand.INSERT, pos, data) |
|
142 self.push(uc) |
|
143 |
|
144 def insertByteArray(self, pos, byteArray): |
|
145 """ |
|
146 Public method to insert bytes. |
|
147 |
|
148 @param pos position to insert at |
|
149 @type int |
|
150 @param byteArray data to be inserted |
|
151 @type byteArray or QByteArray |
|
152 """ |
|
153 ba = bytearray(byteArray) |
|
154 |
|
155 if pos >= 0 and pos <= self.__chunks.size(): |
|
156 txt = self.tr("Inserting %n byte(s)", "", len(ba)) |
|
157 self.beginMacro(txt) |
|
158 for idx in range(len(ba)): |
|
159 uc = HexEditUndoCommand( |
|
160 self.__chunks, HexEditCommand.INSERT, pos + idx, ba[idx]) |
|
161 self.push(uc) |
|
162 self.endMacro() |
|
163 |
|
164 def removeAt(self, pos, length=1): |
|
165 """ |
|
166 Public method to remove bytes. |
|
167 |
|
168 @param pos position to remove bytes from |
|
169 @type int |
|
170 @param length amount of bytes to remove |
|
171 @type int |
|
172 """ |
|
173 if pos >= 0 and pos <= self.__chunks.size(): |
|
174 if length == 1: |
|
175 uc = HexEditUndoCommand( |
|
176 self.__chunks, HexEditCommand.REMOVEAT, pos, 0) |
|
177 self.push(uc) |
|
178 else: |
|
179 txt = self.tr("Deleting %n byte(s)", "", length) |
|
180 self.beginMacro(txt) |
|
181 for _cnt in range(length): |
|
182 uc = HexEditUndoCommand( |
|
183 self.__chunks, HexEditCommand.REMOVEAT, pos, 0) |
|
184 self.push(uc) |
|
185 self.endMacro() |
|
186 |
|
187 def overwrite(self, pos, data): |
|
188 """ |
|
189 Public method to replace a byte. |
|
190 |
|
191 @param pos position to replace the byte at |
|
192 @type int |
|
193 @param data byte to replace with |
|
194 @type int (range 0 to 255) |
|
195 """ |
|
196 if pos >= 0 and pos <= self.__chunks.size(): |
|
197 uc = HexEditUndoCommand( |
|
198 self.__chunks, HexEditCommand.OVERWRITE, pos, data) |
|
199 self.push(uc) |
|
200 |
|
201 def overwriteByteArray(self, pos, length, byteArray): |
|
202 """ |
|
203 Public method to replace bytes. |
|
204 |
|
205 @param pos position to replace the bytes at |
|
206 @type int |
|
207 @param length amount of bytes to replace |
|
208 @type int |
|
209 @param byteArray bytes to replace with |
|
210 @type bytearray or QByteArray |
|
211 """ |
|
212 ba = bytearray(byteArray) |
|
213 |
|
214 if pos >= 0 and pos <= self.__chunks.size(): |
|
215 txt = self.tr("Inserting %n byte(s)", "", len(ba)) |
|
216 self.beginMacro(txt) |
|
217 self.removeAt(pos, length) |
|
218 self.insertByteArray(pos, ba) |
|
219 self.endMacro() |