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