|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing a dialog to edit the history modification plan. |
|
8 """ |
|
9 |
|
10 from PyQt5.QtCore import pyqtSlot, QCoreApplication |
|
11 from PyQt5.QtWidgets import QDialog, QTreeWidgetItem, QComboBox |
|
12 |
|
13 from E5Gui import E5MessageBox |
|
14 |
|
15 from Ui_HgHisteditPlanEditor import Ui_HgHisteditPlanEditor |
|
16 |
|
17 import UI.PixmapCache |
|
18 |
|
19 |
|
20 class HgHisteditPlanActionComboBox(QComboBox): |
|
21 """ |
|
22 Class implementing a combo box to select the action in the plan tree. |
|
23 """ |
|
24 def __init__(self, item, column): |
|
25 """ |
|
26 Constructor |
|
27 |
|
28 @param item reference to the item |
|
29 @type QTreeWidgetItem |
|
30 @param column column number inside the tree widget item |
|
31 @type int |
|
32 """ |
|
33 super(HgHisteditPlanActionComboBox, self).__init__() |
|
34 |
|
35 self.__item = item |
|
36 self.__column = column |
|
37 |
|
38 self.addItems(["pick", "drop", "mess", "fold", "roll", "edit"]) |
|
39 txt = self.__item.text(self.__column) |
|
40 index = self.findText(txt) |
|
41 if index > -1: |
|
42 self.setCurrentIndex(index) |
|
43 |
|
44 self.currentIndexChanged.connect(self.__changeItem) |
|
45 |
|
46 @pyqtSlot(int) |
|
47 def __changeItem(self, index): |
|
48 """ |
|
49 Private slot to handle the selection of a plan action. |
|
50 |
|
51 This method sets the text of the associated item for the specified |
|
52 cell in order to be able to retrieve it with a call of the text() |
|
53 method of the item. |
|
54 |
|
55 @param index index of the selected action |
|
56 @type int |
|
57 """ |
|
58 self.__item.setText(self.__column, self.currentText()) |
|
59 self.__item.treeWidget().setCurrentItem(self.__item) |
|
60 |
|
61 def showPopup(self): |
|
62 """ |
|
63 Public method to show the list of items of the combo box. |
|
64 |
|
65 This is reimplemented in order to set the associated item as the |
|
66 current item of the tree widget. |
|
67 """ |
|
68 self.__item.treeWidget().setCurrentItem(self.__item) |
|
69 super(HgHisteditPlanActionComboBox, self).showPopup() |
|
70 |
|
71 |
|
72 class HgHisteditPlanEditor(QDialog, Ui_HgHisteditPlanEditor): |
|
73 """ |
|
74 Class implementing a dialog to edit the history modification plan. |
|
75 """ |
|
76 def __init__(self, fileName, parent=None): |
|
77 """ |
|
78 Constructor |
|
79 |
|
80 @param fileName name of the file containing the history edit plan |
|
81 to be edited |
|
82 @type str |
|
83 @param parent reference to the parent widget |
|
84 @type QWidget |
|
85 """ |
|
86 super(HgHisteditPlanEditor, self).__init__(parent) |
|
87 self.setupUi(self) |
|
88 |
|
89 self.upButton.setIcon(UI.PixmapCache.getIcon("1uparrow.png")) |
|
90 self.downButton.setIcon(UI.PixmapCache.getIcon("1downarrow.png")) |
|
91 |
|
92 self.planTreeWidget.headerItem().setText( |
|
93 self.planTreeWidget.columnCount(), "") |
|
94 |
|
95 self.__fileName = fileName |
|
96 self.__readFile() |
|
97 |
|
98 self.__updateButtons() |
|
99 |
|
100 def __readFile(self): |
|
101 """ |
|
102 Private method to read the file containing the edit plan and |
|
103 populate the dialog. |
|
104 """ |
|
105 try: |
|
106 f = open(self.__fileName, "r") |
|
107 txt = f.read() |
|
108 f.close() |
|
109 except (IOError, OSError) as err: |
|
110 E5MessageBox.critical( |
|
111 self, |
|
112 self.tr("Edit Plan"), |
|
113 self.tr("""<p>The file <b>{0}</b> could not be read.</p>""" |
|
114 """<p>Reason: {1}</p>""").format( |
|
115 self.__fileName, str(err))) |
|
116 self.on_buttonBox_rejected() |
|
117 return |
|
118 |
|
119 infoLines = [] |
|
120 for line in txt.splitlines(): |
|
121 if line.startswith("#"): |
|
122 infoLines.append(line[1:].lstrip()) |
|
123 else: |
|
124 self.__createPlanItem(line) |
|
125 self.infoEdit.setPlainText("\n".join(infoLines)) |
|
126 |
|
127 self.__resizeSections() |
|
128 |
|
129 def __addActionCombo(self, item): |
|
130 """ |
|
131 Private method to add an edit action combo to an item. |
|
132 |
|
133 @param item reference to the tree widget item |
|
134 @type QTreeWidgetItem |
|
135 """ |
|
136 actionCombo = HgHisteditPlanActionComboBox(item, 0) |
|
137 self.planTreeWidget.setItemWidget(item, 0, actionCombo) |
|
138 item.setSizeHint(0, actionCombo.sizeHint()) |
|
139 |
|
140 def __createPlanItem(self, text): |
|
141 """ |
|
142 Private method to create an edit plan tree item. |
|
143 """ |
|
144 if not text.lstrip(): |
|
145 return |
|
146 |
|
147 parts = text.split(" ", 3) |
|
148 action = parts[0] |
|
149 try: |
|
150 rev = int(parts[2]) |
|
151 if len(parts) > 3: |
|
152 summary = parts[3] |
|
153 else: |
|
154 summary = "" |
|
155 except ValueError: |
|
156 rev = -1 |
|
157 summary = " ".join(parts[2:]) |
|
158 if rev > -1: |
|
159 revision = "{0:>7}:{1}".format(rev, parts[1]) |
|
160 else: |
|
161 revision = parts[1] |
|
162 |
|
163 itm = QTreeWidgetItem(self.planTreeWidget, [ |
|
164 action, |
|
165 revision, |
|
166 summary, |
|
167 ]) |
|
168 self.__addActionCombo(itm) |
|
169 |
|
170 def __resizeSections(self): |
|
171 """ |
|
172 Private method to resize the tree widget sections. |
|
173 """ |
|
174 for column in range(self.planTreeWidget.columnCount()): |
|
175 self.planTreeWidget.resizeColumnToContents(column) |
|
176 self.planTreeWidget.header().setStretchLastSection(True) |
|
177 |
|
178 def __updateButtons(self): |
|
179 """ |
|
180 Private method to set the enabled state of the up and down buttons. |
|
181 """ |
|
182 if self.planTreeWidget.currentItem() is None: |
|
183 self.upButton.setEnabled(False) |
|
184 self.downButton.setEnabled(False) |
|
185 else: |
|
186 row = self.planTreeWidget.indexOfTopLevelItem( |
|
187 self.planTreeWidget.currentItem()) |
|
188 self.upButton.setEnabled(row > 0) |
|
189 self.downButton.setEnabled( |
|
190 row < self.planTreeWidget.topLevelItemCount() - 1) |
|
191 |
|
192 @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem) |
|
193 def on_planTreeWidget_currentItemChanged(self, current, previous): |
|
194 """ |
|
195 Private slot handling the change of the current edit plan item. |
|
196 |
|
197 @param current reference to the current edit plan item |
|
198 @type QTreeWidgetItem |
|
199 @param previous reference to the previous current edit plan item |
|
200 @type QTreeWidgetItem |
|
201 """ |
|
202 self.__updateButtons() |
|
203 |
|
204 @pyqtSlot() |
|
205 def on_upButton_clicked(self): |
|
206 """ |
|
207 Private slot to move the current entry up one line. |
|
208 """ |
|
209 row = self.planTreeWidget.indexOfTopLevelItem( |
|
210 self.planTreeWidget.currentItem()) |
|
211 if row > 0: |
|
212 targetRow = row - 1 |
|
213 itm = self.planTreeWidget.takeTopLevelItem(row) |
|
214 self.planTreeWidget.insertTopLevelItem(targetRow, itm) |
|
215 self.__addActionCombo(itm) |
|
216 self.planTreeWidget.setCurrentItem(itm) |
|
217 |
|
218 @pyqtSlot() |
|
219 def on_downButton_clicked(self): |
|
220 """ |
|
221 Private slot to move the current entry down one line. |
|
222 """ |
|
223 row = self.planTreeWidget.indexOfTopLevelItem( |
|
224 self.planTreeWidget.currentItem()) |
|
225 if row < self.planTreeWidget.topLevelItemCount() - 1: |
|
226 targetRow = row + 1 |
|
227 itm = self.planTreeWidget.takeTopLevelItem(row) |
|
228 self.planTreeWidget.insertTopLevelItem(targetRow, itm) |
|
229 self.__addActionCombo(itm) |
|
230 self.planTreeWidget.setCurrentItem(itm) |
|
231 |
|
232 @pyqtSlot() |
|
233 def on_buttonBox_accepted(self): |
|
234 """ |
|
235 Private slot called by the buttonBox accepted signal. |
|
236 """ |
|
237 text = self.__assembleEditPlan() |
|
238 try: |
|
239 f = open(self.__fileName, "w") |
|
240 f.write(text) |
|
241 f.close() |
|
242 except (IOError, OSError) as err: |
|
243 E5MessageBox.critical( |
|
244 self, |
|
245 self.tr("Edit Plan"), |
|
246 self.tr("""<p>The file <b>{0}</b> could not be read.</p>""" |
|
247 """<p>Reason: {1}</p>""").format( |
|
248 self.__fileName, str(err))) |
|
249 self.on_buttonBox_rejected() |
|
250 return |
|
251 |
|
252 self.close() |
|
253 QCoreApplication.exit(0) |
|
254 |
|
255 @pyqtSlot() |
|
256 def on_buttonBox_rejected(self): |
|
257 """ |
|
258 Private slot called by the buttonBox rejected signal. |
|
259 """ |
|
260 self.close() |
|
261 QCoreApplication.exit(1) |
|
262 |
|
263 def __assembleEditPlan(self): |
|
264 """ |
|
265 Private method to assemble the edit plan into text suitable for the |
|
266 histedit file. |
|
267 |
|
268 @return assembled edit plan text |
|
269 @rtype str |
|
270 """ |
|
271 lines = [] |
|
272 for row in range(self.planTreeWidget.topLevelItemCount()): |
|
273 itm = self.planTreeWidget.topLevelItem(row) |
|
274 if ":" in itm.text(1): |
|
275 rev, changeset = itm.text(1).split(":", 1) |
|
276 rev = "{0} {1}".format(changeset.strip(), rev.strip()) |
|
277 else: |
|
278 rev = itm.text(1).strip() |
|
279 |
|
280 lines.append("{0} {1} {2}".format(itm.text(0).strip(), rev, |
|
281 itm.text(2))) |
|
282 |
|
283 return "\n".join(lines) |