7 Module implementing the History dialog. |
7 Module implementing the History dialog. |
8 """ |
8 """ |
9 |
9 |
10 from __future__ import unicode_literals |
10 from __future__ import unicode_literals |
11 |
11 |
12 from PyQt5.QtCore import Qt, pyqtSlot |
12 from PyQt5.QtCore import pyqtSlot, Qt, QItemSelectionModel |
13 from PyQt5.QtWidgets import QDialogButtonBox, QListWidgetItem, QApplication |
13 from PyQt5.QtGui import QBrush, QColor, QTextCursor |
|
14 from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QListWidgetItem, \ |
|
15 QAbstractButton |
14 |
16 |
15 from E5Gui.E5Application import e5App |
17 from E5Gui.E5Application import e5App |
16 from E5Gui import E5MessageBox |
18 from E5Gui import E5MessageBox |
17 |
19 |
18 from PreviewDialogBase import PreviewDialogBase |
20 from Ui_HistoryDialog import Ui_HistoryDialog |
19 |
21 |
|
22 import Globals |
20 import Utilities |
23 import Utilities |
21 |
24 |
22 |
25 |
23 class HistoryDialog(PreviewDialogBase): |
26 class HistoryDialog(QDialog, Ui_HistoryDialog): |
24 """ |
27 """ |
25 Class implementing the History dialog. |
28 Class implementing the History dialog. |
26 """ |
29 """ |
27 ChangeIDRole = Qt.UserRole |
30 ChangeIDRole = Qt.UserRole |
28 |
31 |
29 def __init__(self, refactoring, changes, isUndo, parent=None): |
32 def __init__(self, refactoring, filename="", parent=None): |
30 """ |
33 """ |
31 Constructor |
34 Constructor |
32 |
35 |
33 @param refactoring reference to the main refactoring object |
36 @param refactoring reference to the main refactoring object |
34 (Refactoring) |
37 @type Refactoring |
35 @param changes list of ChangeSet objects |
38 @param filename name of the file to show the history for |
36 (list of rope.base.change.ChangeSet) |
39 @type str |
37 @param isUndo flag indicating an undo history dialog (boolean) |
40 @param parent reference to the parent widget |
38 @param parent reference to the parent widget (QWidget) |
41 @type QWidget |
39 """ |
42 """ |
40 PreviewDialogBase.__init__(self, parent) |
43 QDialog.__init__(self, parent) |
|
44 self.setupUi(self) |
|
45 self.setWindowFlags(Qt.Window) |
|
46 |
|
47 if Globals.isWindowsPlatform(): |
|
48 self.previewEdit.setFontFamily("Lucida Console") |
|
49 else: |
|
50 self.previewEdit.setFontFamily("Monospace") |
|
51 |
|
52 self.formats = {} |
|
53 self.formats[' '] = self.previewEdit.currentCharFormat() |
|
54 charFormat = self.previewEdit.currentCharFormat() |
|
55 charFormat.setBackground(QBrush(QColor(190, 237, 190))) |
|
56 self.formats['+'] = charFormat |
|
57 charFormat = self.previewEdit.currentCharFormat() |
|
58 charFormat.setBackground(QBrush(QColor(237, 190, 190))) |
|
59 self.formats['-'] = charFormat |
|
60 charFormat = self.previewEdit.currentCharFormat() |
|
61 charFormat.setBackground(QBrush(QColor(190, 190, 237))) |
|
62 self.formats['@'] = charFormat |
|
63 charFormat = self.previewEdit.currentCharFormat() |
|
64 charFormat.setBackground(QBrush(QColor(124, 124, 124))) |
|
65 self.formats['?'] = charFormat |
|
66 charFormat = self.previewEdit.currentCharFormat() |
|
67 charFormat.setBackground(QBrush(QColor(190, 190, 190))) |
|
68 self.formats['='] = charFormat |
41 |
69 |
42 self.__refactoring = refactoring |
70 self.__refactoring = refactoring |
43 self.__isUndo = isUndo |
71 self.__filename = filename |
44 |
72 |
45 if self.__isUndo: |
73 if not filename: |
46 self.__actionButton = self.buttonBox.addButton( |
74 self.header.setText(self.tr("<b>Project History</b>")) |
47 self.tr("&Undo"), |
|
48 QDialogButtonBox.AcceptRole) |
|
49 self.description.setText(self.tr("Undoable Changes")) |
|
50 title = self.tr("Undo History") |
|
51 else: |
75 else: |
52 self.__actionButton = self.buttonBox.addButton( |
76 self.header.setText(self.tr("<b>File History: {0}</b>").format( |
53 self.tr("&Redo"), |
77 filename)) |
54 QDialogButtonBox.AcceptRole) |
78 |
55 self.description.setText(self.tr("Redoable Changes")) |
79 self.__undoButton = self.buttonBox.addButton( |
56 title = self.tr("Redo History") |
80 self.tr("&Undo"), QDialogButtonBox.ActionRole) |
57 self.buttonBox.addButton(QDialogButtonBox.Close) |
81 self.__redoButton = self.buttonBox.addButton( |
58 self.setWindowTitle(title) |
82 self.tr("&Redo"), QDialogButtonBox.ActionRole) |
|
83 self.__refreshButton = self.buttonBox.addButton( |
|
84 self.tr("Re&fresh"), QDialogButtonBox.ActionRole) |
|
85 self.__clearButton = self.buttonBox.addButton( |
|
86 self.tr("&Clear History"), QDialogButtonBox.ActionRole) |
59 |
87 |
60 # populate the list |
88 # populate the list |
61 self.__changes = {} |
89 self.__refreshHistories() |
62 for change in changes: |
90 |
63 self.__changes[id(change)] = change |
91 def __appendText(self, txt, charFormat): |
64 itm = QListWidgetItem(str(change), self.changesList) |
92 """ |
65 itm.setData(HistoryDialog.ChangeIDRole, id(change)) |
93 Private method to append text to the end of the preview pane. |
66 if self.changesList.count() > 0: |
94 |
67 self.changesList.item(0).setSelected(True) |
95 @param txt text to insert |
68 |
96 @type str |
69 @pyqtSlot(QListWidgetItem, QListWidgetItem) |
97 @param charFormat text format to be used |
70 def on_changesList_currentItemChanged(self, current, previous): |
98 @type QTextCharFormat |
71 """ |
99 """ |
72 Private slot called when a change is selected. |
100 tc = self.previewEdit.textCursor() |
73 |
101 tc.movePosition(QTextCursor.End) |
74 @param current reference to the new current item (QListWidgetItem) |
102 self.previewEdit.setTextCursor(tc) |
75 @param previous reference to the old current item (QListWidgetItem) |
103 self.previewEdit.setCurrentCharFormat(charFormat) |
|
104 self.previewEdit.insertPlainText(txt) |
|
105 |
|
106 @pyqtSlot(QAbstractButton) |
|
107 def on_buttonBox_clicked(self, button): |
|
108 """ |
|
109 Private slot handling the selection of a dialog button. |
|
110 |
|
111 @param button reference to the button clicked |
|
112 @type QAbstractButton |
|
113 """ |
|
114 if button == QDialogButtonBox.Close: |
|
115 self.close() |
|
116 elif button == self.__undoButton: |
|
117 self.__undoChanges() |
|
118 elif button == self.__redoButton: |
|
119 self.__redoChanges() |
|
120 elif button == self.__refreshButton: |
|
121 self.__refreshHistories() |
|
122 elif button == self.__clearButton: |
|
123 self.__clearHistory() |
|
124 |
|
125 def __currentItemChanged(self, current): |
|
126 """ |
|
127 Private method to request change data of an item. |
|
128 |
|
129 @param current reference to the item to get change data for |
|
130 @type QListWidgetItem |
76 """ |
131 """ |
77 if current is None: |
132 if current is None: |
78 return |
133 return |
79 |
134 |
80 self.previewEdit.clear() |
135 self.previewEdit.clear() |
81 changeId = current.data(HistoryDialog.ChangeIDRole) |
136 changeId = current.data(HistoryDialog.ChangeIDRole) |
82 change = self.__changes[changeId] |
137 self.__refactoring.sendJson("History", { |
83 for line in change.get_description().splitlines(True): |
138 "Subcommand": "GetChange", |
84 try: |
139 "Id": changeId, |
85 charFormat = self.formats[line[0]] |
140 }) |
86 except (IndexError, KeyError): |
141 |
87 charFormat = self.formats[' '] |
142 @pyqtSlot(QListWidgetItem, QListWidgetItem) |
88 self._appendText(line, charFormat) |
143 def on_redoChangesList_currentItemChanged(self, current, previous): |
89 |
144 """ |
90 def accept(self): |
145 Private slot handling a change of the current redo change. |
91 """ |
146 |
92 Public slot to undo the selected set of changes. |
147 @param current reference to the new current redo item |
93 """ |
148 @type QListWidgetItem |
94 changeId = self.changesList.currentItem()\ |
149 @param previous reference to the previous current redo item |
95 .data(HistoryDialog.ChangeIDRole) |
150 @type QListWidgetItem |
96 change = self.__changes[changeId] |
151 """ |
97 |
152 self.__redoButton.setEnabled(current is not None) |
98 if self.__isUndo: |
153 self.__currentItemChanged(current) |
99 res = E5MessageBox.yesNo( |
154 |
100 None, |
155 @pyqtSlot(QListWidgetItem) |
101 self.tr("Undo refactorings"), |
156 def on_redoChangesList_itemClicked(self, item): |
102 self.tr("""Shall all refactorings up to <b>{0}</b>""" |
157 """ |
103 """ be undone?""") |
158 Private slot handling a click on a redo entry. |
104 .format(Utilities.html_encode(str(change)))) |
159 |
105 else: |
160 @param item reference to the clicked item |
106 res = E5MessageBox.yesNo( |
161 @type QListWidgetItem |
107 None, |
162 """ |
108 self.tr("Redo refactorings"), |
163 self.__currentItemChanged(item) |
109 self.tr("""Shall all refactorings up to <b>{0}</b>""" |
164 |
110 """ be redone?""") |
165 @pyqtSlot(QListWidgetItem, QListWidgetItem) |
111 .format(Utilities.html_encode(str(change)))) |
166 def on_undoChangesList_currentItemChanged(self, current, previous): |
|
167 """ |
|
168 Private slot handling a change of the current undo change. |
|
169 |
|
170 @param current reference to the new current undo item |
|
171 @type QListWidgetItem |
|
172 @param previous reference to the previous current undo item |
|
173 @type QListWidgetItem |
|
174 """ |
|
175 self.__undoButton.setEnabled(current is not None) |
|
176 self.__currentItemChanged(current) |
|
177 |
|
178 @pyqtSlot(QListWidgetItem) |
|
179 def on_undoChangesList_itemClicked(self, item): |
|
180 """ |
|
181 Private slot handling a click on an undo entry. |
|
182 |
|
183 @param item reference to the clicked item |
|
184 @type QListWidgetItem |
|
185 """ |
|
186 self.__currentItemChanged(item) |
|
187 |
|
188 def __undoChanges(self): |
|
189 """ |
|
190 Private method to undo the selected set of changes. |
|
191 """ |
|
192 currentUndoItem = self.undoChangesList.currentItem() |
|
193 change = currentUndoItem.text() |
|
194 changeId = currentUndoItem.data(HistoryDialog.ChangeIDRole) |
|
195 res = E5MessageBox.yesNo( |
|
196 None, |
|
197 self.tr("Undo refactorings"), |
|
198 self.tr("""Shall all refactorings up to <b>{0}</b>""" |
|
199 """ be undone?""") |
|
200 .format(Utilities.html_encode(change))) |
112 if res: |
201 if res: |
113 if not self.__refactoring.confirmAllBuffersSaved(): |
202 if not self.__refactoring.confirmAllBuffersSaved(): |
114 return |
203 return |
115 |
204 |
116 from ProgressHandle import ProgressHandle |
205 self.__refactoring.sendJson("History", { |
117 handle = ProgressHandle(change.description, False, self) |
206 "Subcommand": "Undo", |
118 handle.show() |
207 "Id": changeId, |
119 QApplication.processEvents() |
208 }) |
120 if self.__isUndo: |
209 |
121 self.__refactoring.getProject().history.undo( |
210 def __redoChanges(self): |
122 change, task_handle=handle) |
211 """ |
123 else: |
212 Private method to redo the selected set of changes. |
124 self.__refactoring.getProject().history.redo( |
213 """ |
125 change, task_handle=handle) |
214 currentRedoItem = self.redoChangesList.currentItem() |
126 handle.reset() |
215 change = currentRedoItem.text() |
127 |
216 changeId = currentRedoItem.data(HistoryDialog.ChangeIDRole) |
128 self.__refactoring.refreshEditors(change) |
217 res = E5MessageBox.yesNo( |
|
218 None, |
|
219 self.tr("Redo refactorings"), |
|
220 self.tr("""Shall all refactorings up to <b>{0}</b>""" |
|
221 """ be redone?""") |
|
222 .format(Utilities.html_encode(change))) |
|
223 if res: |
|
224 if not self.__refactoring.confirmAllBuffersSaved(): |
|
225 return |
|
226 |
|
227 self.__refactoring.sendJson("History", { |
|
228 "Subcommand": "Redo", |
|
229 "Id": changeId, |
|
230 }) |
|
231 |
|
232 def __refreshHistories(self): |
|
233 """ |
|
234 Private method to refresh the undo and redo history lists. |
|
235 """ |
|
236 self.__undoButton.setEnabled(False) |
|
237 self.__redoButton.setEnabled(False) |
|
238 self.__refreshButton.setEnabled(False) |
|
239 self.__clearButton.setEnabled(False) |
|
240 |
|
241 self.undoChangesList.clear() |
|
242 self.redoChangesList.clear() |
|
243 self.previewEdit.clear() |
|
244 |
|
245 self.__refactoring.sendJson("History", { |
|
246 "Subcommand": "Get", |
|
247 "Filename": self.__filename |
|
248 }) |
|
249 |
|
250 def __clearHistory(self): |
|
251 """ |
|
252 Private method to clear the refactoring history. |
|
253 """ |
|
254 res = E5MessageBox.yesNo( |
|
255 None, |
|
256 self.tr("Clear History"), |
|
257 self.tr("Do you really want to clear the refactoring history?")) |
|
258 if res: |
|
259 self.sendJson("History", { |
|
260 "Subcommand": "Clear", |
|
261 }) |
|
262 |
|
263 self.historyCleared() |
|
264 |
|
265 def historyCleared(self): |
|
266 """ |
|
267 Public method to indicate, that the refactoring history was cleared |
|
268 through the menu. |
|
269 """ |
|
270 self.__refreshHistories() |
|
271 |
|
272 def processHistoryCommand(self, data): |
|
273 """ |
|
274 Public method to process the data sent by the refactoring client. |
|
275 |
|
276 @param data dictionary containing the history data |
|
277 @type dict |
|
278 """ |
|
279 subcommand = data["Subcommand"] |
|
280 if subcommand == "Histories": |
|
281 for change, changeId in data["Undo"]: |
|
282 itm = QListWidgetItem(change, self.undoChangesList) |
|
283 itm.setData(HistoryDialog.ChangeIDRole, changeId) |
|
284 for change, changeId in data["Redo"]: |
|
285 itm = QListWidgetItem(change, self.redoChangesList) |
|
286 itm.setData(HistoryDialog.ChangeIDRole, changeId) |
|
287 if self.undoChangesList.count() > 0: |
|
288 self.undoChangesList.setCurrentItem( |
|
289 self.undoChangesList.item(0), |
|
290 QItemSelectionModel.Select) |
|
291 elif self.redoChangesList.count() > 0: |
|
292 self.redoChangesList.setCurrentItem( |
|
293 self.redoChangesList.item(0), |
|
294 QItemSelectionModel.Select) |
|
295 |
|
296 self.__refreshButton.setEnabled(True) |
|
297 if self.undoChangesList.count() > 0 or \ |
|
298 self.redoChangesList.count() > 0: |
|
299 self.__clearButton.setEnabled(True) |
|
300 |
|
301 elif subcommand == "ChangeDescription": |
|
302 for line in data["Description"].splitlines(True): |
|
303 try: |
|
304 charFormat = self.formats[line[0]] |
|
305 except (IndexError, KeyError): |
|
306 charFormat = self.formats[' '] |
|
307 self.__appendText(line, charFormat) |
|
308 |
|
309 elif subcommand in ["Undo", "Redo"]: |
|
310 self.__refactoring.refreshEditors(data["ChangedFiles"]) |
129 p = e5App().getObject("Project") |
311 p = e5App().getObject("Project") |
130 if p.isDirty(): |
312 if p.isDirty(): |
131 p.saveProject() |
313 p.saveProject() |
132 |
314 |
133 PreviewDialogBase.accept(self) |
315 self.raise_() |
134 else: |
316 self.__refreshHistories() |
135 self.reject() |
|