forked from geosim/QAD
-
Notifications
You must be signed in to change notification settings - Fork 13
/
qad_undoredo.py
393 lines (312 loc) · 14.8 KB
/
qad_undoredo.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
# -*- coding: utf-8 -*-
"""
/***************************************************************************
QAD Quantum Aided Design plugin
funzioni per undo e redo
-------------------
begin : 2014-04-24
copyright : iiiii
email : hhhhh
developers : bbbbb aaaaa ggggg
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
"""
# Import the PyQt and QGIS libraries
from qgis.PyQt.QtCore import *
from qgis.PyQt.QtGui import *
from qgis.core import *
from qgis.gui import *
#===============================================================================
# QadUndoRecordTypeEnum class.
#===============================================================================
class QadUndoRecordTypeEnum():
NONE = 0 # nessuno
COMMAND = 1 # singolo comando
BEGIN = 2 # inizio di un gruppo di comandi
END = 3 # fine di un gruppo di comandi
BOOKMARK = 4 # flag di segnalibro, significa che si tratta di un segno a cui
# si può ritornare
#===============================================================================
# QadUndoRecord classe x gestire un registrazione di UNDO
#===============================================================================
class QadUndoRecord():
def __init__(self):
self.text = "" # descrizione operazione
self.undoType = QadUndoRecordTypeEnum.NONE # tipo di undo (vedi QadUndoRecordTypeEnum)
self.layerList = None # lista di layer coinvolti nel comando di editazione
def setUndoType(self, text = "", undoType = QadUndoRecordTypeEnum.NONE):
# si sta impostando una tipologia di marcatore di undo
self.text = text
self.layerList = None # lista di layer coinvolti nel comando di editazione
self.undoType = undoType
def layerAt(self, layerId):
# ritorna la posizione nella lista 0-based), -1 se non trovato
if self.layerList is not None and self.undoType == QadUndoRecordTypeEnum.COMMAND:
for j in range(0, len(self.layerList), 1):
if self.layerList[j].id() == layerId:
return j
return -1
def clearByLayer(self, layerId):
# elimino dalla lista il layer <layerId>
pos = self.layerAt(layerId)
if pos >= 0:
del self.layerList[pos]
def beginEditCommand(self, text, layerList):
# si sta iniziando un comando che coinvolge una lista di layer
self.text = text # descrizione operazione
self.undoType = QadUndoRecordTypeEnum.COMMAND
# <parameter> contiene la lista dei layer coinvolti nel comando di editazione
self.layerList = []
for layer in layerList: # copio la lista
if self.layerAt(layer.id()) == -1: # non ammetto duplicazioni di layer
layer.beginEditCommand(text)
self.layerList.append(layer)
def destroyEditCommand(self):
# si sta distruggendo un comando che coinvolge una lista di layer
if self.layerList is not None and self.undoType == QadUndoRecordTypeEnum.COMMAND:
for layer in self.layerList:
layer.destroyEditCommand() # Destroy active command and reverts all changes in it
return True
else:
return False
def endEditCommand(self, canvas):
# si sta concludendo un comando che coinvolge una lista di layer
if self.layerList is not None and self.undoType == QadUndoRecordTypeEnum.COMMAND:
for layer in self.layerList:
layer.endEditCommand()
layer.triggerRepaint()
canvas.refresh()
def undoEditCommand(self, canvas = None):
# si sta facendo un UNDO di un comando che coinvolge una lista di layer
if self.layerList is not None and self.undoType == QadUndoRecordTypeEnum.COMMAND:
for layer in self.layerList:
layer.undoStack().undo()
if canvas is not None:
canvas.refresh()
def redoEditCommand(self, canvas = None):
# si sta facendo un REDO di un comando che coinvolge una lista di layer
if self.layerList is not None and self.undoType == QadUndoRecordTypeEnum.COMMAND:
for layer in self.layerList:
layer.undoStack().redo()
if canvas is not None:
canvas.refresh()
def addLayer(self, layer):
# si sta aggiungendo un layer al comando corrente
if self.undoType != QadUndoRecordTypeEnum.COMMAND: # si deve trattare di un comando
return False
if self.layerAt(layer.id()) == -1: # non ammetto duplicazioni di layer
layer.beginEditCommand(self.text)
self.layerList.append(layer)
#===============================================================================
# QadUndoStack classe x gestire lo stack delle operazioni
#===============================================================================
class QadUndoStack():
def __init__(self):
self.UndoRecordList = [] # lista di record di undo
self.index = -1
def clear(self):
del self.UndoRecordList[:] # svuoto la lista
self.index = -1
def clearByLayer(self, layerId):
# elimino il layer <layerId> dalla lista dei record di undo
for i in range(len(self.UndoRecordList) - 1, -1, -1):
UndoRecord = self.UndoRecordList[i]
if UndoRecord.undoType == QadUndoRecordTypeEnum.COMMAND:
UndoRecord.clearByLayer(layerId)
if len(UndoRecord.layerList) == 0:
# elimino la lista dei layer (vuota) coinvolta nel comando di editazione
del self.UndoRecordList[i]
if self.index >= i: # aggiorno il puntatore
self.index = self.index - 1
def insertBeginGroup(self, text):
UndoRecord = QadUndoRecord()
UndoRecord.setUndoType(text, QadUndoRecordTypeEnum.BEGIN)
self.UndoRecordList.append(UndoRecord)
self.index = len(self.UndoRecordList) - 1
return True
def getOpenGroupPos(self, endGroupPos):
# dalla posizione di fine gruppo <endgroupPos> cerca la posizione di inizio gruppo
# -1 se non trovato
openFlag = 0
for i in range(endGroupPos, -1, -1):
UndoRecord = self.UndoRecordList[i]
if UndoRecord.undoType == QadUndoRecordTypeEnum.BEGIN:
openFlag = openFlag + 1
if openFlag >= 0:
return i
elif UndoRecord.undoType == QadUndoRecordTypeEnum.END:
openFlag = openFlag - 1
return -1
def getEndGroupPos(self, beginGroupPos):
# dalla posizione di inizio gruppo <endgroupPos> cerca la posizione di inizio gruppo
# -1 se non trovato
closeFlag = 0
for i in range(beginGroupPos, len(self.UndoRecordList), 1):
UndoRecord = self.UndoRecordList[i]
if UndoRecord.undoType == QadUndoRecordTypeEnum.BEGIN:
closeFlag = closeFlag - 1
elif UndoRecord.undoType == QadUndoRecordTypeEnum.END:
closeFlag = closeFlag + 1
if closeFlag >= 0:
return i
return -1
def insertEndGroup(self):
# non si può inserire un end gruppo se non si é rimasto aperto un gruppo
openGroupPos = self.getOpenGroupPos(len(self.UndoRecordList) - 1)
if openGroupPos == -1:
return False
UndoRecord = QadUndoRecord()
UndoRecord.setUndoType(self.UndoRecordList[openGroupPos].text, QadUndoRecordTypeEnum.END)
self.UndoRecordList.append(UndoRecord)
self.index = len(self.UndoRecordList) - 1
return True
def beginEditCommand(self, text, layerList):
tot = len(self.UndoRecordList)
if tot > 0 and self.index < tot - 1:
del self.UndoRecordList[self.index + 1 :] # cancello fino alla fine
UndoRecord = QadUndoRecord()
UndoRecord.beginEditCommand(text, layerList)
self.UndoRecordList.append(UndoRecord)
self.index = len(self.UndoRecordList) - 1
def destroyEditCommand(self):
if len(self.UndoRecordList) > 0:
UndoRecord = self.UndoRecordList[-1]
if UndoRecord.destroyEditCommand():
del self.UndoRecordList[-1]
self.index = self.index - 1
def endEditCommand(self, canvas):
if len(self.UndoRecordList) > 0:
UndoRecord = self.UndoRecordList[-1]
UndoRecord.endEditCommand(canvas)
def moveOnFirstUndoRecord(self):
# sposta il cursore dalla posizione attuale fino l'inizio
# e si ferma quando trova un record di tipo END o COMMAND
while self.index >= 0:
UndoRecord = self.UndoRecordList[self.index]
if UndoRecord.undoType == QadUndoRecordTypeEnum.END or \
UndoRecord.undoType == QadUndoRecordTypeEnum.COMMAND:
return True
self.index = self.index - 1
return False
def undoEditCommand(self, canvas = None, nTimes = 1):
for i in range(0, nTimes, 1):
# cerco il primo record in cui ha senso fare UNDO
if self.moveOnFirstUndoRecord() == False:
break
UndoRecord = self.UndoRecordList[self.index]
# se incontro un end-group devo andare fino al begin-group
if UndoRecord.undoType == QadUndoRecordTypeEnum.END:
openGroupPos = self.getOpenGroupPos(self.index)
while self.index >= openGroupPos:
UndoRecord.undoEditCommand(None) # senza fare refresh
self.index = self.index - 1
if self.moveOnFirstUndoRecord() == False:
break
UndoRecord = self.UndoRecordList[self.index]
else:
UndoRecord.undoEditCommand(None)
self.index = self.index - 1
if canvas is not None:
canvas.refresh()
def moveOnFirstRedoRecord(self):
# sposta il cursore dalla posizione attuale fino alla fine
# e si ferma quando trova un record di tipo BEGIN o COMMAND
tot = len(self.UndoRecordList) - 1
while self.index < tot:
self.index = self.index + 1
UndoRecord = self.UndoRecordList[self.index]
if UndoRecord.undoType == QadUndoRecordTypeEnum.BEGIN or \
UndoRecord.undoType == QadUndoRecordTypeEnum.COMMAND:
return True
return False
def redoEditCommand(self, canvas = None, nTimes = 1):
for i in range(0, nTimes, 1):
# cerco il primo record in cui ha senso fare REDO
if self.moveOnFirstRedoRecord() == False:
break
UndoRecord = self.UndoRecordList[self.index]
# se incontro un begin-group devo andare fino al end-group
if UndoRecord.undoType == QadUndoRecordTypeEnum.BEGIN:
endGroupPos = self.getEndGroupPos(self.index)
while self.index <= endGroupPos:
UndoRecord.redoEditCommand(None) # senza refresh
if self.moveOnFirstRedoRecord() == False:
break
UndoRecord = self.UndoRecordList[self.index]
else:
UndoRecord.redoEditCommand(None)
if canvas is not None:
canvas.refresh()
def addLayerToLastEditCommand(self, text, layer):
if len(self.UndoRecordList) > 0:
self.UndoRecordList[-1].addLayer(layer)
def isUndoAble(self):
# cerca un record di tipo COMMAND dalla posizione attuale fino l'inizio
i = self.index
while i >= 0:
UndoRecord = self.UndoRecordList[i]
if UndoRecord.undoType == QadUndoRecordTypeEnum.COMMAND:
return True
i = i - 1
return False
def isRedoAble(self):
# cerca un record di tipo COMMAND dalla posizione attuale fino alla fine
i = self.index + 1
tot = len(self.UndoRecordList)
while i < tot:
UndoRecord = self.UndoRecordList[i]
if UndoRecord.undoType == QadUndoRecordTypeEnum.COMMAND:
return True
i = i + 1
return False
#===============================================================================
# BOOKMARK - INIZIO
#===============================================================================
def undoUntilBookmark(self, canvas):
if self.index == -1:
return
for i in range(self.index, -1, -1):
UndoRecord = self.UndoRecordList[i]
if UndoRecord.undoType == QadUndoRecordTypeEnum.BOOKMARK:
break
UndoRecord.undoEditCommand(None) # senza refresh
self.index = i - 1
canvas.refresh()
def redoUntilBookmark(self, canvas):
for i in range(self.index + 1, len(self.UndoRecordList), 1):
UndoRecord = self.UndoRecordList[i]
if UndoRecord.undoType == QadUndoRecordTypeEnum.BOOKMARK:
break
UndoRecord.redoEditCommand(None) # senza refresh
self.index = i
canvas.refresh()
def getPrevBookmarkPos(self, pos):
# dalla posizione <pos> cerca la posizione di bookmark precedente
# -1 se non trovato
for i in range(pos - 1, -1, -1):
UndoRecord = self.UndoRecordList[i]
if UndoRecord.undoType == QadUndoRecordTypeEnum.BOOKMARK:
return i
return -1
def insertBookmark(self, text):
# non si può inserire un bookmark all'interno di un gruppo begin-end
if self.getOpenGroupPos(self.index) >= 0:
return False
tot = len(self.UndoRecordList)
if tot > 0 and self.index < tot - 1:
del self.UndoRecordList[self.index + 1 :] # cancello fino alla fine
UndoRecord = QadUndoRecord()
UndoRecord.setUndoType(text, QadUndoRecordTypeEnum.BOOKMARK)
self.UndoRecordList.append(UndoRecord)
self.index = len(self.UndoRecordList) - 1
return True
#===============================================================================
# BOOKMARK - FINE
#===============================================================================