33 |
46 |
34 import UI.PixmapCache |
47 import UI.PixmapCache |
35 import Preferences |
48 import Preferences |
36 import Utilities |
49 import Utilities |
37 |
50 |
38 COLORNAMES = ["blue", "darkgreen", "red", "green", "darkblue", "purple", |
51 COLORNAMES = [ |
39 "cyan", "olive", "magenta", "darkred", "darkmagenta", |
52 "blue", |
40 "darkcyan", "gray", "yellow"] |
53 "darkgreen", |
|
54 "red", |
|
55 "green", |
|
56 "darkblue", |
|
57 "purple", |
|
58 "cyan", |
|
59 "olive", |
|
60 "magenta", |
|
61 "darkred", |
|
62 "darkmagenta", |
|
63 "darkcyan", |
|
64 "gray", |
|
65 "yellow", |
|
66 ] |
41 COLORS = [str(QColor(x).name()) for x in COLORNAMES] |
67 COLORS = [str(QColor(x).name()) for x in COLORNAMES] |
42 |
68 |
43 LIGHTCOLORS = ["#aaaaff", "#7faa7f", "#ffaaaa", "#aaffaa", "#7f7faa", |
69 LIGHTCOLORS = [ |
44 "#ffaaff", "#aaffff", "#d5d579", "#ffaaff", "#d57979", |
70 "#aaaaff", |
45 "#d579d5", "#79d5d5", "#d5d5d5", "#d5d500", |
71 "#7faa7f", |
46 ] |
72 "#ffaaaa", |
|
73 "#aaffaa", |
|
74 "#7f7faa", |
|
75 "#ffaaff", |
|
76 "#aaffff", |
|
77 "#d5d579", |
|
78 "#ffaaff", |
|
79 "#d57979", |
|
80 "#d579d5", |
|
81 "#79d5d5", |
|
82 "#d5d5d5", |
|
83 "#d5d500", |
|
84 ] |
47 |
85 |
48 |
86 |
49 class HgLogBrowserDialog(QWidget, Ui_HgLogBrowserDialog): |
87 class HgLogBrowserDialog(QWidget, Ui_HgLogBrowserDialog): |
50 """ |
88 """ |
51 Class implementing a dialog to browse the log history. |
89 Class implementing a dialog to browse the log history. |
52 """ |
90 """ |
|
91 |
53 IconColumn = 0 |
92 IconColumn = 0 |
54 BranchColumn = 1 |
93 BranchColumn = 1 |
55 RevisionColumn = 2 |
94 RevisionColumn = 2 |
56 PhaseColumn = 3 |
95 PhaseColumn = 3 |
57 AuthorColumn = 4 |
96 AuthorColumn = 4 |
58 DateColumn = 5 |
97 DateColumn = 5 |
59 MessageColumn = 6 |
98 MessageColumn = 6 |
60 TagsColumn = 7 |
99 TagsColumn = 7 |
61 BookmarksColumn = 8 |
100 BookmarksColumn = 8 |
62 |
101 |
63 LargefilesCacheL = ".hglf/" |
102 LargefilesCacheL = ".hglf/" |
64 LargefilesCacheW = ".hglf\\" |
103 LargefilesCacheW = ".hglf\\" |
65 PathSeparatorRe = re.compile(r"/|\\") |
104 PathSeparatorRe = re.compile(r"/|\\") |
66 |
105 |
67 GraftedRe = re.compile(r"\(grafted from ([0-9a-fA-F]+)\)") |
106 GraftedRe = re.compile(r"\(grafted from ([0-9a-fA-F]+)\)") |
68 GraftedTemplate = '(grafted from <a href="chg:{0}">{0}</a>)' |
107 GraftedTemplate = '(grafted from <a href="chg:{0}">{0}</a>)' |
69 |
108 |
70 ClosedIndicator = " \u2612" |
109 ClosedIndicator = " \u2612" |
71 |
110 |
72 def __init__(self, vcs, mode="", parent=None): |
111 def __init__(self, vcs, mode="", parent=None): |
73 """ |
112 """ |
74 Constructor |
113 Constructor |
75 |
114 |
76 @param vcs reference to the vcs object |
115 @param vcs reference to the vcs object |
77 @type Hg |
116 @type Hg |
78 @param mode mode of the dialog |
117 @param mode mode of the dialog |
79 @type str (one of log, full_log, incoming, outgoing) |
118 @type str (one of log, full_log, incoming, outgoing) |
80 @param parent parent widget |
119 @param parent parent widget |
81 @type QWidget |
120 @type QWidget |
82 """ |
121 """ |
83 super().__init__(parent) |
122 super().__init__(parent) |
84 self.setupUi(self) |
123 self.setupUi(self) |
85 |
124 |
86 windowFlags = self.windowFlags() |
125 windowFlags = self.windowFlags() |
87 windowFlags |= Qt.WindowType.WindowContextHelpButtonHint |
126 windowFlags |= Qt.WindowType.WindowContextHelpButtonHint |
88 self.setWindowFlags(windowFlags) |
127 self.setWindowFlags(windowFlags) |
89 |
128 |
90 self.mainSplitter.setSizes([300, 400]) |
129 self.mainSplitter.setSizes([300, 400]) |
91 self.mainSplitter.setStretchFactor(0, 1) |
130 self.mainSplitter.setStretchFactor(0, 1) |
92 self.mainSplitter.setStretchFactor(1, 2) |
131 self.mainSplitter.setStretchFactor(1, 2) |
93 self.diffSplitter.setStretchFactor(0, 1) |
132 self.diffSplitter.setStretchFactor(0, 1) |
94 self.diffSplitter.setStretchFactor(1, 2) |
133 self.diffSplitter.setStretchFactor(1, 2) |
95 |
134 |
96 if not mode: |
135 if not mode: |
97 if vcs.getPlugin().getPreferences("LogBrowserShowFullLog"): |
136 if vcs.getPlugin().getPreferences("LogBrowserShowFullLog"): |
98 mode = "full_log" |
137 mode = "full_log" |
99 else: |
138 else: |
100 mode = "log" |
139 mode = "log" |
101 |
140 |
102 if mode == "log": |
141 if mode == "log": |
103 self.setWindowTitle(self.tr("Mercurial Log")) |
142 self.setWindowTitle(self.tr("Mercurial Log")) |
104 elif mode == "incoming": |
143 elif mode == "incoming": |
105 self.setWindowTitle(self.tr("Mercurial Log (Incoming)")) |
144 self.setWindowTitle(self.tr("Mercurial Log (Incoming)")) |
106 elif mode == "outgoing": |
145 elif mode == "outgoing": |
107 self.setWindowTitle(self.tr("Mercurial Log (Outgoing)")) |
146 self.setWindowTitle(self.tr("Mercurial Log (Outgoing)")) |
108 elif mode == "full_log": |
147 elif mode == "full_log": |
109 self.setWindowTitle(self.tr("Mercurial Full Log")) |
148 self.setWindowTitle(self.tr("Mercurial Full Log")) |
110 |
149 |
111 self.buttonBox.button( |
150 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(False) |
112 QDialogButtonBox.StandardButton.Close).setEnabled(False) |
151 self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setDefault(True) |
113 self.buttonBox.button( |
152 |
114 QDialogButtonBox.StandardButton.Cancel).setDefault(True) |
|
115 |
|
116 self.filesTree.headerItem().setText(self.filesTree.columnCount(), "") |
153 self.filesTree.headerItem().setText(self.filesTree.columnCount(), "") |
117 self.filesTree.header().setSortIndicator( |
154 self.filesTree.header().setSortIndicator(0, Qt.SortOrder.AscendingOrder) |
118 0, Qt.SortOrder.AscendingOrder) |
155 |
119 |
|
120 self.upButton.setIcon(UI.PixmapCache.getIcon("1uparrow")) |
156 self.upButton.setIcon(UI.PixmapCache.getIcon("1uparrow")) |
121 self.downButton.setIcon(UI.PixmapCache.getIcon("1downarrow")) |
157 self.downButton.setIcon(UI.PixmapCache.getIcon("1downarrow")) |
122 |
158 |
123 self.refreshButton = self.buttonBox.addButton( |
159 self.refreshButton = self.buttonBox.addButton( |
124 self.tr("&Refresh"), QDialogButtonBox.ButtonRole.ActionRole) |
160 self.tr("&Refresh"), QDialogButtonBox.ButtonRole.ActionRole |
|
161 ) |
125 self.refreshButton.setToolTip( |
162 self.refreshButton.setToolTip( |
126 self.tr("Press to refresh the list of changesets")) |
163 self.tr("Press to refresh the list of changesets") |
|
164 ) |
127 self.refreshButton.setEnabled(False) |
165 self.refreshButton.setEnabled(False) |
128 |
166 |
129 self.findPrevButton.setIcon(UI.PixmapCache.getIcon("1leftarrow")) |
167 self.findPrevButton.setIcon(UI.PixmapCache.getIcon("1leftarrow")) |
130 self.findNextButton.setIcon(UI.PixmapCache.getIcon("1rightarrow")) |
168 self.findNextButton.setIcon(UI.PixmapCache.getIcon("1rightarrow")) |
131 self.__findBackwards = False |
169 self.__findBackwards = False |
132 |
170 |
133 self.modeComboBox.addItem(self.tr("Find"), "find") |
171 self.modeComboBox.addItem(self.tr("Find"), "find") |
134 self.modeComboBox.addItem(self.tr("Filter"), "filter") |
172 self.modeComboBox.addItem(self.tr("Filter"), "filter") |
135 |
173 |
136 self.fieldCombo.addItem(self.tr("Revision"), "revision") |
174 self.fieldCombo.addItem(self.tr("Revision"), "revision") |
137 self.fieldCombo.addItem(self.tr("Author"), "author") |
175 self.fieldCombo.addItem(self.tr("Author"), "author") |
138 self.fieldCombo.addItem(self.tr("Message"), "message") |
176 self.fieldCombo.addItem(self.tr("Message"), "message") |
139 self.fieldCombo.addItem(self.tr("File"), "file") |
177 self.fieldCombo.addItem(self.tr("File"), "file") |
140 self.fieldCombo.addItem(self.tr("Phase"), "phase") |
178 self.fieldCombo.addItem(self.tr("Phase"), "phase") |
141 |
179 |
142 font = Preferences.getEditorOtherFonts("MonospacedFont") |
180 font = Preferences.getEditorOtherFonts("MonospacedFont") |
143 self.diffEdit.document().setDefaultFont(font) |
181 self.diffEdit.document().setDefaultFont(font) |
144 |
182 |
145 self.diffHighlighter = HgDiffHighlighter(self.diffEdit.document()) |
183 self.diffHighlighter = HgDiffHighlighter(self.diffEdit.document()) |
146 self.__diffGenerator = HgDiffGenerator(vcs, self) |
184 self.__diffGenerator = HgDiffGenerator(vcs, self) |
147 self.__diffGenerator.finished.connect(self.__generatorFinished) |
185 self.__diffGenerator.finished.connect(self.__generatorFinished) |
148 |
186 |
149 self.vcs = vcs |
187 self.vcs = vcs |
150 if mode in ("log", "incoming", "outgoing", "full_log"): |
188 if mode in ("log", "incoming", "outgoing", "full_log"): |
151 if mode == "full_log": |
189 if mode == "full_log": |
152 self.commandMode = "incoming" |
190 self.commandMode = "incoming" |
153 else: |
191 else: |
155 self.initialCommandMode = mode |
193 self.initialCommandMode = mode |
156 else: |
194 else: |
157 self.commandMode = "log" |
195 self.commandMode = "log" |
158 self.initialCommandMode = "log" |
196 self.initialCommandMode = "log" |
159 self.__hgClient = vcs.getClient() |
197 self.__hgClient = vcs.getClient() |
160 |
198 |
161 self.__detailsTemplate = self.tr( |
199 self.__detailsTemplate = self.tr( |
162 "<table>" |
200 "<table>" |
163 "<tr><td><b>Revision</b></td><td>{0}</td></tr>" |
201 "<tr><td><b>Revision</b></td><td>{0}</td></tr>" |
164 "<tr><td><b>Date</b></td><td>{1}</td></tr>" |
202 "<tr><td><b>Date</b></td><td>{1}</td></tr>" |
165 "<tr><td><b>Author</b></td><td>{2}</td></tr>" |
203 "<tr><td><b>Author</b></td><td>{2}</td></tr>" |
166 "<tr><td><b>Branch</b></td><td>{3}</td></tr>" |
204 "<tr><td><b>Branch</b></td><td>{3}</td></tr>" |
167 "{4}" |
205 "{4}" |
168 "<tr><td><b>Message</b></td><td>{5}</td></tr>" |
206 "<tr><td><b>Message</b></td><td>{5}</td></tr>" |
169 "</table>" |
207 "</table>" |
170 ) |
208 ) |
171 self.__parentsTemplate = self.tr( |
209 self.__parentsTemplate = self.tr("<tr><td><b>Parents</b></td><td>{0}</td></tr>") |
172 "<tr><td><b>Parents</b></td><td>{0}</td></tr>" |
|
173 ) |
|
174 self.__childrenTemplate = self.tr( |
210 self.__childrenTemplate = self.tr( |
175 "<tr><td><b>Children</b></td><td>{0}</td></tr>" |
211 "<tr><td><b>Children</b></td><td>{0}</td></tr>" |
176 ) |
212 ) |
177 self.__tagsTemplate = self.tr( |
213 self.__tagsTemplate = self.tr("<tr><td><b>Tags</b></td><td>{0}</td></tr>") |
178 "<tr><td><b>Tags</b></td><td>{0}</td></tr>" |
|
179 ) |
|
180 self.__latestTagTemplate = self.tr( |
214 self.__latestTagTemplate = self.tr( |
181 "<tr><td><b>Latest Tag</b></td><td>{0}</td></tr>" |
215 "<tr><td><b>Latest Tag</b></td><td>{0}</td></tr>" |
182 ) |
216 ) |
183 self.__bookmarksTemplate = self.tr( |
217 self.__bookmarksTemplate = self.tr( |
184 "<tr><td><b>Bookmarks</b></td><td>{0}</td></tr>" |
218 "<tr><td><b>Bookmarks</b></td><td>{0}</td></tr>" |
185 ) |
219 ) |
186 |
220 |
187 self.__bundle = "" |
221 self.__bundle = "" |
188 self.__filename = "" |
222 self.__filename = "" |
189 self.__isFile = False |
223 self.__isFile = False |
190 self.__selectedRevisions = [] |
224 self.__selectedRevisions = [] |
191 self.intercept = False |
225 self.intercept = False |
192 |
226 |
193 self.__initData() |
227 self.__initData() |
194 |
228 |
195 self.__allBranchesFilter = self.tr("All") |
229 self.__allBranchesFilter = self.tr("All") |
196 |
230 |
197 self.fromDate.setDisplayFormat("yyyy-MM-dd") |
231 self.fromDate.setDisplayFormat("yyyy-MM-dd") |
198 self.toDate.setDisplayFormat("yyyy-MM-dd") |
232 self.toDate.setDisplayFormat("yyyy-MM-dd") |
199 self.__resetUI() |
233 self.__resetUI() |
200 |
234 |
201 # roles used in the log tree |
235 # roles used in the log tree |
202 self.__messageRole = Qt.ItemDataRole.UserRole |
236 self.__messageRole = Qt.ItemDataRole.UserRole |
203 self.__changesRole = Qt.ItemDataRole.UserRole + 1 |
237 self.__changesRole = Qt.ItemDataRole.UserRole + 1 |
204 self.__edgesRole = Qt.ItemDataRole.UserRole + 2 |
238 self.__edgesRole = Qt.ItemDataRole.UserRole + 2 |
205 self.__parentsRole = Qt.ItemDataRole.UserRole + 3 |
239 self.__parentsRole = Qt.ItemDataRole.UserRole + 3 |
206 self.__latestTagRole = Qt.ItemDataRole.UserRole + 4 |
240 self.__latestTagRole = Qt.ItemDataRole.UserRole + 4 |
207 self.__incomingRole = Qt.ItemDataRole.UserRole + 5 |
241 self.__incomingRole = Qt.ItemDataRole.UserRole + 5 |
208 |
242 |
209 # roles used in the file tree |
243 # roles used in the file tree |
210 self.__diffFileLineRole = Qt.ItemDataRole.UserRole |
244 self.__diffFileLineRole = Qt.ItemDataRole.UserRole |
211 |
245 |
212 self.flags = { |
246 self.flags = { |
213 'A': self.tr('Added'), |
247 "A": self.tr("Added"), |
214 'D': self.tr('Deleted'), |
248 "D": self.tr("Deleted"), |
215 'M': self.tr('Modified'), |
249 "M": self.tr("Modified"), |
216 } |
250 } |
217 |
251 |
218 self.phases = { |
252 self.phases = { |
219 'draft': self.tr("Draft"), |
253 "draft": self.tr("Draft"), |
220 'public': self.tr("Public"), |
254 "public": self.tr("Public"), |
221 'secret': self.tr("Secret"), |
255 "secret": self.tr("Secret"), |
222 } |
256 } |
223 |
257 |
224 self.__dotRadius = 8 |
258 self.__dotRadius = 8 |
225 self.__rowHeight = 20 |
259 self.__rowHeight = 20 |
226 |
260 |
227 self.logTree.setIconSize( |
261 self.logTree.setIconSize(QSize(100 * self.__rowHeight, self.__rowHeight)) |
228 QSize(100 * self.__rowHeight, self.__rowHeight)) |
|
229 self.BookmarksColumn = self.logTree.columnCount() |
262 self.BookmarksColumn = self.logTree.columnCount() |
230 self.logTree.headerItem().setText( |
263 self.logTree.headerItem().setText(self.BookmarksColumn, self.tr("Bookmarks")) |
231 self.BookmarksColumn, self.tr("Bookmarks")) |
264 |
232 |
|
233 self.__logTreeNormalFont = self.logTree.font() |
265 self.__logTreeNormalFont = self.logTree.font() |
234 self.__logTreeNormalFont.setBold(False) |
266 self.__logTreeNormalFont.setBold(False) |
235 self.__logTreeBoldFont = self.logTree.font() |
267 self.__logTreeBoldFont = self.logTree.font() |
236 self.__logTreeBoldFont.setBold(True) |
268 self.__logTreeBoldFont.setBold(True) |
237 self.__logTreeHasDarkBackground = ericApp().usesDarkPalette() |
269 self.__logTreeHasDarkBackground = ericApp().usesDarkPalette() |
238 |
270 |
239 self.detailsEdit.anchorClicked.connect(self.__revisionClicked) |
271 self.detailsEdit.anchorClicked.connect(self.__revisionClicked) |
240 |
272 |
241 self.__initActionsMenu() |
273 self.__initActionsMenu() |
242 |
274 |
243 self.__finishCallbacks = [] |
275 self.__finishCallbacks = [] |
244 if self.initialCommandMode == "full_log": |
276 if self.initialCommandMode == "full_log": |
245 self.__addFinishCallback(self.on_nextButton_clicked) |
277 self.__addFinishCallback(self.on_nextButton_clicked) |
246 |
278 |
247 def __addFinishCallback(self, callback): |
279 def __addFinishCallback(self, callback): |
248 """ |
280 """ |
249 Private method to add a method to be called once the process finished. |
281 Private method to add a method to be called once the process finished. |
250 |
282 |
251 The callback methods are invoke in a FIFO style and are consumed. If |
283 The callback methods are invoke in a FIFO style and are consumed. If |
252 a callback method needs to be called again, it must be added again. |
284 a callback method needs to be called again, it must be added again. |
253 |
285 |
254 @param callback callback method |
286 @param callback callback method |
255 @type function |
287 @type function |
256 """ |
288 """ |
257 if callback not in self.__finishCallbacks: |
289 if callback not in self.__finishCallbacks: |
258 self.__finishCallbacks.append(callback) |
290 self.__finishCallbacks.append(callback) |
259 |
291 |
260 def __initActionsMenu(self): |
292 def __initActionsMenu(self): |
261 """ |
293 """ |
262 Private method to initialize the actions menu. |
294 Private method to initialize the actions menu. |
263 """ |
295 """ |
264 self.__actionsMenu = QMenu() |
296 self.__actionsMenu = QMenu() |
265 self.__actionsMenu.setTearOffEnabled(True) |
297 self.__actionsMenu.setTearOffEnabled(True) |
266 self.__actionsMenu.setToolTipsVisible(True) |
298 self.__actionsMenu.setToolTipsVisible(True) |
267 |
299 |
268 self.__graftAct = self.__actionsMenu.addAction( |
300 self.__graftAct = self.__actionsMenu.addAction( |
269 UI.PixmapCache.getIcon("vcsGraft"), |
301 UI.PixmapCache.getIcon("vcsGraft"), |
270 self.tr("Copy Changesets"), self.__graftActTriggered) |
302 self.tr("Copy Changesets"), |
271 self.__graftAct.setToolTip(self.tr( |
303 self.__graftActTriggered, |
272 "Copy the selected changesets to the current branch")) |
304 ) |
273 |
305 self.__graftAct.setToolTip( |
|
306 self.tr("Copy the selected changesets to the current branch") |
|
307 ) |
|
308 |
274 self.__mergeAct = self.__actionsMenu.addAction( |
309 self.__mergeAct = self.__actionsMenu.addAction( |
275 UI.PixmapCache.getIcon("vcsMerge"), |
310 UI.PixmapCache.getIcon("vcsMerge"), |
276 self.tr("Merge with Changeset"), self.__mergeActTriggered) |
311 self.tr("Merge with Changeset"), |
277 self.__mergeAct.setToolTip(self.tr( |
312 self.__mergeActTriggered, |
278 "Merge the working directory with the selected changeset")) |
313 ) |
279 |
314 self.__mergeAct.setToolTip( |
|
315 self.tr("Merge the working directory with the selected changeset") |
|
316 ) |
|
317 |
280 self.__phaseAct = self.__actionsMenu.addAction( |
318 self.__phaseAct = self.__actionsMenu.addAction( |
281 self.tr("Change Phase"), self.__phaseActTriggered) |
319 self.tr("Change Phase"), self.__phaseActTriggered |
282 self.__phaseAct.setToolTip(self.tr( |
320 ) |
283 "Change the phase of the selected revisions")) |
321 self.__phaseAct.setToolTip( |
284 self.__phaseAct.setWhatsThis(self.tr( |
322 self.tr("Change the phase of the selected revisions") |
285 """<b>Change Phase</b>\n<p>This changes the phase of the""" |
323 ) |
286 """ selected revisions. The selected revisions have to have""" |
324 self.__phaseAct.setWhatsThis( |
287 """ the same current phase.</p>""")) |
325 self.tr( |
288 |
326 """<b>Change Phase</b>\n<p>This changes the phase of the""" |
|
327 """ selected revisions. The selected revisions have to have""" |
|
328 """ the same current phase.</p>""" |
|
329 ) |
|
330 ) |
|
331 |
289 self.__tagAct = self.__actionsMenu.addAction( |
332 self.__tagAct = self.__actionsMenu.addAction( |
290 UI.PixmapCache.getIcon("vcsTag"), self.tr("Tag"), |
333 UI.PixmapCache.getIcon("vcsTag"), self.tr("Tag"), self.__tagActTriggered |
291 self.__tagActTriggered) |
334 ) |
292 self.__tagAct.setToolTip(self.tr("Tag the selected revision")) |
335 self.__tagAct.setToolTip(self.tr("Tag the selected revision")) |
293 |
336 |
294 self.__closeHeadsAct = self.__actionsMenu.addAction( |
337 self.__closeHeadsAct = self.__actionsMenu.addAction( |
295 UI.PixmapCache.getIcon("closehead"), self.tr("Close Heads"), |
338 UI.PixmapCache.getIcon("closehead"), |
296 self.__closeHeadsActTriggered) |
339 self.tr("Close Heads"), |
|
340 self.__closeHeadsActTriggered, |
|
341 ) |
297 self.__closeHeadsAct.setToolTip(self.tr("Close the selected heads")) |
342 self.__closeHeadsAct.setToolTip(self.tr("Close the selected heads")) |
298 |
343 |
299 self.__switchAct = self.__actionsMenu.addAction( |
344 self.__switchAct = self.__actionsMenu.addAction( |
300 UI.PixmapCache.getIcon("vcsSwitch"), self.tr("Switch"), |
345 UI.PixmapCache.getIcon("vcsSwitch"), |
301 self.__switchActTriggered) |
346 self.tr("Switch"), |
302 self.__switchAct.setToolTip(self.tr( |
347 self.__switchActTriggered, |
303 "Switch the working directory to the selected revision")) |
348 ) |
304 |
349 self.__switchAct.setToolTip( |
|
350 self.tr("Switch the working directory to the selected revision") |
|
351 ) |
|
352 |
305 self.__actionsMenu.addSeparator() |
353 self.__actionsMenu.addSeparator() |
306 |
354 |
307 self.__bookmarkAct = self.__actionsMenu.addAction( |
355 self.__bookmarkAct = self.__actionsMenu.addAction( |
308 UI.PixmapCache.getIcon("addBookmark"), |
356 UI.PixmapCache.getIcon("addBookmark"), |
309 self.tr("Define Bookmark..."), self.__bookmarkActTriggered) |
357 self.tr("Define Bookmark..."), |
310 self.__bookmarkAct.setToolTip( |
358 self.__bookmarkActTriggered, |
311 self.tr("Bookmark the selected revision")) |
359 ) |
|
360 self.__bookmarkAct.setToolTip(self.tr("Bookmark the selected revision")) |
312 self.__bookmarkMoveAct = self.__actionsMenu.addAction( |
361 self.__bookmarkMoveAct = self.__actionsMenu.addAction( |
313 UI.PixmapCache.getIcon("moveBookmark"), |
362 UI.PixmapCache.getIcon("moveBookmark"), |
314 self.tr("Move Bookmark..."), self.__bookmarkMoveActTriggered) |
363 self.tr("Move Bookmark..."), |
|
364 self.__bookmarkMoveActTriggered, |
|
365 ) |
315 self.__bookmarkMoveAct.setToolTip( |
366 self.__bookmarkMoveAct.setToolTip( |
316 self.tr("Move bookmark to the selected revision")) |
367 self.tr("Move bookmark to the selected revision") |
317 |
368 ) |
|
369 |
318 self.__actionsMenu.addSeparator() |
370 self.__actionsMenu.addSeparator() |
319 |
371 |
320 self.__pullAct = self.__actionsMenu.addAction( |
372 self.__pullAct = self.__actionsMenu.addAction( |
321 UI.PixmapCache.getIcon("vcsUpdate"), self.tr("Pull Changes"), |
373 UI.PixmapCache.getIcon("vcsUpdate"), |
322 self.__pullActTriggered) |
374 self.tr("Pull Changes"), |
323 self.__pullAct.setToolTip(self.tr( |
375 self.__pullActTriggered, |
324 "Pull changes from a remote repository")) |
376 ) |
|
377 self.__pullAct.setToolTip(self.tr("Pull changes from a remote repository")) |
325 self.__lfPullAct = self.__actionsMenu.addAction( |
378 self.__lfPullAct = self.__actionsMenu.addAction( |
326 self.tr("Pull Large Files"), self.__lfPullActTriggered) |
379 self.tr("Pull Large Files"), self.__lfPullActTriggered |
327 self.__lfPullAct.setToolTip(self.tr( |
380 ) |
328 "Pull large files for selected revisions")) |
381 self.__lfPullAct.setToolTip(self.tr("Pull large files for selected revisions")) |
329 |
382 |
330 self.__actionsMenu.addSeparator() |
383 self.__actionsMenu.addSeparator() |
331 |
384 |
332 self.__pushAct = self.__actionsMenu.addAction( |
385 self.__pushAct = self.__actionsMenu.addAction( |
333 UI.PixmapCache.getIcon("vcsCommit"), |
386 UI.PixmapCache.getIcon("vcsCommit"), |
334 self.tr("Push Selected Changes"), self.__pushActTriggered) |
387 self.tr("Push Selected Changes"), |
335 self.__pushAct.setToolTip(self.tr( |
388 self.__pushActTriggered, |
336 "Push changes of the selected changeset and its ancestors" |
389 ) |
337 " to a remote repository")) |
390 self.__pushAct.setToolTip( |
|
391 self.tr( |
|
392 "Push changes of the selected changeset and its ancestors" |
|
393 " to a remote repository" |
|
394 ) |
|
395 ) |
338 self.__pushAllAct = self.__actionsMenu.addAction( |
396 self.__pushAllAct = self.__actionsMenu.addAction( |
339 UI.PixmapCache.getIcon("vcsCommit"), |
397 UI.PixmapCache.getIcon("vcsCommit"), |
340 self.tr("Push All Changes"), self.__pushAllActTriggered) |
398 self.tr("Push All Changes"), |
341 self.__pushAllAct.setToolTip(self.tr( |
399 self.__pushAllActTriggered, |
342 "Push all changes to a remote repository")) |
400 ) |
343 |
401 self.__pushAllAct.setToolTip(self.tr("Push all changes to a remote repository")) |
|
402 |
344 self.__actionsMenu.addSeparator() |
403 self.__actionsMenu.addSeparator() |
345 |
404 |
346 self.__bundleAct = self.__actionsMenu.addAction( |
405 self.__bundleAct = self.__actionsMenu.addAction( |
347 UI.PixmapCache.getIcon("vcsCreateChangegroup"), |
406 UI.PixmapCache.getIcon("vcsCreateChangegroup"), |
348 self.tr("Create Changegroup"), self.__bundleActTriggered) |
407 self.tr("Create Changegroup"), |
349 self.__bundleAct.setToolTip(self.tr( |
408 self.__bundleActTriggered, |
350 "Create a changegroup file containing the selected changesets")) |
409 ) |
351 self.__bundleAct.setWhatsThis(self.tr( |
410 self.__bundleAct.setToolTip( |
352 """<b>Create Changegroup</b>\n<p>This creates a changegroup""" |
411 self.tr("Create a changegroup file containing the selected changesets") |
353 """ file containing the selected revisions. If no revisions""" |
412 ) |
354 """ are selected, all changesets will be bundled. If one""" |
413 self.__bundleAct.setWhatsThis( |
355 """ revision is selected, it will be interpreted as the base""" |
414 self.tr( |
356 """ revision. Otherwise the lowest revision will be used as""" |
415 """<b>Create Changegroup</b>\n<p>This creates a changegroup""" |
357 """ the base revision and all other revision will be bundled.""" |
416 """ file containing the selected revisions. If no revisions""" |
358 """ If the dialog is showing outgoing changesets, all""" |
417 """ are selected, all changesets will be bundled. If one""" |
359 """ selected changesets will be bundled.</p>""")) |
418 """ revision is selected, it will be interpreted as the base""" |
|
419 """ revision. Otherwise the lowest revision will be used as""" |
|
420 """ the base revision and all other revision will be bundled.""" |
|
421 """ If the dialog is showing outgoing changesets, all""" |
|
422 """ selected changesets will be bundled.</p>""" |
|
423 ) |
|
424 ) |
360 self.__unbundleAct = self.__actionsMenu.addAction( |
425 self.__unbundleAct = self.__actionsMenu.addAction( |
361 UI.PixmapCache.getIcon("vcsApplyChangegroup"), |
426 UI.PixmapCache.getIcon("vcsApplyChangegroup"), |
362 self.tr("Apply Changegroup"), self.__unbundleActTriggered) |
427 self.tr("Apply Changegroup"), |
363 self.__unbundleAct.setToolTip(self.tr( |
428 self.__unbundleActTriggered, |
364 "Apply the currently viewed changegroup file")) |
429 ) |
365 |
430 self.__unbundleAct.setToolTip( |
|
431 self.tr("Apply the currently viewed changegroup file") |
|
432 ) |
|
433 |
366 self.__actionsMenu.addSeparator() |
434 self.__actionsMenu.addSeparator() |
367 |
435 |
368 self.__gpgSignAct = self.__actionsMenu.addAction( |
436 self.__gpgSignAct = self.__actionsMenu.addAction( |
369 UI.PixmapCache.getIcon("changesetSign"), |
437 UI.PixmapCache.getIcon("changesetSign"), |
370 self.tr("Sign Revisions"), self.__gpgSignActTriggered) |
438 self.tr("Sign Revisions"), |
371 self.__gpgSignAct.setToolTip(self.tr( |
439 self.__gpgSignActTriggered, |
372 "Add a signature for the selected revisions")) |
440 ) |
|
441 self.__gpgSignAct.setToolTip( |
|
442 self.tr("Add a signature for the selected revisions") |
|
443 ) |
373 self.__gpgVerifyAct = self.__actionsMenu.addAction( |
444 self.__gpgVerifyAct = self.__actionsMenu.addAction( |
374 UI.PixmapCache.getIcon("changesetSignVerify"), |
445 UI.PixmapCache.getIcon("changesetSignVerify"), |
375 self.tr("Verify Signatures"), self.__gpgVerifyActTriggered) |
446 self.tr("Verify Signatures"), |
376 self.__gpgVerifyAct.setToolTip(self.tr( |
447 self.__gpgVerifyActTriggered, |
377 "Verify all signatures there may be for the selected revision")) |
448 ) |
378 |
449 self.__gpgVerifyAct.setToolTip( |
|
450 self.tr("Verify all signatures there may be for the selected revision") |
|
451 ) |
|
452 |
379 self.__actionsMenu.addSeparator() |
453 self.__actionsMenu.addSeparator() |
380 |
454 |
381 self.__stripAct = self.__actionsMenu.addAction( |
455 self.__stripAct = self.__actionsMenu.addAction( |
382 UI.PixmapCache.getIcon("fileDelete"), |
456 UI.PixmapCache.getIcon("fileDelete"), |
383 self.tr("Strip Changesets"), self.__stripActTriggered) |
457 self.tr("Strip Changesets"), |
384 self.__stripAct.setToolTip(self.tr( |
458 self.__stripActTriggered, |
385 "Strip changesets from a repository")) |
459 ) |
386 |
460 self.__stripAct.setToolTip(self.tr("Strip changesets from a repository")) |
|
461 |
387 self.__actionsMenu.addSeparator() |
462 self.__actionsMenu.addSeparator() |
388 |
463 |
389 self.__selectAllAct = self.__actionsMenu.addAction( |
464 self.__selectAllAct = self.__actionsMenu.addAction( |
390 self.tr("Select All Entries"), self.__selectAllActTriggered) |
465 self.tr("Select All Entries"), self.__selectAllActTriggered |
|
466 ) |
391 self.__unselectAllAct = self.__actionsMenu.addAction( |
467 self.__unselectAllAct = self.__actionsMenu.addAction( |
392 self.tr("Deselect All Entries"), |
468 self.tr("Deselect All Entries"), lambda: self.__selectAllActTriggered(False) |
393 lambda: self.__selectAllActTriggered(False)) |
469 ) |
394 |
470 |
395 self.actionsButton.setIcon( |
471 self.actionsButton.setIcon(UI.PixmapCache.getIcon("actionsToolButton")) |
396 UI.PixmapCache.getIcon("actionsToolButton")) |
|
397 self.actionsButton.setMenu(self.__actionsMenu) |
472 self.actionsButton.setMenu(self.__actionsMenu) |
398 |
473 |
399 def __initData(self): |
474 def __initData(self): |
400 """ |
475 """ |
401 Private method to (re-)initialize some data. |
476 Private method to (re-)initialize some data. |
402 """ |
477 """ |
403 self.__maxDate = QDate() |
478 self.__maxDate = QDate() |
404 self.__minDate = QDate() |
479 self.__minDate = QDate() |
405 self.__filterLogsEnabled = True |
480 self.__filterLogsEnabled = True |
406 |
481 |
407 self.buf = [] # buffer for stdout |
482 self.buf = [] # buffer for stdout |
408 self.diff = None |
483 self.diff = None |
409 self.__started = False |
484 self.__started = False |
410 self.__lastRev = 0 |
485 self.__lastRev = 0 |
411 self.projectMode = False |
486 self.projectMode = False |
412 |
487 |
413 # attributes to store log graph data |
488 # attributes to store log graph data |
414 self.__revs = [] |
489 self.__revs = [] |
415 self.__revColors = {} |
490 self.__revColors = {} |
416 self.__revColor = 0 |
491 self.__revColor = 0 |
417 |
492 |
418 self.__branchColors = {} |
493 self.__branchColors = {} |
419 |
494 |
420 self.__projectWorkingDirParents = [] |
495 self.__projectWorkingDirParents = [] |
421 self.__projectBranch = "" |
496 self.__projectBranch = "" |
422 |
497 |
423 self.__childrenInfo = collections.defaultdict(list) |
498 self.__childrenInfo = collections.defaultdict(list) |
424 |
499 |
425 def closeEvent(self, e): |
500 def closeEvent(self, e): |
426 """ |
501 """ |
427 Protected slot implementing a close event handler. |
502 Protected slot implementing a close event handler. |
428 |
503 |
429 @param e close event (QCloseEvent) |
504 @param e close event (QCloseEvent) |
430 """ |
505 """ |
431 if self.__hgClient.isExecuting(): |
506 if self.__hgClient.isExecuting(): |
432 self.__hgClient.cancel() |
507 self.__hgClient.cancel() |
433 |
508 |
|
509 self.vcs.getPlugin().setPreferences("LogBrowserGeometry", self.saveGeometry()) |
434 self.vcs.getPlugin().setPreferences( |
510 self.vcs.getPlugin().setPreferences( |
435 "LogBrowserGeometry", self.saveGeometry()) |
511 "LogBrowserSplitterStates", |
436 self.vcs.getPlugin().setPreferences( |
512 [ |
437 "LogBrowserSplitterStates", [ |
|
438 self.mainSplitter.saveState(), |
513 self.mainSplitter.saveState(), |
439 self.detailsSplitter.saveState(), |
514 self.detailsSplitter.saveState(), |
440 self.diffSplitter.saveState(), |
515 self.diffSplitter.saveState(), |
441 ] |
516 ], |
442 ) |
517 ) |
443 |
518 |
444 e.accept() |
519 e.accept() |
445 |
520 |
446 def show(self): |
521 def show(self): |
447 """ |
522 """ |
448 Public slot to show the dialog. |
523 Public slot to show the dialog. |
449 """ |
524 """ |
450 self.__reloadGeometry() |
525 self.__reloadGeometry() |
451 self.__restoreSplitterStates() |
526 self.__restoreSplitterStates() |
452 self.__resetUI() |
527 self.__resetUI() |
453 |
528 |
454 super().show() |
529 super().show() |
455 |
530 |
456 def __reloadGeometry(self): |
531 def __reloadGeometry(self): |
457 """ |
532 """ |
458 Private method to restore the geometry. |
533 Private method to restore the geometry. |
461 if geom.isEmpty(): |
536 if geom.isEmpty(): |
462 s = QSize(1000, 800) |
537 s = QSize(1000, 800) |
463 self.resize(s) |
538 self.resize(s) |
464 else: |
539 else: |
465 self.restoreGeometry(geom) |
540 self.restoreGeometry(geom) |
466 |
541 |
467 def __restoreSplitterStates(self): |
542 def __restoreSplitterStates(self): |
468 """ |
543 """ |
469 Private method to restore the state of the various splitters. |
544 Private method to restore the state of the various splitters. |
470 """ |
545 """ |
471 states = self.vcs.getPlugin().getPreferences( |
546 states = self.vcs.getPlugin().getPreferences("LogBrowserSplitterStates") |
472 "LogBrowserSplitterStates") |
|
473 if len(states) == 3: |
547 if len(states) == 3: |
474 # we have three splitters |
548 # we have three splitters |
475 self.mainSplitter.restoreState(states[0]) |
549 self.mainSplitter.restoreState(states[0]) |
476 self.detailsSplitter.restoreState(states[1]) |
550 self.detailsSplitter.restoreState(states[1]) |
477 self.diffSplitter.restoreState(states[2]) |
551 self.diffSplitter.restoreState(states[2]) |
478 |
552 |
479 def __resetUI(self): |
553 def __resetUI(self): |
480 """ |
554 """ |
481 Private method to reset the user interface. |
555 Private method to reset the user interface. |
482 """ |
556 """ |
483 self.branchCombo.clear() |
557 self.branchCombo.clear() |
484 self.fromDate.setDate(QDate.currentDate()) |
558 self.fromDate.setDate(QDate.currentDate()) |
485 self.toDate.setDate(QDate.currentDate()) |
559 self.toDate.setDate(QDate.currentDate()) |
486 self.fieldCombo.setCurrentIndex(self.fieldCombo.findData("message")) |
560 self.fieldCombo.setCurrentIndex(self.fieldCombo.findData("message")) |
487 self.limitSpinBox.setValue(self.vcs.getPlugin().getPreferences( |
561 self.limitSpinBox.setValue(self.vcs.getPlugin().getPreferences("LogLimit")) |
488 "LogLimit")) |
562 self.stopCheckBox.setChecked( |
489 self.stopCheckBox.setChecked(self.vcs.getPlugin().getPreferences( |
563 self.vcs.getPlugin().getPreferences("StopLogOnCopy") |
490 "StopLogOnCopy")) |
564 ) |
491 |
565 |
492 if self.initialCommandMode in ("incoming", "outgoing"): |
566 if self.initialCommandMode in ("incoming", "outgoing"): |
493 self.nextButton.setEnabled(False) |
567 self.nextButton.setEnabled(False) |
494 self.limitSpinBox.setEnabled(False) |
568 self.limitSpinBox.setEnabled(False) |
495 else: |
569 else: |
496 self.nextButton.setEnabled(True) |
570 self.nextButton.setEnabled(True) |
497 self.limitSpinBox.setEnabled(True) |
571 self.limitSpinBox.setEnabled(True) |
498 |
572 |
499 self.logTree.clear() |
573 self.logTree.clear() |
500 |
574 |
501 if self.initialCommandMode == "full_log": |
575 if self.initialCommandMode == "full_log": |
502 self.commandMode = "incoming" |
576 self.commandMode = "incoming" |
503 else: |
577 else: |
504 self.commandMode = self.initialCommandMode |
578 self.commandMode = self.initialCommandMode |
505 |
579 |
506 def __resizeColumnsLog(self): |
580 def __resizeColumnsLog(self): |
507 """ |
581 """ |
508 Private method to resize the log tree columns. |
582 Private method to resize the log tree columns. |
509 """ |
583 """ |
510 self.logTree.header().resizeSections( |
584 self.logTree.header().resizeSections(QHeaderView.ResizeMode.ResizeToContents) |
511 QHeaderView.ResizeMode.ResizeToContents) |
|
512 self.logTree.header().setStretchLastSection(True) |
585 self.logTree.header().setStretchLastSection(True) |
513 |
586 |
514 def __resizeColumnsFiles(self): |
587 def __resizeColumnsFiles(self): |
515 """ |
588 """ |
516 Private method to resize the changed files tree columns. |
589 Private method to resize the changed files tree columns. |
517 """ |
590 """ |
518 self.filesTree.header().resizeSections( |
591 self.filesTree.header().resizeSections(QHeaderView.ResizeMode.ResizeToContents) |
519 QHeaderView.ResizeMode.ResizeToContents) |
|
520 self.filesTree.header().setStretchLastSection(True) |
592 self.filesTree.header().setStretchLastSection(True) |
521 |
593 |
522 def __resortFiles(self): |
594 def __resortFiles(self): |
523 """ |
595 """ |
524 Private method to resort the changed files tree. |
596 Private method to resort the changed files tree. |
525 """ |
597 """ |
526 sortColumn = self.filesTree.sortColumn() |
598 sortColumn = self.filesTree.sortColumn() |
|
599 self.filesTree.sortItems(1, self.filesTree.header().sortIndicatorOrder()) |
527 self.filesTree.sortItems( |
600 self.filesTree.sortItems( |
528 1, self.filesTree.header().sortIndicatorOrder()) |
601 sortColumn, self.filesTree.header().sortIndicatorOrder() |
529 self.filesTree.sortItems( |
602 ) |
530 sortColumn, self.filesTree.header().sortIndicatorOrder()) |
603 |
531 |
|
532 def __getColor(self, n): |
604 def __getColor(self, n): |
533 """ |
605 """ |
534 Private method to get the (rotating) name of the color given an index. |
606 Private method to get the (rotating) name of the color given an index. |
535 |
607 |
536 @param n color index |
608 @param n color index |
537 @type int |
609 @type int |
538 @return color name |
610 @return color name |
539 @rtype str |
611 @rtype str |
540 """ |
612 """ |
541 if self.__logTreeHasDarkBackground: |
613 if self.__logTreeHasDarkBackground: |
542 return LIGHTCOLORS[n % len(LIGHTCOLORS)] |
614 return LIGHTCOLORS[n % len(LIGHTCOLORS)] |
543 else: |
615 else: |
544 return COLORS[n % len(COLORS)] |
616 return COLORS[n % len(COLORS)] |
545 |
617 |
546 def __branchColor(self, branchName): |
618 def __branchColor(self, branchName): |
547 """ |
619 """ |
548 Private method to calculate a color for a given branch name. |
620 Private method to calculate a color for a given branch name. |
549 |
621 |
550 @param branchName name of the branch (string) |
622 @param branchName name of the branch (string) |
551 @return name of the color to use (string) |
623 @return name of the color to use (string) |
552 """ |
624 """ |
553 if branchName not in self.__branchColors: |
625 if branchName not in self.__branchColors: |
554 self.__branchColors[branchName] = self.__getColor( |
626 self.__branchColors[branchName] = self.__getColor(len(self.__branchColors)) |
555 len(self.__branchColors)) |
|
556 return self.__branchColors[branchName] |
627 return self.__branchColors[branchName] |
557 |
628 |
558 def __generateEdges(self, rev, parents): |
629 def __generateEdges(self, rev, parents): |
559 """ |
630 """ |
560 Private method to generate edge info for the give data. |
631 Private method to generate edge info for the give data. |
561 |
632 |
562 @param rev revision to calculate edge info for (integer) |
633 @param rev revision to calculate edge info for (integer) |
563 @param parents list of parent revisions (list of integers) |
634 @param parents list of parent revisions (list of integers) |
564 @return tuple containing the column and color index for |
635 @return tuple containing the column and color index for |
565 the given node and a list of tuples indicating the edges |
636 the given node and a list of tuples indicating the edges |
566 between the given node and its parents |
637 between the given node and its parents |
682 painter.setBrush(dotColor) |
760 painter.setBrush(dotColor) |
683 pen = QPen(pencolor) |
761 pen = QPen(pencolor) |
684 pen.setWidth(penradius) |
762 pen.setWidth(penradius) |
685 painter.setPen(pen) |
763 painter.setPen(pen) |
686 if closed: |
764 if closed: |
687 painter.drawRect(dot_x - 2, dot_y + 1, |
765 painter.drawRect(dot_x - 2, dot_y + 1, radius + 4, radius - 2) |
688 radius + 4, radius - 2) |
|
689 elif self.commandMode in ("incoming", "outgoing"): |
766 elif self.commandMode in ("incoming", "outgoing"): |
690 offset = radius // 2 |
767 offset = radius // 2 |
691 if self.commandMode == "incoming": |
768 if self.commandMode == "incoming": |
692 # incoming: draw a down arrow |
769 # incoming: draw a down arrow |
693 painter.drawConvexPolygon( |
770 painter.drawConvexPolygon( |
694 QPoint(dot_x, dot_y), |
771 QPoint(dot_x, dot_y), |
695 QPoint(dot_x + 2 * offset, dot_y), |
772 QPoint(dot_x + 2 * offset, dot_y), |
696 QPoint(dot_x + offset, dot_y + 2 * offset) |
773 QPoint(dot_x + offset, dot_y + 2 * offset), |
697 ) |
774 ) |
698 else: |
775 else: |
699 # outgoing: draw an up arrow |
776 # outgoing: draw an up arrow |
700 painter.drawConvexPolygon( |
777 painter.drawConvexPolygon( |
701 QPoint(dot_x + offset, dot_y), |
778 QPoint(dot_x + offset, dot_y), |
702 QPoint(dot_x, dot_y + 2 * offset), |
779 QPoint(dot_x, dot_y + 2 * offset), |
703 QPoint(dot_x + 2 * offset, dot_y + 2 * offset) |
780 QPoint(dot_x + 2 * offset, dot_y + 2 * offset), |
704 ) |
781 ) |
705 else: |
782 else: |
706 if isPushableDraft: |
783 if isPushableDraft: |
707 # 'draft' phase: draw an up arrow like outgoing, |
784 # 'draft' phase: draw an up arrow like outgoing, |
708 # if it can be pushed |
785 # if it can be pushed |
709 offset = radius // 2 |
786 offset = radius // 2 |
710 painter.drawConvexPolygon( |
787 painter.drawConvexPolygon( |
711 QPoint(dot_x + offset, dot_y), |
788 QPoint(dot_x + offset, dot_y), |
712 QPoint(dot_x, dot_y + 2 * offset), |
789 QPoint(dot_x, dot_y + 2 * offset), |
713 QPoint(dot_x + 2 * offset, dot_y + 2 * offset) |
790 QPoint(dot_x + 2 * offset, dot_y + 2 * offset), |
714 ) |
791 ) |
715 else: |
792 else: |
716 painter.drawEllipse(dot_x, dot_y, radius, radius) |
793 painter.drawEllipse(dot_x, dot_y, radius, radius) |
717 painter.end() |
794 painter.end() |
718 return QIcon(pix) |
795 return QIcon(pix) |
719 |
796 |
720 def __getParents(self, rev): |
797 def __getParents(self, rev): |
721 """ |
798 """ |
722 Private method to get the parents of the currently viewed |
799 Private method to get the parents of the currently viewed |
723 file/directory. |
800 file/directory. |
724 |
801 |
725 @param rev revision number to get parents for (string) |
802 @param rev revision number to get parents for (string) |
726 @return list of parent revisions (list of integers) |
803 @return list of parent revisions (list of integers) |
727 """ |
804 """ |
728 errMsg = "" |
805 errMsg = "" |
729 parents = [-1] |
806 parents = [-1] |
730 |
807 |
731 if int(rev) > 0: |
808 if int(rev) > 0: |
732 args = self.vcs.initCommand("parents") |
809 args = self.vcs.initCommand("parents") |
733 if self.commandMode == "incoming": |
810 if self.commandMode == "incoming": |
734 if self.__bundle: |
811 if self.__bundle: |
735 args.append("--repository") |
812 args.append("--repository") |
736 args.append(self.__bundle) |
813 args.append(self.__bundle) |
737 elif ( |
814 elif self.vcs.bundleFile and os.path.exists(self.vcs.bundleFile): |
738 self.vcs.bundleFile and |
|
739 os.path.exists(self.vcs.bundleFile) |
|
740 ): |
|
741 args.append("--repository") |
815 args.append("--repository") |
742 args.append(self.vcs.bundleFile) |
816 args.append(self.vcs.bundleFile) |
743 args.append("--template") |
817 args.append("--template") |
744 args.append("{rev}\n") |
818 args.append("{rev}\n") |
745 args.append("-r") |
819 args.append("-r") |
746 args.append(rev) |
820 args.append(rev) |
747 if not self.projectMode: |
821 if not self.projectMode: |
748 args.append(self.__filename) |
822 args.append(self.__filename) |
749 |
823 |
750 output, errMsg = self.__hgClient.runcommand(args) |
824 output, errMsg = self.__hgClient.runcommand(args) |
751 |
825 |
752 if output: |
826 if output: |
753 parents = [int(p) for p in output.strip().splitlines()] |
827 parents = [int(p) for p in output.strip().splitlines()] |
754 |
828 |
755 return parents |
829 return parents |
756 |
830 |
757 def __identifyProject(self): |
831 def __identifyProject(self): |
758 """ |
832 """ |
759 Private method to determine the revision of the project directory. |
833 Private method to determine the revision of the project directory. |
760 """ |
834 """ |
761 errMsg = "" |
835 errMsg = "" |
762 |
836 |
763 args = self.vcs.initCommand("identify") |
837 args = self.vcs.initCommand("identify") |
764 args.append("-nb") |
838 args.append("-nb") |
765 |
839 |
766 output, errMsg = self.__hgClient.runcommand(args) |
840 output, errMsg = self.__hgClient.runcommand(args) |
767 |
841 |
768 if errMsg: |
842 if errMsg: |
769 EricMessageBox.critical( |
843 EricMessageBox.critical(self, self.tr("Mercurial Error"), errMsg) |
770 self, |
844 |
771 self.tr("Mercurial Error"), |
|
772 errMsg) |
|
773 |
|
774 if output: |
845 if output: |
775 outputList = output.strip().split(None, 1) |
846 outputList = output.strip().split(None, 1) |
776 if len(outputList) == 2: |
847 if len(outputList) == 2: |
777 outputRevs = outputList[0].strip() |
848 outputRevs = outputList[0].strip() |
778 if outputRevs.endswith("+"): |
849 if outputRevs.endswith("+"): |
779 outputRevs = outputRevs[:-1] |
850 outputRevs = outputRevs[:-1] |
780 self.__projectWorkingDirParents = outputRevs.split('+') |
851 self.__projectWorkingDirParents = outputRevs.split("+") |
781 else: |
852 else: |
782 self.__projectWorkingDirParents = [outputRevs] |
853 self.__projectWorkingDirParents = [outputRevs] |
783 self.__projectBranch = outputList[1].strip() |
854 self.__projectBranch = outputList[1].strip() |
784 |
855 |
785 def __getClosedBranches(self): |
856 def __getClosedBranches(self): |
786 """ |
857 """ |
787 Private method to get the list of closed branches. |
858 Private method to get the list of closed branches. |
788 """ |
859 """ |
789 self.__closedBranchesRevs = [] |
860 self.__closedBranchesRevs = [] |
790 errMsg = "" |
861 errMsg = "" |
791 |
862 |
792 args = self.vcs.initCommand("branches") |
863 args = self.vcs.initCommand("branches") |
793 args.append("--closed") |
864 args.append("--closed") |
794 |
865 |
795 output, errMsg = self.__hgClient.runcommand(args) |
866 output, errMsg = self.__hgClient.runcommand(args) |
796 |
867 |
797 if errMsg: |
868 if errMsg: |
798 EricMessageBox.critical( |
869 EricMessageBox.critical(self, self.tr("Mercurial Error"), errMsg) |
799 self, |
870 |
800 self.tr("Mercurial Error"), |
|
801 errMsg) |
|
802 |
|
803 if output: |
871 if output: |
804 for line in output.splitlines(): |
872 for line in output.splitlines(): |
805 if line.strip().endswith("(closed)"): |
873 if line.strip().endswith("(closed)"): |
806 parts = line.split() |
874 parts = line.split() |
807 self.__closedBranchesRevs.append( |
875 self.__closedBranchesRevs.append(parts[-2].split(":", 1)[0]) |
808 parts[-2].split(":", 1)[0]) |
876 |
809 |
|
810 def __getHeads(self): |
877 def __getHeads(self): |
811 """ |
878 """ |
812 Private method to get the list of all heads. |
879 Private method to get the list of all heads. |
813 """ |
880 """ |
814 self.__headRevisions = [] |
881 self.__headRevisions = [] |
815 errMsg = "" |
882 errMsg = "" |
816 |
883 |
817 args = self.vcs.initCommand("heads") |
884 args = self.vcs.initCommand("heads") |
818 args.append("--closed") |
885 args.append("--closed") |
819 args.append("--template") |
886 args.append("--template") |
820 args.append("{rev}\n") |
887 args.append("{rev}\n") |
821 |
888 |
822 output, errMsg = self.__hgClient.runcommand(args) |
889 output, errMsg = self.__hgClient.runcommand(args) |
823 |
890 |
824 if errMsg: |
891 if errMsg: |
825 EricMessageBox.critical( |
892 EricMessageBox.critical(self, self.tr("Mercurial Error"), errMsg) |
826 self, |
893 |
827 self.tr("Mercurial Error"), |
|
828 errMsg) |
|
829 |
|
830 if output: |
894 if output: |
831 for line in output.splitlines(): |
895 for line in output.splitlines(): |
832 line = line.strip() |
896 line = line.strip() |
833 if line: |
897 if line: |
834 self.__headRevisions.append(line) |
898 self.__headRevisions.append(line) |
835 |
899 |
836 def __getRevisionOfTag(self, tag): |
900 def __getRevisionOfTag(self, tag): |
837 """ |
901 """ |
838 Private method to get the revision of a tag. |
902 Private method to get the revision of a tag. |
839 |
903 |
840 @param tag tag name |
904 @param tag tag name |
841 @type str |
905 @type str |
842 @return tuple containing the revision and changeset ID |
906 @return tuple containing the revision and changeset ID |
843 @rtype tuple of (str, str) |
907 @rtype tuple of (str, str) |
844 """ |
908 """ |
845 errMsg = "" |
909 errMsg = "" |
846 |
910 |
847 args = self.vcs.initCommand("tags") |
911 args = self.vcs.initCommand("tags") |
848 |
912 |
849 output, errMsg = self.__hgClient.runcommand(args) |
913 output, errMsg = self.__hgClient.runcommand(args) |
850 |
914 |
851 if errMsg: |
915 if errMsg: |
852 EricMessageBox.critical( |
916 EricMessageBox.critical(self, self.tr("Mercurial Error"), errMsg) |
853 self, |
917 |
854 self.tr("Mercurial Error"), |
|
855 errMsg) |
|
856 |
|
857 res = ("", "") |
918 res = ("", "") |
858 if output: |
919 if output: |
859 for line in output.splitlines(): |
920 for line in output.splitlines(): |
860 if line.strip(): |
921 if line.strip(): |
861 with contextlib.suppress(ValueError): |
922 with contextlib.suppress(ValueError): |
862 name, rev = line.strip().rsplit(None, 1) |
923 name, rev = line.strip().rsplit(None, 1) |
863 if name == tag: |
924 if name == tag: |
864 res = tuple(rev.split(":", 1)) |
925 res = tuple(rev.split(":", 1)) |
865 break |
926 break |
866 |
927 |
867 return res |
928 return res |
868 |
929 |
869 def __generateLogItem(self, author, date, message, revision, changedPaths, |
930 def __generateLogItem( |
870 parents, branches, tags, phase, bookmarks, |
931 self, |
871 latestTag, canPush=False): |
932 author, |
|
933 date, |
|
934 message, |
|
935 revision, |
|
936 changedPaths, |
|
937 parents, |
|
938 branches, |
|
939 tags, |
|
940 phase, |
|
941 bookmarks, |
|
942 latestTag, |
|
943 canPush=False, |
|
944 ): |
872 """ |
945 """ |
873 Private method to generate a log tree entry. |
946 Private method to generate a log tree entry. |
874 |
947 |
875 @param author author info |
948 @param author author info |
876 @type str |
949 @type str |
877 @param date date info |
950 @param date date info |
878 @type str |
951 @type str |
879 @param message text of the log message |
952 @param message text of the log message |
948 else: |
1022 else: |
949 itm.setData(0, self.__parentsRole, parents) |
1023 itm.setData(0, self.__parentsRole, parents) |
950 for parent in parents: |
1024 for parent in parents: |
951 self.__childrenInfo[parent].append(int(rev)) |
1025 self.__childrenInfo[parent].append(int(rev)) |
952 itm.setData(0, self.__incomingRole, self.commandMode == "incoming") |
1026 itm.setData(0, self.__incomingRole, self.commandMode == "incoming") |
953 |
1027 |
954 topedges = ( |
1028 topedges = ( |
955 self.logTree.topLevelItem( |
1029 self.logTree.topLevelItem(self.logTree.indexOfTopLevelItem(itm) - 1).data( |
956 self.logTree.indexOfTopLevelItem(itm) - 1 |
1030 0, self.__edgesRole |
957 ).data(0, self.__edgesRole) |
1031 ) |
958 if self.logTree.topLevelItemCount() > 1 else |
1032 if self.logTree.topLevelItemCount() > 1 |
959 None |
1033 else None |
960 ) |
1034 ) |
961 |
1035 |
962 icon = self.__generateIcon(column, color, edges, topedges, |
1036 icon = self.__generateIcon( |
963 QColor(self.__branchColor(branches[0])), |
1037 column, |
964 rev in self.__projectWorkingDirParents, |
1038 color, |
965 rev in self.__closedBranchesRevs, |
1039 edges, |
966 phase == "draft" and canPush) |
1040 topedges, |
|
1041 QColor(self.__branchColor(branches[0])), |
|
1042 rev in self.__projectWorkingDirParents, |
|
1043 rev in self.__closedBranchesRevs, |
|
1044 phase == "draft" and canPush, |
|
1045 ) |
967 itm.setIcon(0, icon) |
1046 itm.setIcon(0, icon) |
968 |
1047 |
969 try: |
1048 try: |
970 self.__lastRev = int(revision.split(":")[0]) |
1049 self.__lastRev = int(revision.split(":")[0]) |
971 except ValueError: |
1050 except ValueError: |
972 self.__lastRev = 0 |
1051 self.__lastRev = 0 |
973 |
1052 |
974 return itm |
1053 return itm |
975 |
1054 |
976 def __getLogEntries(self, startRev=None, noEntries=0): |
1055 def __getLogEntries(self, startRev=None, noEntries=0): |
977 """ |
1056 """ |
978 Private method to retrieve log entries from the repository. |
1057 Private method to retrieve log entries from the repository. |
979 |
1058 |
980 @param startRev revision number to start from (integer, string) |
1059 @param startRev revision number to start from (integer, string) |
981 @param noEntries number of entries to get (0 = default) (int) |
1060 @param noEntries number of entries to get (0 = default) (int) |
982 """ |
1061 """ |
983 self.buttonBox.button( |
1062 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(False) |
984 QDialogButtonBox.StandardButton.Close).setEnabled(False) |
1063 self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setEnabled(True) |
985 self.buttonBox.button( |
1064 self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setDefault(True) |
986 QDialogButtonBox.StandardButton.Cancel).setEnabled(True) |
|
987 self.buttonBox.button( |
|
988 QDialogButtonBox.StandardButton.Cancel).setDefault(True) |
|
989 QApplication.processEvents() |
1065 QApplication.processEvents() |
990 |
1066 |
991 with EricOverrideCursor(): |
1067 with EricOverrideCursor(): |
992 self.buf = [] |
1068 self.buf = [] |
993 self.cancelled = False |
1069 self.cancelled = False |
994 self.errors.clear() |
1070 self.errors.clear() |
995 self.intercept = False |
1071 self.intercept = False |
996 |
1072 |
997 if noEntries == 0: |
1073 if noEntries == 0: |
998 noEntries = self.limitSpinBox.value() |
1074 noEntries = self.limitSpinBox.value() |
999 |
1075 |
1000 preargs = [] |
1076 preargs = [] |
1001 args = self.vcs.initCommand(self.commandMode) |
1077 args = self.vcs.initCommand(self.commandMode) |
1002 args.append('--verbose') |
1078 args.append("--verbose") |
1003 if self.commandMode not in ("incoming", "outgoing"): |
1079 if self.commandMode not in ("incoming", "outgoing"): |
1004 args.append('--limit') |
1080 args.append("--limit") |
1005 args.append(str(noEntries)) |
1081 args.append(str(noEntries)) |
1006 if self.commandMode in ("incoming", "outgoing"): |
1082 if self.commandMode in ("incoming", "outgoing"): |
1007 args.append("--newest-first") |
1083 args.append("--newest-first") |
1008 if self.vcs.hasSubrepositories(): |
1084 if self.vcs.hasSubrepositories(): |
1009 args.append("--subrepos") |
1085 args.append("--subrepos") |
1010 if startRev is not None: |
1086 if startRev is not None: |
1011 args.append('--rev') |
1087 args.append("--rev") |
1012 args.append('{0}:0'.format(startRev)) |
1088 args.append("{0}:0".format(startRev)) |
1013 if ( |
1089 if not self.projectMode and not self.stopCheckBox.isChecked(): |
1014 not self.projectMode and |
1090 args.append("--follow") |
1015 not self.stopCheckBox.isChecked() |
|
1016 ): |
|
1017 args.append('--follow') |
|
1018 if self.commandMode == "log": |
1091 if self.commandMode == "log": |
1019 args.append('--copies') |
1092 args.append("--copies") |
1020 args.append('--template') |
1093 args.append("--template") |
1021 args.append(os.path.join(os.path.dirname(__file__), |
1094 args.append( |
1022 "templates", |
1095 os.path.join( |
1023 "logBrowserBookmarkPhase.tmpl")) |
1096 os.path.dirname(__file__), |
|
1097 "templates", |
|
1098 "logBrowserBookmarkPhase.tmpl", |
|
1099 ) |
|
1100 ) |
1024 if self.commandMode == "incoming": |
1101 if self.commandMode == "incoming": |
1025 if self.__bundle: |
1102 if self.__bundle: |
1026 args.append(self.__bundle) |
1103 args.append(self.__bundle) |
1027 elif not self.vcs.hasSubrepositories(): |
1104 elif not self.vcs.hasSubrepositories(): |
1028 project = ericApp().getObject("Project") |
1105 project = ericApp().getObject("Project") |
1029 self.vcs.bundleFile = os.path.join( |
1106 self.vcs.bundleFile = os.path.join( |
1030 project.getProjectManagementDir(), "hg-bundle.hg") |
1107 project.getProjectManagementDir(), "hg-bundle.hg" |
|
1108 ) |
1031 if os.path.exists(self.vcs.bundleFile): |
1109 if os.path.exists(self.vcs.bundleFile): |
1032 os.remove(self.vcs.bundleFile) |
1110 os.remove(self.vcs.bundleFile) |
1033 preargs = args[:] |
1111 preargs = args[:] |
1034 preargs.append("--quiet") |
1112 preargs.append("--quiet") |
1035 preargs.append('--bundle') |
1113 preargs.append("--bundle") |
1036 preargs.append(self.vcs.bundleFile) |
1114 preargs.append(self.vcs.bundleFile) |
1037 args.append(self.vcs.bundleFile) |
1115 args.append(self.vcs.bundleFile) |
1038 if not self.projectMode: |
1116 if not self.projectMode: |
1039 args.append(self.__filename) |
1117 args.append(self.__filename) |
1040 |
1118 |
1041 if preargs: |
1119 if preargs: |
1042 out, err = self.__hgClient.runcommand(preargs) |
1120 out, err = self.__hgClient.runcommand(preargs) |
1043 else: |
1121 else: |
1044 err = "" |
1122 err = "" |
1045 if err: |
1123 if err: |
1046 if ( |
1124 if ( |
1047 self.commandMode == "incoming" and |
1125 self.commandMode == "incoming" |
1048 self.initialCommandMode == "full_log" |
1126 and self.initialCommandMode == "full_log" |
1049 ): |
1127 ): |
1050 # ignore the error |
1128 # ignore the error |
1051 self.commandMode = "log" |
1129 self.commandMode = "log" |
1052 else: |
1130 else: |
1053 self.__showError(err) |
1131 self.__showError(err) |
1054 elif ( |
1132 elif ( |
1055 self.commandMode != "incoming" or |
1133 self.commandMode != "incoming" |
1056 (self.vcs.bundleFile and |
1134 or (self.vcs.bundleFile and os.path.exists(self.vcs.bundleFile)) |
1057 os.path.exists(self.vcs.bundleFile)) or |
1135 or self.__bundle |
1058 self.__bundle |
|
1059 ): |
1136 ): |
1060 out, err = self.__hgClient.runcommand(args) |
1137 out, err = self.__hgClient.runcommand(args) |
1061 self.buf = out.splitlines(True) |
1138 self.buf = out.splitlines(True) |
1062 if err: |
1139 if err: |
1063 self.__showError(err) |
1140 self.__showError(err) |
1064 self.__processBuffer() |
1141 self.__processBuffer() |
1065 elif ( |
1142 elif ( |
1066 self.commandMode == "incoming" and |
1143 self.commandMode == "incoming" and self.initialCommandMode == "full_log" |
1067 self.initialCommandMode == "full_log" |
|
1068 ): |
1144 ): |
1069 # no incoming changesets, just switch to log mode |
1145 # no incoming changesets, just switch to log mode |
1070 self.commandMode = "log" |
1146 self.commandMode = "log" |
1071 self.__finish() |
1147 self.__finish() |
1072 |
1148 |
1073 def start(self, name=None, bundle=None, isFile=False, noEntries=0): |
1149 def start(self, name=None, bundle=None, isFile=False, noEntries=0): |
1074 """ |
1150 """ |
1075 Public slot to start the hg log command. |
1151 Public slot to start the hg log command. |
1076 |
1152 |
1077 @param name file/directory name to show the log for |
1153 @param name file/directory name to show the log for |
1078 @type str |
1154 @type str |
1079 @param bundle name of a bundle file |
1155 @param bundle name of a bundle file |
1080 @type str |
1156 @type str |
1081 @param isFile flag indicating log for a file is to be shown |
1157 @param isFile flag indicating log for a file is to be shown |
1261 self.__minDate = dt |
1349 self.__minDate = dt |
1262 noEntries += 1 |
1350 noEntries += 1 |
1263 log = {"message": [], "bookmarks": None, "phase": ""} |
1351 log = {"message": [], "bookmarks": None, "phase": ""} |
1264 changedPaths = [] |
1352 changedPaths = [] |
1265 fileCopies = {} |
1353 fileCopies = {} |
1266 |
1354 |
1267 self.__resizeColumnsLog() |
1355 self.__resizeColumnsLog() |
1268 |
1356 |
1269 if self.__started and not self.__finishCallbacks: |
1357 if self.__started and not self.__finishCallbacks: |
1270 # we are really done |
1358 # we are really done |
1271 if self.__selectedRevisions: |
1359 if self.__selectedRevisions: |
1272 foundItems = self.logTree.findItems( |
1360 foundItems = self.logTree.findItems( |
1273 self.__selectedRevisions[0], Qt.MatchFlag.MatchExactly, |
1361 self.__selectedRevisions[0], |
1274 self.RevisionColumn) |
1362 Qt.MatchFlag.MatchExactly, |
|
1363 self.RevisionColumn, |
|
1364 ) |
1275 if foundItems: |
1365 if foundItems: |
1276 self.logTree.setCurrentItem(foundItems[0]) |
1366 self.logTree.setCurrentItem(foundItems[0]) |
1277 else: |
1367 else: |
1278 self.logTree.setCurrentItem( |
1368 self.logTree.setCurrentItem(self.logTree.topLevelItem(0)) |
1279 self.logTree.topLevelItem(0)) |
|
1280 elif self.__projectWorkingDirParents: |
1369 elif self.__projectWorkingDirParents: |
1281 for rev in self.__projectWorkingDirParents: |
1370 for rev in self.__projectWorkingDirParents: |
1282 # rev string format must match with the format of the |
1371 # rev string format must match with the format of the |
1283 # __generateLogItem() method |
1372 # __generateLogItem() method |
1284 items = self.logTree.findItems( |
1373 items = self.logTree.findItems( |
1285 "{0:>7}:".format(rev), |
1374 "{0:>7}:".format(rev), |
1286 Qt.MatchFlag.MatchStartsWith, |
1375 Qt.MatchFlag.MatchStartsWith, |
1287 self.RevisionColumn) |
1376 self.RevisionColumn, |
|
1377 ) |
1288 if items: |
1378 if items: |
1289 self.logTree.setCurrentItem(items[0]) |
1379 self.logTree.setCurrentItem(items[0]) |
1290 break |
1380 break |
1291 else: |
1381 else: |
1292 self.logTree.setCurrentItem( |
1382 self.logTree.setCurrentItem(self.logTree.topLevelItem(0)) |
1293 self.logTree.topLevelItem(0)) |
|
1294 else: |
1383 else: |
1295 self.logTree.setCurrentItem(self.logTree.topLevelItem(0)) |
1384 self.logTree.setCurrentItem(self.logTree.topLevelItem(0)) |
1296 self.__started = False |
1385 self.__started = False |
1297 |
1386 |
1298 if self.commandMode in ("incoming", "outgoing"): |
1387 if self.commandMode in ("incoming", "outgoing"): |
1299 self.commandMode = "log" # switch to log mode |
1388 self.commandMode = "log" # switch to log mode |
1300 if self.__lastRev > 0: |
1389 if self.__lastRev > 0: |
1301 self.nextButton.setEnabled(True) |
1390 self.nextButton.setEnabled(True) |
1302 self.limitSpinBox.setEnabled(True) |
1391 self.limitSpinBox.setEnabled(True) |
1303 else: |
1392 else: |
1304 if noEntries < self.limitSpinBox.value() and not self.cancelled: |
1393 if noEntries < self.limitSpinBox.value() and not self.cancelled: |
1305 self.nextButton.setEnabled(False) |
1394 self.nextButton.setEnabled(False) |
1306 self.limitSpinBox.setEnabled(False) |
1395 self.limitSpinBox.setEnabled(False) |
1307 |
1396 |
1308 # update the log filters |
1397 # update the log filters |
1309 self.__filterLogsEnabled = False |
1398 self.__filterLogsEnabled = False |
1310 self.fromDate.setMinimumDate(self.__minDate) |
1399 self.fromDate.setMinimumDate(self.__minDate) |
1311 self.fromDate.setMaximumDate(self.__maxDate) |
1400 self.fromDate.setMaximumDate(self.__maxDate) |
1312 self.fromDate.setDate(self.__minDate) |
1401 self.fromDate.setDate(self.__minDate) |
1313 self.toDate.setMinimumDate(self.__minDate) |
1402 self.toDate.setMinimumDate(self.__minDate) |
1314 self.toDate.setMaximumDate(self.__maxDate) |
1403 self.toDate.setMaximumDate(self.__maxDate) |
1315 self.toDate.setDate(self.__maxDate) |
1404 self.toDate.setDate(self.__maxDate) |
1316 |
1405 |
1317 branchFilter = self.branchCombo.currentText() |
1406 branchFilter = self.branchCombo.currentText() |
1318 if not branchFilter: |
1407 if not branchFilter: |
1319 branchFilter = self.__allBranchesFilter |
1408 branchFilter = self.__allBranchesFilter |
1320 self.branchCombo.clear() |
1409 self.branchCombo.clear() |
1321 self.branchCombo.addItems( |
1410 self.branchCombo.addItems( |
1322 [self.__allBranchesFilter] + sorted(self.__branchColors.keys())) |
1411 [self.__allBranchesFilter] + sorted(self.__branchColors.keys()) |
1323 self.branchCombo.setCurrentIndex( |
1412 ) |
1324 self.branchCombo.findText(branchFilter)) |
1413 self.branchCombo.setCurrentIndex(self.branchCombo.findText(branchFilter)) |
1325 |
1414 |
1326 self.__filterLogsEnabled = True |
1415 self.__filterLogsEnabled = True |
1327 if self.__actionMode() == "filter": |
1416 if self.__actionMode() == "filter": |
1328 self.__filterLogs() |
1417 self.__filterLogs() |
1329 self.__updateToolMenuActions() |
1418 self.__updateToolMenuActions() |
1330 |
1419 |
1331 # restore selected item |
1420 # restore selected item |
1332 if self.__selectedRevisions and not self.__finishCallbacks: |
1421 if self.__selectedRevisions and not self.__finishCallbacks: |
1333 # we are really done |
1422 # we are really done |
1334 for revision in self.__selectedRevisions: |
1423 for revision in self.__selectedRevisions: |
1335 items = self.logTree.findItems( |
1424 items = self.logTree.findItems( |
1336 revision, Qt.MatchFlag.MatchExactly, self.RevisionColumn) |
1425 revision, Qt.MatchFlag.MatchExactly, self.RevisionColumn |
|
1426 ) |
1337 if items: |
1427 if items: |
1338 items[0].setSelected(True) |
1428 items[0].setSelected(True) |
1339 self.__selectedRevisions = [] |
1429 self.__selectedRevisions = [] |
1340 |
1430 |
1341 def __showError(self, out): |
1431 def __showError(self, out): |
1342 """ |
1432 """ |
1343 Private slot to show some error. |
1433 Private slot to show some error. |
1344 |
1434 |
1345 @param out error to be shown (string) |
1435 @param out error to be shown (string) |
1346 """ |
1436 """ |
1347 self.errorGroup.show() |
1437 self.errorGroup.show() |
1348 self.errors.insertPlainText(out) |
1438 self.errors.insertPlainText(out) |
1349 self.errors.ensureCursorVisible() |
1439 self.errors.ensureCursorVisible() |
1350 |
1440 |
1351 def on_buttonBox_clicked(self, button): |
1441 def on_buttonBox_clicked(self, button): |
1352 """ |
1442 """ |
1353 Private slot called by a button of the button box clicked. |
1443 Private slot called by a button of the button box clicked. |
1354 |
1444 |
1355 @param button button that was clicked (QAbstractButton) |
1445 @param button button that was clicked (QAbstractButton) |
1356 """ |
1446 """ |
1357 if button == self.buttonBox.button( |
1447 if button == self.buttonBox.button(QDialogButtonBox.StandardButton.Close): |
1358 QDialogButtonBox.StandardButton.Close |
|
1359 ): |
|
1360 self.close() |
1448 self.close() |
1361 elif button == self.buttonBox.button( |
1449 elif button == self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel): |
1362 QDialogButtonBox.StandardButton.Cancel |
|
1363 ): |
|
1364 self.cancelled = True |
1450 self.cancelled = True |
1365 self.__hgClient.cancel() |
1451 self.__hgClient.cancel() |
1366 elif button == self.refreshButton: |
1452 elif button == self.refreshButton: |
1367 self.on_refreshButton_clicked() |
1453 self.on_refreshButton_clicked() |
1368 |
1454 |
1369 def __updateSbsSelectLabel(self): |
1455 def __updateSbsSelectLabel(self): |
1370 """ |
1456 """ |
1371 Private slot to update the enabled status of the diff buttons. |
1457 Private slot to update the enabled status of the diff buttons. |
1372 """ |
1458 """ |
1373 self.sbsSelectLabel.clear() |
1459 self.sbsSelectLabel.clear() |
1374 if self.__isFile: |
1460 if self.__isFile: |
1375 selectedItems = self.logTree.selectedItems() |
1461 selectedItems = self.logTree.selectedItems() |
1376 if len(selectedItems) == 1: |
1462 if len(selectedItems) == 1: |
1377 currentItem = selectedItems[0] |
1463 currentItem = selectedItems[0] |
1378 rev2 = ( |
1464 rev2 = currentItem.text(self.RevisionColumn).split(":", 1)[0].strip() |
1379 currentItem.text(self.RevisionColumn).split(":", 1)[0] |
|
1380 .strip() |
|
1381 ) |
|
1382 parents = currentItem.data(0, self.__parentsRole) |
1465 parents = currentItem.data(0, self.__parentsRole) |
1383 if parents: |
1466 if parents: |
1384 parentLinks = [] |
1467 parentLinks = [] |
1385 for index in range(len(parents)): |
1468 for index in range(len(parents)): |
1386 parentLinks.append( |
1469 parentLinks.append( |
1387 '<a href="sbsdiff:{0}_{1}"> {2} </a>' |
1470 '<a href="sbsdiff:{0}_{1}"> {2} </a>'.format( |
1388 .format(parents[index], rev2, index + 1)) |
1471 parents[index], rev2, index + 1 |
|
1472 ) |
|
1473 ) |
1389 self.sbsSelectLabel.setText( |
1474 self.sbsSelectLabel.setText( |
1390 self.tr('Side-by-Side Diff to Parent {0}').format( |
1475 self.tr("Side-by-Side Diff to Parent {0}").format( |
1391 " ".join(parentLinks))) |
1476 " ".join(parentLinks) |
|
1477 ) |
|
1478 ) |
1392 elif len(selectedItems) == 2: |
1479 elif len(selectedItems) == 2: |
1393 rev1 = int(selectedItems[0].text(self.RevisionColumn) |
1480 rev1 = int(selectedItems[0].text(self.RevisionColumn).split(":", 1)[0]) |
1394 .split(":", 1)[0]) |
1481 rev2 = int(selectedItems[1].text(self.RevisionColumn).split(":", 1)[0]) |
1395 rev2 = int(selectedItems[1].text(self.RevisionColumn) |
|
1396 .split(":", 1)[0]) |
|
1397 if rev1 > rev2: |
1482 if rev1 > rev2: |
1398 # Swap the entries, so that rev1 < rev2 |
1483 # Swap the entries, so that rev1 < rev2 |
1399 rev1, rev2 = rev2, rev1 |
1484 rev1, rev2 = rev2, rev1 |
1400 self.sbsSelectLabel.setText(self.tr( |
1485 self.sbsSelectLabel.setText( |
1401 '<a href="sbsdiff:{0}_{1}">Side-by-Side Compare</a>') |
1486 self.tr( |
1402 .format(rev1, rev2)) |
1487 '<a href="sbsdiff:{0}_{1}">Side-by-Side Compare</a>' |
1403 |
1488 ).format(rev1, rev2) |
|
1489 ) |
|
1490 |
1404 def __updateToolMenuActions(self): |
1491 def __updateToolMenuActions(self): |
1405 """ |
1492 """ |
1406 Private slot to update the status of the tool menu actions and |
1493 Private slot to update the status of the tool menu actions and |
1407 the tool menu button. |
1494 the tool menu button. |
1408 """ |
1495 """ |
1410 # do the phase action |
1497 # do the phase action |
1411 # step 1: count entries with changeable phases |
1498 # step 1: count entries with changeable phases |
1412 secret = 0 |
1499 secret = 0 |
1413 draft = 0 |
1500 draft = 0 |
1414 public = 0 |
1501 public = 0 |
1415 for itm in [item for item in self.logTree.selectedItems() |
1502 for itm in [ |
1416 if not item.data(0, self.__incomingRole)]: |
1503 item |
|
1504 for item in self.logTree.selectedItems() |
|
1505 if not item.data(0, self.__incomingRole) |
|
1506 ]: |
1417 # count phase for local items only |
1507 # count phase for local items only |
1418 phase = itm.text(self.PhaseColumn) |
1508 phase = itm.text(self.PhaseColumn) |
1419 if phase == self.phases["draft"]: |
1509 if phase == self.phases["draft"]: |
1420 draft += 1 |
1510 draft += 1 |
1421 elif phase == self.phases["secret"]: |
1511 elif phase == self.phases["secret"]: |
1422 secret += 1 |
1512 secret += 1 |
1423 else: |
1513 else: |
1424 public += 1 |
1514 public += 1 |
1425 |
1515 |
1426 # step 2: set the status of the phase action |
1516 # step 2: set the status of the phase action |
1427 if ( |
1517 if public == 0 and ( |
1428 public == 0 and |
1518 (secret > 0 and draft == 0) or (secret == 0 and draft > 0) |
1429 ((secret > 0 and draft == 0) or |
|
1430 (secret == 0 and draft > 0)) |
|
1431 ): |
1519 ): |
1432 self.__phaseAct.setEnabled(True) |
1520 self.__phaseAct.setEnabled(True) |
1433 else: |
1521 else: |
1434 self.__phaseAct.setEnabled(False) |
1522 self.__phaseAct.setEnabled(False) |
1435 |
1523 |
1436 # do the graft action |
1524 # do the graft action |
1437 # step 1: count selected entries not belonging to the |
1525 # step 1: count selected entries not belonging to the |
1438 # current branch |
1526 # current branch |
1439 otherBranches = 0 |
1527 otherBranches = 0 |
1440 for itm in [item for item in self.logTree.selectedItems() |
1528 for itm in [ |
1441 if not item.data(0, self.__incomingRole)]: |
1529 item |
|
1530 for item in self.logTree.selectedItems() |
|
1531 if not item.data(0, self.__incomingRole) |
|
1532 ]: |
1442 # for local items only |
1533 # for local items only |
1443 branch = itm.text(self.BranchColumn) |
1534 branch = itm.text(self.BranchColumn) |
1444 if branch != self.__projectBranch: |
1535 if branch != self.__projectBranch: |
1445 otherBranches += 1 |
1536 otherBranches += 1 |
1446 |
1537 |
1447 # step 2: set the status of the graft action |
1538 # step 2: set the status of the graft action |
1448 self.__graftAct.setEnabled(otherBranches > 0) |
1539 self.__graftAct.setEnabled(otherBranches > 0) |
1449 |
1540 |
1450 selectedItemsCount = len([ |
1541 selectedItemsCount = len( |
1451 itm for itm in self.logTree.selectedItems() |
1542 [ |
1452 if not itm.data(0, self.__incomingRole) |
1543 itm |
1453 ]) |
1544 for itm in self.logTree.selectedItems() |
1454 selectedIncomingItemsCount = len([ |
1545 if not itm.data(0, self.__incomingRole) |
1455 itm for itm in self.logTree.selectedItems() |
1546 ] |
1456 if itm.data(0, self.__incomingRole) |
1547 ) |
1457 ]) |
1548 selectedIncomingItemsCount = len( |
1458 |
1549 [ |
|
1550 itm |
|
1551 for itm in self.logTree.selectedItems() |
|
1552 if itm.data(0, self.__incomingRole) |
|
1553 ] |
|
1554 ) |
|
1555 |
1459 self.__mergeAct.setEnabled(selectedItemsCount == 1) |
1556 self.__mergeAct.setEnabled(selectedItemsCount == 1) |
1460 self.__tagAct.setEnabled(selectedItemsCount == 1) |
1557 self.__tagAct.setEnabled(selectedItemsCount == 1) |
1461 self.__switchAct.setEnabled(selectedItemsCount == 1) |
1558 self.__switchAct.setEnabled(selectedItemsCount == 1) |
1462 self.__bookmarkAct.setEnabled(selectedItemsCount == 1) |
1559 self.__bookmarkAct.setEnabled(selectedItemsCount == 1) |
1463 self.__bookmarkMoveAct.setEnabled(selectedItemsCount == 1) |
1560 self.__bookmarkMoveAct.setEnabled(selectedItemsCount == 1) |
1464 |
1561 |
1465 if selectedIncomingItemsCount > 0: |
1562 if selectedIncomingItemsCount > 0: |
1466 self.__pullAct.setText(self.tr("Pull Selected Changes")) |
1563 self.__pullAct.setText(self.tr("Pull Selected Changes")) |
1467 else: |
1564 else: |
1468 self.__pullAct.setText(self.tr("Pull Changes")) |
1565 self.__pullAct.setText(self.tr("Pull Changes")) |
1469 if self.vcs.canPull(): |
1566 if self.vcs.canPull(): |
1470 self.__pullAct.setEnabled(True) |
1567 self.__pullAct.setEnabled(True) |
1471 self.__lfPullAct.setEnabled( |
1568 self.__lfPullAct.setEnabled( |
1472 self.vcs.isExtensionActive("largefiles") and |
1569 self.vcs.isExtensionActive("largefiles") and selectedItemsCount > 0 |
1473 selectedItemsCount > 0) |
1570 ) |
1474 else: |
1571 else: |
1475 self.__pullAct.setEnabled(False) |
1572 self.__pullAct.setEnabled(False) |
1476 self.__lfPullAct.setEnabled(False) |
1573 self.__lfPullAct.setEnabled(False) |
1477 |
1574 |
1478 if self.vcs.canPush(): |
1575 if self.vcs.canPush(): |
1479 self.__pushAct.setEnabled( |
1576 self.__pushAct.setEnabled( |
1480 selectedItemsCount == 1 and |
1577 selectedItemsCount == 1 |
1481 not self.logTree.selectedItems()[0].data( |
1578 and not self.logTree.selectedItems()[0].data(0, self.__incomingRole) |
1482 0, self.__incomingRole) and |
1579 and self.logTree.selectedItems()[0].text(self.PhaseColumn) |
1483 self.logTree.selectedItems()[0].text(self.PhaseColumn) == |
1580 == self.phases["draft"] |
1484 self.phases["draft"]) |
1581 ) |
1485 self.__pushAllAct.setEnabled(True) |
1582 self.__pushAllAct.setEnabled(True) |
1486 else: |
1583 else: |
1487 self.__pushAct.setEnabled(False) |
1584 self.__pushAct.setEnabled(False) |
1488 self.__pushAllAct.setEnabled(False) |
1585 self.__pushAllAct.setEnabled(False) |
1489 |
1586 |
1490 self.__stripAct.setEnabled( |
1587 self.__stripAct.setEnabled( |
1491 self.vcs.isExtensionActive("strip") and |
1588 self.vcs.isExtensionActive("strip") and selectedItemsCount == 1 |
1492 selectedItemsCount == 1) |
1589 ) |
1493 |
1590 |
1494 # count incoming items for 'full_log' |
1591 # count incoming items for 'full_log' |
1495 if self.initialCommandMode == "full_log": |
1592 if self.initialCommandMode == "full_log": |
1496 # incoming items are at the top |
1593 # incoming items are at the top |
1497 incomingCount = 0 |
1594 incomingCount = 0 |
1498 for row in range(self.logTree.topLevelItemCount()): |
1595 for row in range(self.logTree.topLevelItemCount()): |
1499 if self.logTree.topLevelItem(row).data( |
1596 if self.logTree.topLevelItem(row).data(0, self.__incomingRole): |
1500 0, self.__incomingRole): |
|
1501 incomingCount += 1 |
1597 incomingCount += 1 |
1502 else: |
1598 else: |
1503 break |
1599 break |
1504 localCount = self.logTree.topLevelItemCount() - incomingCount |
1600 localCount = self.logTree.topLevelItemCount() - incomingCount |
1505 else: |
1601 else: |
1506 localCount = self.logTree.topLevelItemCount() |
1602 localCount = self.logTree.topLevelItemCount() |
1507 self.__bundleAct.setEnabled(localCount > 0) |
1603 self.__bundleAct.setEnabled(localCount > 0) |
1508 self.__unbundleAct.setEnabled(False) |
1604 self.__unbundleAct.setEnabled(False) |
1509 |
1605 |
1510 self.__gpgSignAct.setEnabled( |
1606 self.__gpgSignAct.setEnabled( |
1511 self.vcs.isExtensionActive("gpg") and |
1607 self.vcs.isExtensionActive("gpg") and selectedItemsCount > 0 |
1512 selectedItemsCount > 0) |
1608 ) |
1513 self.__gpgVerifyAct.setEnabled( |
1609 self.__gpgVerifyAct.setEnabled( |
1514 self.vcs.isExtensionActive("gpg") and |
1610 self.vcs.isExtensionActive("gpg") and selectedItemsCount == 1 |
1515 selectedItemsCount == 1) |
1611 ) |
1516 |
1612 |
1517 if self.vcs.isExtensionActive("closehead"): |
1613 if self.vcs.isExtensionActive("closehead"): |
1518 revs = [itm.text(self.RevisionColumn).strip().split(":", 1)[0] |
1614 revs = [ |
1519 for itm in self.logTree.selectedItems() |
1615 itm.text(self.RevisionColumn).strip().split(":", 1)[0] |
1520 if not itm.data(0, self.__incomingRole)] |
1616 for itm in self.logTree.selectedItems() |
|
1617 if not itm.data(0, self.__incomingRole) |
|
1618 ] |
1521 revs = [rev for rev in revs if rev in self.__headRevisions] |
1619 revs = [rev for rev in revs if rev in self.__headRevisions] |
1522 self.__closeHeadsAct.setEnabled(len(revs) > 0) |
1620 self.__closeHeadsAct.setEnabled(len(revs) > 0) |
1523 else: |
1621 else: |
1524 self.__closeHeadsAct.setEnabled(False) |
1622 self.__closeHeadsAct.setEnabled(False) |
1525 self.actionsButton.setEnabled(True) |
1623 self.actionsButton.setEnabled(True) |
1526 |
1624 |
1527 elif self.initialCommandMode == "incoming" and self.projectMode: |
1625 elif self.initialCommandMode == "incoming" and self.projectMode: |
1528 for act in [self.__phaseAct, self.__graftAct, self.__mergeAct, |
1626 for act in [ |
1529 self.__tagAct, self.__closeHeadsAct, self.__switchAct, |
1627 self.__phaseAct, |
1530 self.__bookmarkAct, self.__bookmarkMoveAct, |
1628 self.__graftAct, |
1531 self.__pushAct, self.__pushAllAct, self.__stripAct, |
1629 self.__mergeAct, |
1532 self.__bundleAct, self.__gpgSignAct, |
1630 self.__tagAct, |
1533 self.__gpgVerifyAct]: |
1631 self.__closeHeadsAct, |
|
1632 self.__switchAct, |
|
1633 self.__bookmarkAct, |
|
1634 self.__bookmarkMoveAct, |
|
1635 self.__pushAct, |
|
1636 self.__pushAllAct, |
|
1637 self.__stripAct, |
|
1638 self.__bundleAct, |
|
1639 self.__gpgSignAct, |
|
1640 self.__gpgVerifyAct, |
|
1641 ]: |
1534 act.setEnabled(False) |
1642 act.setEnabled(False) |
1535 |
1643 |
1536 self.__pullAct.setText(self.tr("Pull Selected Changes")) |
1644 self.__pullAct.setText(self.tr("Pull Selected Changes")) |
1537 if self.vcs.canPull() and not bool(self.__bundle): |
1645 if self.vcs.canPull() and not bool(self.__bundle): |
1538 selectedIncomingItemsCount = len([ |
1646 selectedIncomingItemsCount = len( |
1539 itm for itm in self.logTree.selectedItems() |
1647 [ |
1540 if itm.data(0, self.__incomingRole) |
1648 itm |
1541 ]) |
1649 for itm in self.logTree.selectedItems() |
|
1650 if itm.data(0, self.__incomingRole) |
|
1651 ] |
|
1652 ) |
1542 self.__pullAct.setEnabled(selectedIncomingItemsCount > 0) |
1653 self.__pullAct.setEnabled(selectedIncomingItemsCount > 0) |
1543 self.__lfPullAct.setEnabled( |
1654 self.__lfPullAct.setEnabled( |
1544 self.vcs.isExtensionActive("largefiles") and |
1655 self.vcs.isExtensionActive("largefiles") |
1545 selectedIncomingItemsCount > 0) |
1656 and selectedIncomingItemsCount > 0 |
|
1657 ) |
1546 else: |
1658 else: |
1547 self.__pullAct.setEnabled(False) |
1659 self.__pullAct.setEnabled(False) |
1548 self.__lfPullAct.setEnabled(False) |
1660 self.__lfPullAct.setEnabled(False) |
1549 |
1661 |
1550 self.__unbundleAct.setEnabled(bool(self.__bundle)) |
1662 self.__unbundleAct.setEnabled(bool(self.__bundle)) |
1551 |
1663 |
1552 self.actionsButton.setEnabled(True) |
1664 self.actionsButton.setEnabled(True) |
1553 |
1665 |
1554 elif self.initialCommandMode == "outgoing" and self.projectMode: |
1666 elif self.initialCommandMode == "outgoing" and self.projectMode: |
1555 for act in [self.__phaseAct, self.__graftAct, self.__mergeAct, |
1667 for act in [ |
1556 self.__tagAct, self.__closeHeadsAct, self.__switchAct, |
1668 self.__phaseAct, |
1557 self.__bookmarkAct, self.__bookmarkMoveAct, |
1669 self.__graftAct, |
1558 self.__pullAct, self.__lfPullAct, |
1670 self.__mergeAct, |
1559 self.__stripAct, self.__gpgSignAct, |
1671 self.__tagAct, |
1560 self.__gpgVerifyAct, self.__unbundleAct]: |
1672 self.__closeHeadsAct, |
|
1673 self.__switchAct, |
|
1674 self.__bookmarkAct, |
|
1675 self.__bookmarkMoveAct, |
|
1676 self.__pullAct, |
|
1677 self.__lfPullAct, |
|
1678 self.__stripAct, |
|
1679 self.__gpgSignAct, |
|
1680 self.__gpgVerifyAct, |
|
1681 self.__unbundleAct, |
|
1682 ]: |
1561 act.setEnabled(False) |
1683 act.setEnabled(False) |
1562 |
1684 |
1563 selectedItemsCount = len(self.logTree.selectedItems()) |
1685 selectedItemsCount = len(self.logTree.selectedItems()) |
1564 if self.vcs.canPush(): |
1686 if self.vcs.canPush(): |
1565 self.__pushAct.setEnabled( |
1687 self.__pushAct.setEnabled( |
1566 selectedItemsCount == 1 and |
1688 selectedItemsCount == 1 |
1567 self.logTree.selectedItems()[0].text(self.PhaseColumn) == |
1689 and self.logTree.selectedItems()[0].text(self.PhaseColumn) |
1568 self.phases["draft"]) |
1690 == self.phases["draft"] |
|
1691 ) |
1569 self.__pushAllAct.setEnabled(True) |
1692 self.__pushAllAct.setEnabled(True) |
1570 else: |
1693 else: |
1571 self.__pushAct.setEnabled(False) |
1694 self.__pushAct.setEnabled(False) |
1572 self.__pushAllAct.setEnabled(False) |
1695 self.__pushAllAct.setEnabled(False) |
1573 |
1696 |
1574 self.__bundleAct.setEnabled(selectedItemsCount > 0) |
1697 self.__bundleAct.setEnabled(selectedItemsCount > 0) |
1575 |
1698 |
1576 else: |
1699 else: |
1577 self.actionsButton.setEnabled(False) |
1700 self.actionsButton.setEnabled(False) |
1578 |
1701 |
1579 def __updateDetailsAndFiles(self): |
1702 def __updateDetailsAndFiles(self): |
1580 """ |
1703 """ |
1581 Private slot to update the details and file changes panes. |
1704 Private slot to update the details and file changes panes. |
1582 """ |
1705 """ |
1583 self.detailsEdit.clear() |
1706 self.detailsEdit.clear() |
1584 self.filesTree.clear() |
1707 self.filesTree.clear() |
1585 self.__diffUpdatesFiles = False |
1708 self.__diffUpdatesFiles = False |
1586 |
1709 |
1587 selectedItems = self.logTree.selectedItems() |
1710 selectedItems = self.logTree.selectedItems() |
1588 if len(selectedItems) == 1: |
1711 if len(selectedItems) == 1: |
1589 self.detailsEdit.setHtml( |
1712 self.detailsEdit.setHtml(self.__generateDetailsTableText(selectedItems[0])) |
1590 self.__generateDetailsTableText(selectedItems[0])) |
|
1591 self.__updateFilesTree(self.filesTree, selectedItems[0]) |
1713 self.__updateFilesTree(self.filesTree, selectedItems[0]) |
1592 self.__resizeColumnsFiles() |
1714 self.__resizeColumnsFiles() |
1593 self.__resortFiles() |
1715 self.__resortFiles() |
1594 elif len(selectedItems) == 2: |
1716 elif len(selectedItems) == 2: |
1595 self.__diffUpdatesFiles = True |
1717 self.__diffUpdatesFiles = True |
1596 index1 = self.logTree.indexOfTopLevelItem(selectedItems[0]) |
1718 index1 = self.logTree.indexOfTopLevelItem(selectedItems[0]) |
1597 index2 = self.logTree.indexOfTopLevelItem(selectedItems[1]) |
1719 index2 = self.logTree.indexOfTopLevelItem(selectedItems[1]) |
1598 if index1 > index2: |
1720 if index1 > index2: |
1599 # Swap the entries |
1721 # Swap the entries |
1600 selectedItems[0], selectedItems[1] = ( |
1722 selectedItems[0], selectedItems[1] = ( |
1601 selectedItems[1], selectedItems[0] |
1723 selectedItems[1], |
|
1724 selectedItems[0], |
1602 ) |
1725 ) |
1603 html = "{0}<hr/>{1}".format( |
1726 html = "{0}<hr/>{1}".format( |
1604 self.__generateDetailsTableText(selectedItems[0]), |
1727 self.__generateDetailsTableText(selectedItems[0]), |
1605 self.__generateDetailsTableText(selectedItems[1]), |
1728 self.__generateDetailsTableText(selectedItems[1]), |
1606 ) |
1729 ) |
1607 self.detailsEdit.setHtml(html) |
1730 self.detailsEdit.setHtml(html) |
1608 # self.filesTree is updated by the diff |
1731 # self.filesTree is updated by the diff |
1609 |
1732 |
1610 def __generateDetailsTableText(self, itm): |
1733 def __generateDetailsTableText(self, itm): |
1611 """ |
1734 """ |
1612 Private method to generate an HTML table with the details of the given |
1735 Private method to generate an HTML table with the details of the given |
1613 changeset. |
1736 changeset. |
1614 |
1737 |
1615 @param itm reference to the item the table should be based on |
1738 @param itm reference to the item the table should be based on |
1616 @type QTreeWidgetItem |
1739 @type QTreeWidgetItem |
1617 @return HTML table containing details |
1740 @return HTML table containing details |
1618 @rtype str |
1741 @rtype str |
1619 """ |
1742 """ |
1620 if itm is not None: |
1743 if itm is not None: |
1621 if itm.text(self.TagsColumn): |
1744 if itm.text(self.TagsColumn): |
1622 tagsStr = self.__tagsTemplate.format(itm.text(self.TagsColumn)) |
1745 tagsStr = self.__tagsTemplate.format(itm.text(self.TagsColumn)) |
1623 else: |
1746 else: |
1624 tagsStr = "" |
1747 tagsStr = "" |
1625 |
1748 |
1626 if itm.text(self.BookmarksColumn): |
1749 if itm.text(self.BookmarksColumn): |
1627 bookmarksStr = self.__bookmarksTemplate.format( |
1750 bookmarksStr = self.__bookmarksTemplate.format( |
1628 itm.text(self.BookmarksColumn)) |
1751 itm.text(self.BookmarksColumn) |
|
1752 ) |
1629 else: |
1753 else: |
1630 bookmarksStr = "" |
1754 bookmarksStr = "" |
1631 |
1755 |
1632 if self.projectMode and itm.data(0, self.__latestTagRole): |
1756 if self.projectMode and itm.data(0, self.__latestTagRole): |
1633 latestTagLinks = [] |
1757 latestTagLinks = [] |
1634 for tag in itm.data(0, self.__latestTagRole): |
1758 for tag in itm.data(0, self.__latestTagRole): |
1635 latestTagLinks.append('<a href="rev:{0}">{1}</a>'.format( |
1759 latestTagLinks.append( |
1636 self.__getRevisionOfTag(tag)[0], tag)) |
1760 '<a href="rev:{0}">{1}</a>'.format( |
|
1761 self.__getRevisionOfTag(tag)[0], tag |
|
1762 ) |
|
1763 ) |
1637 latestTagStr = self.__latestTagTemplate.format( |
1764 latestTagStr = self.__latestTagTemplate.format( |
1638 ", ".join(latestTagLinks)) |
1765 ", ".join(latestTagLinks) |
|
1766 ) |
1639 else: |
1767 else: |
1640 latestTagStr = "" |
1768 latestTagStr = "" |
1641 |
1769 |
1642 rev = int(itm.text(self.RevisionColumn).split(":", 1)[0]) |
1770 rev = int(itm.text(self.RevisionColumn).split(":", 1)[0]) |
1643 |
1771 |
1644 if itm.data(0, self.__parentsRole): |
1772 if itm.data(0, self.__parentsRole): |
1645 parentLinks = [] |
1773 parentLinks = [] |
1646 for parent in [str(x) for x in |
1774 for parent in [str(x) for x in itm.data(0, self.__parentsRole)]: |
1647 itm.data(0, self.__parentsRole)]: |
1775 parentLinks.append('<a href="rev:{0}">{0}</a>'.format(parent)) |
1648 parentLinks.append( |
1776 parentsStr = self.__parentsTemplate.format(", ".join(parentLinks)) |
1649 '<a href="rev:{0}">{0}</a>'.format(parent)) |
|
1650 parentsStr = self.__parentsTemplate.format( |
|
1651 ", ".join(parentLinks)) |
|
1652 else: |
1777 else: |
1653 parentsStr = "" |
1778 parentsStr = "" |
1654 |
1779 |
1655 if self.__childrenInfo[rev]: |
1780 if self.__childrenInfo[rev]: |
1656 childLinks = [] |
1781 childLinks = [] |
1657 for child in [str(x) for x in self.__childrenInfo[rev]]: |
1782 for child in [str(x) for x in self.__childrenInfo[rev]]: |
1658 childLinks.append( |
1783 childLinks.append('<a href="rev:{0}">{0}</a>'.format(child)) |
1659 '<a href="rev:{0}">{0}</a>'.format(child)) |
1784 childrenStr = self.__childrenTemplate.format(", ".join(childLinks)) |
1660 childrenStr = self.__childrenTemplate.format( |
|
1661 ", ".join(childLinks)) |
|
1662 else: |
1785 else: |
1663 childrenStr = "" |
1786 childrenStr = "" |
1664 |
1787 |
1665 messagesList = [] |
1788 messagesList = [] |
1666 for line in itm.data(0, self.__messageRole): |
1789 for line in itm.data(0, self.__messageRole): |
1667 match = HgLogBrowserDialog.GraftedRe.fullmatch(line) |
1790 match = HgLogBrowserDialog.GraftedRe.fullmatch(line) |
1668 if match: |
1791 if match: |
1669 messagesList.append( |
1792 messagesList.append( |
1670 HgLogBrowserDialog.GraftedTemplate.format( |
1793 HgLogBrowserDialog.GraftedTemplate.format(match.group(1)) |
1671 match.group(1))) |
1794 ) |
1672 else: |
1795 else: |
1673 messagesList.append(Utilities.html_encode(line.strip())) |
1796 messagesList.append(Utilities.html_encode(line.strip())) |
1674 messageStr = "<br />\n".join(messagesList) |
1797 messageStr = "<br />\n".join(messagesList) |
1675 |
1798 |
1676 html = self.__detailsTemplate.format( |
1799 html = self.__detailsTemplate.format( |
1677 itm.text(self.RevisionColumn), |
1800 itm.text(self.RevisionColumn), |
1678 itm.text(self.DateColumn), |
1801 itm.text(self.DateColumn), |
1679 itm.text(self.AuthorColumn), |
1802 itm.text(self.AuthorColumn), |
1680 itm.text(self.BranchColumn).replace( |
1803 itm.text(self.BranchColumn).replace(self.ClosedIndicator, ""), |
1681 self.ClosedIndicator, ""), |
1804 parentsStr + childrenStr + tagsStr + latestTagStr + bookmarksStr, |
1682 parentsStr + childrenStr + tagsStr + latestTagStr + |
|
1683 bookmarksStr, |
|
1684 messageStr, |
1805 messageStr, |
1685 ) |
1806 ) |
1686 else: |
1807 else: |
1687 html = "" |
1808 html = "" |
1688 |
1809 |
1689 return html |
1810 return html |
1690 |
1811 |
1691 def __updateFilesTree(self, parent, itm): |
1812 def __updateFilesTree(self, parent, itm): |
1692 """ |
1813 """ |
1693 Private method to update the files tree with changes of the given item. |
1814 Private method to update the files tree with changes of the given item. |
1694 |
1815 |
1695 @param parent parent for the items to be added |
1816 @param parent parent for the items to be added |
1696 @type QTreeWidget or QTreeWidgetItem |
1817 @type QTreeWidget or QTreeWidgetItem |
1697 @param itm reference to the item the update should be based on |
1818 @param itm reference to the item the update should be based on |
1698 @type QTreeWidgetItem |
1819 @type QTreeWidgetItem |
1699 """ |
1820 """ |
1700 if itm is not None: |
1821 if itm is not None: |
1701 changes = itm.data(0, self.__changesRole) |
1822 changes = itm.data(0, self.__changesRole) |
1702 if len(changes) > 0: |
1823 if len(changes) > 0: |
1703 for change in changes: |
1824 for change in changes: |
1704 QTreeWidgetItem(parent, [ |
1825 QTreeWidgetItem( |
1705 self.flags[change["action"]], |
1826 parent, |
1706 change["path"].strip(), |
1827 [ |
1707 change["copyfrom"].strip(), |
1828 self.flags[change["action"]], |
1708 ]) |
1829 change["path"].strip(), |
1709 |
1830 change["copyfrom"].strip(), |
|
1831 ], |
|
1832 ) |
|
1833 |
1710 @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem) |
1834 @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem) |
1711 def on_logTree_currentItemChanged(self, current, previous): |
1835 def on_logTree_currentItemChanged(self, current, previous): |
1712 """ |
1836 """ |
1713 Private slot called, when the current item of the log tree changes. |
1837 Private slot called, when the current item of the log tree changes. |
1714 |
1838 |
1715 @param current reference to the new current item (QTreeWidgetItem) |
1839 @param current reference to the new current item (QTreeWidgetItem) |
1716 @param previous reference to the old current item (QTreeWidgetItem) |
1840 @param previous reference to the old current item (QTreeWidgetItem) |
1717 """ |
1841 """ |
1718 self.__updateToolMenuActions() |
1842 self.__updateToolMenuActions() |
1719 |
1843 |
1720 # Highlight the current entry using a bold font |
1844 # Highlight the current entry using a bold font |
1721 for col in range(self.logTree.columnCount()): |
1845 for col in range(self.logTree.columnCount()): |
1722 current and current.setFont(col, self.__logTreeBoldFont) |
1846 current and current.setFont(col, self.__logTreeBoldFont) |
1723 previous and previous.setFont(col, self.__logTreeNormalFont) |
1847 previous and previous.setFont(col, self.__logTreeNormalFont) |
1724 |
1848 |
1725 # set the state of the up and down buttons |
1849 # set the state of the up and down buttons |
1726 self.upButton.setEnabled( |
1850 self.upButton.setEnabled( |
1727 current is not None and |
1851 current is not None and self.logTree.indexOfTopLevelItem(current) > 0 |
1728 self.logTree.indexOfTopLevelItem(current) > 0) |
1852 ) |
1729 self.downButton.setEnabled( |
1853 self.downButton.setEnabled( |
1730 current is not None and |
1854 current is not None |
1731 int(current.text(self.RevisionColumn).split(":")[0]) > 0 and |
1855 and int(current.text(self.RevisionColumn).split(":")[0]) > 0 |
1732 (self.logTree.indexOfTopLevelItem(current) < |
1856 and ( |
1733 self.logTree.topLevelItemCount() - 1 or |
1857 self.logTree.indexOfTopLevelItem(current) |
1734 self.nextButton.isEnabled())) |
1858 < self.logTree.topLevelItemCount() - 1 |
1735 |
1859 or self.nextButton.isEnabled() |
|
1860 ) |
|
1861 ) |
|
1862 |
1736 @pyqtSlot() |
1863 @pyqtSlot() |
1737 def on_logTree_itemSelectionChanged(self): |
1864 def on_logTree_itemSelectionChanged(self): |
1738 """ |
1865 """ |
1739 Private slot called, when the selection has changed. |
1866 Private slot called, when the selection has changed. |
1740 """ |
1867 """ |
1741 self.__updateDetailsAndFiles() |
1868 self.__updateDetailsAndFiles() |
1742 self.__updateSbsSelectLabel() |
1869 self.__updateSbsSelectLabel() |
1743 self.__updateToolMenuActions() |
1870 self.__updateToolMenuActions() |
1744 self.__generateDiffs() |
1871 self.__generateDiffs() |
1745 |
1872 |
1746 @pyqtSlot() |
1873 @pyqtSlot() |
1747 def on_upButton_clicked(self): |
1874 def on_upButton_clicked(self): |
1748 """ |
1875 """ |
1749 Private slot to move the current item up one entry. |
1876 Private slot to move the current item up one entry. |
1750 """ |
1877 """ |
1751 itm = self.logTree.itemAbove(self.logTree.currentItem()) |
1878 itm = self.logTree.itemAbove(self.logTree.currentItem()) |
1752 if itm: |
1879 if itm: |
1753 self.logTree.setCurrentItem(itm) |
1880 self.logTree.setCurrentItem(itm) |
1754 |
1881 |
1755 @pyqtSlot() |
1882 @pyqtSlot() |
1756 def on_downButton_clicked(self): |
1883 def on_downButton_clicked(self): |
1757 """ |
1884 """ |
1758 Private slot to move the current item down one entry. |
1885 Private slot to move the current item down one entry. |
1759 """ |
1886 """ |
1763 else: |
1890 else: |
1764 # load the next bunch and try again |
1891 # load the next bunch and try again |
1765 if self.nextButton.isEnabled(): |
1892 if self.nextButton.isEnabled(): |
1766 self.__addFinishCallback(self.on_downButton_clicked) |
1893 self.__addFinishCallback(self.on_downButton_clicked) |
1767 self.on_nextButton_clicked() |
1894 self.on_nextButton_clicked() |
1768 |
1895 |
1769 @pyqtSlot() |
1896 @pyqtSlot() |
1770 def on_nextButton_clicked(self): |
1897 def on_nextButton_clicked(self): |
1771 """ |
1898 """ |
1772 Private slot to handle the Next button. |
1899 Private slot to handle the Next button. |
1773 """ |
1900 """ |
1774 if self.nextButton.isEnabled(): |
1901 if self.nextButton.isEnabled(): |
1775 if self.__lastRev > 0: |
1902 if self.__lastRev > 0: |
1776 self.__getLogEntries(startRev=self.__lastRev - 1) |
1903 self.__getLogEntries(startRev=self.__lastRev - 1) |
1777 else: |
1904 else: |
1778 self.__getLogEntries() |
1905 self.__getLogEntries() |
1779 |
1906 |
1780 @pyqtSlot(QDate) |
1907 @pyqtSlot(QDate) |
1781 def on_fromDate_dateChanged(self, date): |
1908 def on_fromDate_dateChanged(self, date): |
1782 """ |
1909 """ |
1783 Private slot called, when the from date changes. |
1910 Private slot called, when the from date changes. |
1784 |
1911 |
1785 @param date new date (QDate) |
1912 @param date new date (QDate) |
1786 """ |
1913 """ |
1787 if self.__actionMode() == "filter": |
1914 if self.__actionMode() == "filter": |
1788 self.__filterLogs() |
1915 self.__filterLogs() |
1789 |
1916 |
1790 @pyqtSlot(QDate) |
1917 @pyqtSlot(QDate) |
1791 def on_toDate_dateChanged(self, date): |
1918 def on_toDate_dateChanged(self, date): |
1792 """ |
1919 """ |
1793 Private slot called, when the from date changes. |
1920 Private slot called, when the from date changes. |
1794 |
1921 |
1795 @param date new date (QDate) |
1922 @param date new date (QDate) |
1796 """ |
1923 """ |
1797 if self.__actionMode() == "filter": |
1924 if self.__actionMode() == "filter": |
1798 self.__filterLogs() |
1925 self.__filterLogs() |
1799 |
1926 |
1800 @pyqtSlot(int) |
1927 @pyqtSlot(int) |
1801 def on_branchCombo_activated(self, index): |
1928 def on_branchCombo_activated(self, index): |
1802 """ |
1929 """ |
1803 Private slot called, when a new branch is selected. |
1930 Private slot called, when a new branch is selected. |
1804 |
1931 |
1805 @param index index of the selected entry |
1932 @param index index of the selected entry |
1806 @type int |
1933 @type int |
1807 """ |
1934 """ |
1808 if self.__actionMode() == "filter": |
1935 if self.__actionMode() == "filter": |
1809 self.__filterLogs() |
1936 self.__filterLogs() |
1810 |
1937 |
1811 @pyqtSlot(int) |
1938 @pyqtSlot(int) |
1812 def on_fieldCombo_activated(self, index): |
1939 def on_fieldCombo_activated(self, index): |
1813 """ |
1940 """ |
1814 Private slot called, when a new filter field is selected. |
1941 Private slot called, when a new filter field is selected. |
1815 |
1942 |
1816 @param index index of the selected entry |
1943 @param index index of the selected entry |
1817 @type int |
1944 @type int |
1818 """ |
1945 """ |
1819 if self.__actionMode() == "filter": |
1946 if self.__actionMode() == "filter": |
1820 self.__filterLogs() |
1947 self.__filterLogs() |
1821 |
1948 |
1822 @pyqtSlot(str) |
1949 @pyqtSlot(str) |
1823 def on_rxEdit_textChanged(self, txt): |
1950 def on_rxEdit_textChanged(self, txt): |
1824 """ |
1951 """ |
1825 Private slot called, when a filter expression is entered. |
1952 Private slot called, when a filter expression is entered. |
1826 |
1953 |
1827 @param txt filter expression (string) |
1954 @param txt filter expression (string) |
1828 """ |
1955 """ |
1829 if self.__actionMode() == "filter": |
1956 if self.__actionMode() == "filter": |
1830 self.__filterLogs() |
1957 self.__filterLogs() |
1831 elif self.__actionMode() == "find": |
1958 elif self.__actionMode() == "find": |
1832 self.__findItem(self.__findBackwards, interactive=True) |
1959 self.__findItem(self.__findBackwards, interactive=True) |
1833 |
1960 |
1834 @pyqtSlot() |
1961 @pyqtSlot() |
1835 def on_rxEdit_returnPressed(self): |
1962 def on_rxEdit_returnPressed(self): |
1836 """ |
1963 """ |
1837 Private slot handling a press of the Return key in the rxEdit input. |
1964 Private slot handling a press of the Return key in the rxEdit input. |
1838 """ |
1965 """ |
1839 if self.__actionMode() == "find": |
1966 if self.__actionMode() == "find": |
1840 self.__findItem(self.__findBackwards, interactive=True) |
1967 self.__findItem(self.__findBackwards, interactive=True) |
1841 |
1968 |
1842 def __filterLogs(self): |
1969 def __filterLogs(self): |
1843 """ |
1970 """ |
1844 Private method to filter the log entries. |
1971 Private method to filter the log entries. |
1845 """ |
1972 """ |
1846 if self.__filterLogsEnabled: |
1973 if self.__filterLogsEnabled: |
1847 from_ = self.fromDate.date().toString("yyyy-MM-dd") |
1974 from_ = self.fromDate.date().toString("yyyy-MM-dd") |
1848 to_ = self.toDate.date().addDays(1).toString("yyyy-MM-dd") |
1975 to_ = self.toDate.date().addDays(1).toString("yyyy-MM-dd") |
1849 branch = self.branchCombo.currentText() |
1976 branch = self.branchCombo.currentText() |
1850 closedBranch = branch + '--' |
1977 closedBranch = branch + "--" |
1851 fieldIndex, searchRx, indexIsRole = self.__prepareFieldSearch() |
1978 fieldIndex, searchRx, indexIsRole = self.__prepareFieldSearch() |
1852 |
1979 |
1853 visibleItemCount = self.logTree.topLevelItemCount() |
1980 visibleItemCount = self.logTree.topLevelItemCount() |
1854 currentItem = self.logTree.currentItem() |
1981 currentItem = self.logTree.currentItem() |
1855 for topIndex in range(self.logTree.topLevelItemCount()): |
1982 for topIndex in range(self.logTree.topLevelItemCount()): |
1856 topItem = self.logTree.topLevelItem(topIndex) |
1983 topItem = self.logTree.topLevelItem(topIndex) |
1857 if indexIsRole: |
1984 if indexIsRole: |
1858 if fieldIndex == self.__changesRole: |
1985 if fieldIndex == self.__changesRole: |
1859 changes = topItem.data(0, self.__changesRole) |
1986 changes = topItem.data(0, self.__changesRole) |
1860 txt = "\n".join( |
1987 txt = "\n".join( |
1861 [c["path"] for c in changes] + |
1988 [c["path"] for c in changes] |
1862 [c["copyfrom"] for c in changes] |
1989 + [c["copyfrom"] for c in changes] |
1863 ) |
1990 ) |
1864 else: |
1991 else: |
1865 # Find based on complete message text |
1992 # Find based on complete message text |
1866 txt = "\n".join(topItem.data(0, self.__messageRole)) |
1993 txt = "\n".join(topItem.data(0, self.__messageRole)) |
1867 else: |
1994 else: |
1868 txt = topItem.text(fieldIndex) |
1995 txt = topItem.text(fieldIndex) |
1869 if ( |
1996 if ( |
1870 topItem.text(self.DateColumn) <= to_ and |
1997 topItem.text(self.DateColumn) <= to_ |
1871 topItem.text(self.DateColumn) >= from_ and |
1998 and topItem.text(self.DateColumn) >= from_ |
1872 (branch == self.__allBranchesFilter or |
1999 and ( |
1873 topItem.text(self.BranchColumn) in |
2000 branch == self.__allBranchesFilter |
1874 [branch, closedBranch]) and |
2001 or topItem.text(self.BranchColumn) in [branch, closedBranch] |
1875 searchRx.search(txt) is not None |
2002 ) |
|
2003 and searchRx.search(txt) is not None |
1876 ): |
2004 ): |
1877 topItem.setHidden(False) |
2005 topItem.setHidden(False) |
1878 if topItem is currentItem: |
2006 if topItem is currentItem: |
1879 self.on_logTree_currentItemChanged(topItem, None) |
2007 self.on_logTree_currentItemChanged(topItem, None) |
1880 else: |
2008 else: |
1881 topItem.setHidden(True) |
2009 topItem.setHidden(True) |
1882 if topItem is currentItem: |
2010 if topItem is currentItem: |
1883 self.filesTree.clear() |
2011 self.filesTree.clear() |
1884 visibleItemCount -= 1 |
2012 visibleItemCount -= 1 |
1885 self.logTree.header().setSectionHidden( |
2013 self.logTree.header().setSectionHidden( |
1886 self.IconColumn, |
2014 self.IconColumn, visibleItemCount != self.logTree.topLevelItemCount() |
1887 visibleItemCount != self.logTree.topLevelItemCount()) |
2015 ) |
1888 |
2016 |
1889 def __prepareFieldSearch(self): |
2017 def __prepareFieldSearch(self): |
1890 """ |
2018 """ |
1891 Private slot to prepare the filed search data. |
2019 Private slot to prepare the filed search data. |
1892 |
2020 |
1893 @return tuple of field index, search expression and flag indicating |
2021 @return tuple of field index, search expression and flag indicating |
1894 that the field index is a data role (integer, string, boolean) |
2022 that the field index is a data role (integer, string, boolean) |
1895 """ |
2023 """ |
1896 indexIsRole = False |
2024 indexIsRole = False |
1897 txt = self.fieldCombo.itemData(self.fieldCombo.currentIndex()) |
2025 txt = self.fieldCombo.itemData(self.fieldCombo.currentIndex()) |
1981 if not itm.data(0, self.__incomingRole): |
2110 if not itm.data(0, self.__incomingRole): |
1982 currentPhase = itm.text(self.PhaseColumn) |
2111 currentPhase = itm.text(self.PhaseColumn) |
1983 revs = [] |
2112 revs = [] |
1984 for itm in self.logTree.selectedItems(): |
2113 for itm in self.logTree.selectedItems(): |
1985 if itm.text(self.PhaseColumn) == currentPhase: |
2114 if itm.text(self.PhaseColumn) == currentPhase: |
1986 revs.append( |
2115 revs.append(itm.text(self.RevisionColumn).split(":")[0].strip()) |
1987 itm.text(self.RevisionColumn).split(":")[0].strip()) |
2116 |
1988 |
|
1989 if not revs: |
2117 if not revs: |
1990 self.__phaseAct.setEnabled(False) |
2118 self.__phaseAct.setEnabled(False) |
1991 return |
2119 return |
1992 |
2120 |
1993 if currentPhase == self.phases["draft"]: |
2121 if currentPhase == self.phases["draft"]: |
1994 newPhase = self.phases["secret"] |
2122 newPhase = self.phases["secret"] |
1995 data = (revs, "s", True) |
2123 data = (revs, "s", True) |
1996 else: |
2124 else: |
1997 newPhase = self.phases["draft"] |
2125 newPhase = self.phases["draft"] |
1998 data = (revs, "d", False) |
2126 data = (revs, "d", False) |
1999 res = self.vcs.hgPhase(data) |
2127 res = self.vcs.hgPhase(data) |
2000 if res: |
2128 if res: |
2001 for itm in self.logTree.selectedItems(): |
2129 for itm in self.logTree.selectedItems(): |
2002 itm.setText(self.PhaseColumn, newPhase) |
2130 itm.setText(self.PhaseColumn, newPhase) |
2003 |
2131 |
2004 @pyqtSlot() |
2132 @pyqtSlot() |
2005 def __graftActTriggered(self): |
2133 def __graftActTriggered(self): |
2006 """ |
2134 """ |
2007 Private slot to handle the Copy Changesets action. |
2135 Private slot to handle the Copy Changesets action. |
2008 """ |
2136 """ |
2009 revs = [] |
2137 revs = [] |
2010 |
2138 |
2011 for itm in [item for item in self.logTree.selectedItems() |
2139 for itm in [ |
2012 if not item.data(0, self.__incomingRole)]: |
2140 item |
|
2141 for item in self.logTree.selectedItems() |
|
2142 if not item.data(0, self.__incomingRole) |
|
2143 ]: |
2013 branch = itm.text(self.BranchColumn) |
2144 branch = itm.text(self.BranchColumn) |
2014 if branch != self.__projectBranch: |
2145 if branch != self.__projectBranch: |
2015 revs.append( |
2146 revs.append(itm.text(self.RevisionColumn).strip().split(":", 1)[0]) |
2016 itm.text(self.RevisionColumn).strip().split(":", 1)[0]) |
2147 |
2017 |
|
2018 if revs: |
2148 if revs: |
2019 shouldReopen = self.vcs.hgGraft(revs) |
2149 shouldReopen = self.vcs.hgGraft(revs) |
2020 if shouldReopen: |
2150 if shouldReopen: |
2021 res = EricMessageBox.yesNo( |
2151 res = EricMessageBox.yesNo( |
2022 None, |
2152 None, |
2023 self.tr("Copy Changesets"), |
2153 self.tr("Copy Changesets"), |
2024 self.tr( |
2154 self.tr("""The project should be reread. Do this now?"""), |
2025 """The project should be reread. Do this now?"""), |
2155 yesDefault=True, |
2026 yesDefault=True) |
2156 ) |
2027 if res: |
2157 if res: |
2028 ericApp().getObject("Project").reopenProject() |
2158 ericApp().getObject("Project").reopenProject() |
2029 return |
2159 return |
2030 |
2160 |
2031 self.on_refreshButton_clicked() |
2161 self.on_refreshButton_clicked() |
2032 |
2162 |
2033 @pyqtSlot() |
2163 @pyqtSlot() |
2034 def __tagActTriggered(self): |
2164 def __tagActTriggered(self): |
2035 """ |
2165 """ |
2036 Private slot to tag the selected revision. |
2166 Private slot to tag the selected revision. |
2037 """ |
2167 """ |
2038 if len([itm for itm in self.logTree.selectedItems() |
2168 if ( |
2039 if not itm.data(0, self.__incomingRole)]) == 1: |
2169 len( |
|
2170 [ |
|
2171 itm |
|
2172 for itm in self.logTree.selectedItems() |
|
2173 if not itm.data(0, self.__incomingRole) |
|
2174 ] |
|
2175 ) |
|
2176 == 1 |
|
2177 ): |
2040 itm = self.logTree.selectedItems()[0] |
2178 itm = self.logTree.selectedItems()[0] |
2041 rev = itm.text(self.RevisionColumn).strip().split(":", 1)[0] |
2179 rev = itm.text(self.RevisionColumn).strip().split(":", 1)[0] |
2042 tag = itm.text(self.TagsColumn).strip().split(", ", 1)[0] |
2180 tag = itm.text(self.TagsColumn).strip().split(", ", 1)[0] |
2043 res = self.vcs.vcsTag(revision=rev, tagName=tag) |
2181 res = self.vcs.vcsTag(revision=rev, tagName=tag) |
2044 if res: |
2182 if res: |
2045 self.on_refreshButton_clicked() |
2183 self.on_refreshButton_clicked() |
2046 |
2184 |
2047 @pyqtSlot() |
2185 @pyqtSlot() |
2048 def __closeHeadsActTriggered(self): |
2186 def __closeHeadsActTriggered(self): |
2049 """ |
2187 """ |
2050 Private slot to close the selected head revisions. |
2188 Private slot to close the selected head revisions. |
2051 """ |
2189 """ |
2052 if self.vcs.isExtensionActive("closehead"): |
2190 if self.vcs.isExtensionActive("closehead"): |
2053 revs = [itm.text(self.RevisionColumn).strip().split(":", 1)[0] |
2191 revs = [ |
2054 for itm in self.logTree.selectedItems() |
2192 itm.text(self.RevisionColumn).strip().split(":", 1)[0] |
2055 if not itm.data(0, self.__incomingRole)] |
2193 for itm in self.logTree.selectedItems() |
|
2194 if not itm.data(0, self.__incomingRole) |
|
2195 ] |
2056 revs = [rev for rev in revs if rev in self.__headRevisions] |
2196 revs = [rev for rev in revs if rev in self.__headRevisions] |
2057 if revs: |
2197 if revs: |
2058 closeheadExtension = self.vcs.getExtensionObject("closehead") |
2198 closeheadExtension = self.vcs.getExtensionObject("closehead") |
2059 if closeheadExtension is not None: |
2199 if closeheadExtension is not None: |
2060 closeheadExtension.hgCloseheads(revisions=revs) |
2200 closeheadExtension.hgCloseheads(revisions=revs) |
2061 |
2201 |
2062 self.on_refreshButton_clicked() |
2202 self.on_refreshButton_clicked() |
2063 |
2203 |
2064 @pyqtSlot() |
2204 @pyqtSlot() |
2065 def __switchActTriggered(self): |
2205 def __switchActTriggered(self): |
2066 """ |
2206 """ |
2067 Private slot to switch the working directory to the |
2207 Private slot to switch the working directory to the |
2068 selected revision. |
2208 selected revision. |
2069 """ |
2209 """ |
2070 if len([itm for itm in self.logTree.selectedItems() |
2210 if ( |
2071 if not itm.data(0, self.__incomingRole)]) == 1: |
2211 len( |
|
2212 [ |
|
2213 itm |
|
2214 for itm in self.logTree.selectedItems() |
|
2215 if not itm.data(0, self.__incomingRole) |
|
2216 ] |
|
2217 ) |
|
2218 == 1 |
|
2219 ): |
2072 itm = self.logTree.selectedItems()[0] |
2220 itm = self.logTree.selectedItems()[0] |
2073 rev = itm.text(self.RevisionColumn).strip().split(":", 1)[0] |
2221 rev = itm.text(self.RevisionColumn).strip().split(":", 1)[0] |
2074 bookmarks = [bm.strip() for bm in |
2222 bookmarks = [ |
2075 itm.text(self.BookmarksColumn).strip().split(",") |
2223 bm.strip() |
2076 if bm.strip()] |
2224 for bm in itm.text(self.BookmarksColumn).strip().split(",") |
|
2225 if bm.strip() |
|
2226 ] |
2077 if bookmarks: |
2227 if bookmarks: |
2078 bookmark, ok = QInputDialog.getItem( |
2228 bookmark, ok = QInputDialog.getItem( |
2079 self, |
2229 self, |
2080 self.tr("Switch"), |
2230 self.tr("Switch"), |
2081 self.tr("Select bookmark to switch to (leave empty to" |
2231 self.tr( |
2082 " use revision):"), |
2232 "Select bookmark to switch to (leave empty to" " use revision):" |
|
2233 ), |
2083 [""] + bookmarks, |
2234 [""] + bookmarks, |
2084 0, False) |
2235 0, |
|
2236 False, |
|
2237 ) |
2085 if not ok: |
2238 if not ok: |
2086 return |
2239 return |
2087 if bookmark: |
2240 if bookmark: |
2088 rev = bookmark |
2241 rev = bookmark |
2089 if rev: |
2242 if rev: |
2090 shouldReopen = self.vcs.vcsUpdate(revision=rev) |
2243 shouldReopen = self.vcs.vcsUpdate(revision=rev) |
2091 if shouldReopen: |
2244 if shouldReopen: |
2092 res = EricMessageBox.yesNo( |
2245 res = EricMessageBox.yesNo( |
2093 None, |
2246 None, |
2094 self.tr("Switch"), |
2247 self.tr("Switch"), |
2095 self.tr( |
2248 self.tr("""The project should be reread. Do this now?"""), |
2096 """The project should be reread. Do this now?"""), |
2249 yesDefault=True, |
2097 yesDefault=True) |
2250 ) |
2098 if res: |
2251 if res: |
2099 ericApp().getObject("Project").reopenProject() |
2252 ericApp().getObject("Project").reopenProject() |
2100 return |
2253 return |
2101 |
2254 |
2102 self.on_refreshButton_clicked() |
2255 self.on_refreshButton_clicked() |
2103 |
2256 |
2104 @pyqtSlot() |
2257 @pyqtSlot() |
2105 def __bookmarkActTriggered(self): |
2258 def __bookmarkActTriggered(self): |
2106 """ |
2259 """ |
2107 Private slot to bookmark the selected revision. |
2260 Private slot to bookmark the selected revision. |
2108 """ |
2261 """ |
2109 if len([itm for itm in self.logTree.selectedItems() |
2262 if ( |
2110 if not itm.data(0, self.__incomingRole)]) == 1: |
2263 len( |
|
2264 [ |
|
2265 itm |
|
2266 for itm in self.logTree.selectedItems() |
|
2267 if not itm.data(0, self.__incomingRole) |
|
2268 ] |
|
2269 ) |
|
2270 == 1 |
|
2271 ): |
2111 itm = self.logTree.selectedItems()[0] |
2272 itm = self.logTree.selectedItems()[0] |
2112 rev, changeset = ( |
2273 rev, changeset = itm.text(self.RevisionColumn).strip().split(":", 1) |
2113 itm.text(self.RevisionColumn).strip().split(":", 1) |
|
2114 ) |
|
2115 bookmark, ok = QInputDialog.getText( |
2274 bookmark, ok = QInputDialog.getText( |
2116 self, |
2275 self, |
2117 self.tr("Define Bookmark"), |
2276 self.tr("Define Bookmark"), |
2118 self.tr('Enter bookmark name for changeset "{0}":').format( |
2277 self.tr('Enter bookmark name for changeset "{0}":').format(changeset), |
2119 changeset), |
2278 QLineEdit.EchoMode.Normal, |
2120 QLineEdit.EchoMode.Normal) |
2279 ) |
2121 if ok and bool(bookmark): |
2280 if ok and bool(bookmark): |
2122 self.vcs.hgBookmarkDefine( |
2281 self.vcs.hgBookmarkDefine( |
2123 revision="rev({0})".format(rev), |
2282 revision="rev({0})".format(rev), bookmark=bookmark |
2124 bookmark=bookmark) |
2283 ) |
2125 self.on_refreshButton_clicked() |
2284 self.on_refreshButton_clicked() |
2126 |
2285 |
2127 @pyqtSlot() |
2286 @pyqtSlot() |
2128 def __bookmarkMoveActTriggered(self): |
2287 def __bookmarkMoveActTriggered(self): |
2129 """ |
2288 """ |
2130 Private slot to move a bookmark to the selected revision. |
2289 Private slot to move a bookmark to the selected revision. |
2131 """ |
2290 """ |
2132 if len([itm for itm in self.logTree.selectedItems() |
2291 if ( |
2133 if not itm.data(0, self.__incomingRole)]) == 1: |
2292 len( |
|
2293 [ |
|
2294 itm |
|
2295 for itm in self.logTree.selectedItems() |
|
2296 if not itm.data(0, self.__incomingRole) |
|
2297 ] |
|
2298 ) |
|
2299 == 1 |
|
2300 ): |
2134 itm = self.logTree.selectedItems()[0] |
2301 itm = self.logTree.selectedItems()[0] |
2135 rev, changeset = ( |
2302 rev, changeset = itm.text(self.RevisionColumn).strip().split(":", 1) |
2136 itm.text(self.RevisionColumn).strip().split(":", 1) |
|
2137 ) |
|
2138 bookmarksList = self.vcs.hgGetBookmarksList() |
2303 bookmarksList = self.vcs.hgGetBookmarksList() |
2139 bookmark, ok = QInputDialog.getItem( |
2304 bookmark, ok = QInputDialog.getItem( |
2140 self, |
2305 self, |
2141 self.tr("Move Bookmark"), |
2306 self.tr("Move Bookmark"), |
2142 self.tr('Select the bookmark to be moved to changeset' |
2307 self.tr( |
2143 ' "{0}":').format(changeset), |
2308 "Select the bookmark to be moved to changeset" ' "{0}":' |
|
2309 ).format(changeset), |
2144 [""] + bookmarksList, |
2310 [""] + bookmarksList, |
2145 0, False) |
2311 0, |
|
2312 False, |
|
2313 ) |
2146 if ok and bool(bookmark): |
2314 if ok and bool(bookmark): |
2147 self.vcs.hgBookmarkMove( |
2315 self.vcs.hgBookmarkMove( |
2148 revision="rev({0})".format(rev), |
2316 revision="rev({0})".format(rev), bookmark=bookmark |
2149 bookmark=bookmark) |
2317 ) |
2150 self.on_refreshButton_clicked() |
2318 self.on_refreshButton_clicked() |
2151 |
2319 |
2152 @pyqtSlot() |
2320 @pyqtSlot() |
2153 def __lfPullActTriggered(self): |
2321 def __lfPullActTriggered(self): |
2154 """ |
2322 """ |
2155 Private slot to pull large files of selected revisions. |
2323 Private slot to pull large files of selected revisions. |
2156 """ |
2324 """ |
2157 revs = [] |
2325 revs = [] |
2158 for itm in [item for item in self.logTree.selectedItems() |
2326 for itm in [ |
2159 if not item.data(0, self.__incomingRole)]: |
2327 item |
|
2328 for item in self.logTree.selectedItems() |
|
2329 if not item.data(0, self.__incomingRole) |
|
2330 ]: |
2160 rev = itm.text(self.RevisionColumn).strip().split(":", 1)[0] |
2331 rev = itm.text(self.RevisionColumn).strip().split(":", 1)[0] |
2161 if rev: |
2332 if rev: |
2162 revs.append(rev) |
2333 revs.append(rev) |
2163 |
2334 |
2164 if revs: |
2335 if revs: |
2165 self.vcs.getExtensionObject("largefiles").hgLfPull(revisions=revs) |
2336 self.vcs.getExtensionObject("largefiles").hgLfPull(revisions=revs) |
2166 |
2337 |
2167 @pyqtSlot() |
2338 @pyqtSlot() |
2168 def __pullActTriggered(self): |
2339 def __pullActTriggered(self): |
2169 """ |
2340 """ |
2170 Private slot to pull changes from a remote repository. |
2341 Private slot to pull changes from a remote repository. |
2171 """ |
2342 """ |
2172 shouldReopen = False |
2343 shouldReopen = False |
2173 refresh = False |
2344 refresh = False |
2174 addNext = False |
2345 addNext = False |
2175 |
2346 |
2176 if self.initialCommandMode in ("log", "full_log", "incoming"): |
2347 if self.initialCommandMode in ("log", "full_log", "incoming"): |
2177 revs = [] |
2348 revs = [] |
2178 for itm in [item for item in self.logTree.selectedItems() |
2349 for itm in [ |
2179 if item.data(0, self.__incomingRole)]: |
2350 item |
|
2351 for item in self.logTree.selectedItems() |
|
2352 if item.data(0, self.__incomingRole) |
|
2353 ]: |
2180 rev = itm.text(self.RevisionColumn).split(":")[1].strip() |
2354 rev = itm.text(self.RevisionColumn).split(":")[1].strip() |
2181 if rev: |
2355 if rev: |
2182 revs.append(rev) |
2356 revs.append(rev) |
2183 shouldReopen = self.vcs.hgPull(revisions=revs) |
2357 shouldReopen = self.vcs.hgPull(revisions=revs) |
2184 refresh = True |
2358 refresh = True |
2185 if self.initialCommandMode == "incoming": |
2359 if self.initialCommandMode == "incoming": |
2186 addNext = True |
2360 addNext = True |
2187 |
2361 |
2188 if shouldReopen: |
2362 if shouldReopen: |
2189 res = EricMessageBox.yesNo( |
2363 res = EricMessageBox.yesNo( |
2190 None, |
2364 None, |
2191 self.tr("Pull Changes"), |
2365 self.tr("Pull Changes"), |
2192 self.tr( |
2366 self.tr("""The project should be reread. Do this now?"""), |
2193 """The project should be reread. Do this now?"""), |
2367 yesDefault=True, |
2194 yesDefault=True) |
2368 ) |
2195 if res: |
2369 if res: |
2196 ericApp().getObject("Project").reopenProject() |
2370 ericApp().getObject("Project").reopenProject() |
2197 return |
2371 return |
2198 |
2372 |
2199 if refresh: |
2373 if refresh: |
2200 self.on_refreshButton_clicked(addNext=addNext) |
2374 self.on_refreshButton_clicked(addNext=addNext) |
2201 |
2375 |
2202 @pyqtSlot() |
2376 @pyqtSlot() |
2203 def __pushActTriggered(self): |
2377 def __pushActTriggered(self): |
2204 """ |
2378 """ |
2205 Private slot to push changes to a remote repository up to a selected |
2379 Private slot to push changes to a remote repository up to a selected |
2206 changeset. |
2380 changeset. |
2322 if shouldReopen: |
2501 if shouldReopen: |
2323 res = EricMessageBox.yesNo( |
2502 res = EricMessageBox.yesNo( |
2324 None, |
2503 None, |
2325 self.tr("Apply Changegroup"), |
2504 self.tr("Apply Changegroup"), |
2326 self.tr("""The project should be reread. Do this now?"""), |
2505 self.tr("""The project should be reread. Do this now?"""), |
2327 yesDefault=True) |
2506 yesDefault=True, |
|
2507 ) |
2328 if res: |
2508 if res: |
2329 ericApp().getObject("Project").reopenProject() |
2509 ericApp().getObject("Project").reopenProject() |
2330 return |
2510 return |
2331 |
2511 |
2332 self.vcs.vcsLogBrowser() |
2512 self.vcs.vcsLogBrowser() |
2333 self.close() |
2513 self.close() |
2334 |
2514 |
2335 @pyqtSlot() |
2515 @pyqtSlot() |
2336 def __gpgSignActTriggered(self): |
2516 def __gpgSignActTriggered(self): |
2337 """ |
2517 """ |
2338 Private slot to sign the selected revisions. |
2518 Private slot to sign the selected revisions. |
2339 """ |
2519 """ |
2340 revs = [] |
2520 revs = [] |
2341 for itm in [item for item in self.logTree.selectedItems() |
2521 for itm in [ |
2342 if not item.data(0, self.__incomingRole)]: |
2522 item |
|
2523 for item in self.logTree.selectedItems() |
|
2524 if not item.data(0, self.__incomingRole) |
|
2525 ]: |
2343 rev = itm.text(self.RevisionColumn).split(":", 1)[0].strip() |
2526 rev = itm.text(self.RevisionColumn).split(":", 1)[0].strip() |
2344 if rev: |
2527 if rev: |
2345 revs.append(rev) |
2528 revs.append(rev) |
2346 |
2529 |
2347 if revs: |
2530 if revs: |
2348 self.vcs.getExtensionObject("gpg").hgGpgSign(revisions=revs) |
2531 self.vcs.getExtensionObject("gpg").hgGpgSign(revisions=revs) |
2349 |
2532 |
2350 @pyqtSlot() |
2533 @pyqtSlot() |
2351 def __gpgVerifyActTriggered(self): |
2534 def __gpgVerifyActTriggered(self): |
2352 """ |
2535 """ |
2353 Private slot to verify the signatures of a selected revisions. |
2536 Private slot to verify the signatures of a selected revisions. |
2354 """ |
2537 """ |
2355 itm = self.logTree.selectedItems()[0] |
2538 itm = self.logTree.selectedItems()[0] |
2356 if not itm.data(0, self.__incomingRole): |
2539 if not itm.data(0, self.__incomingRole): |
2357 rev = itm.text(self.RevisionColumn).split(":", 1)[0].strip() |
2540 rev = itm.text(self.RevisionColumn).split(":", 1)[0].strip() |
2358 if rev: |
2541 if rev: |
2359 self.vcs.getExtensionObject("gpg").hgGpgVerifySignatures( |
2542 self.vcs.getExtensionObject("gpg").hgGpgVerifySignatures(rev=rev) |
2360 rev=rev) |
2543 |
2361 |
|
2362 def __selectAllActTriggered(self, select=True): |
2544 def __selectAllActTriggered(self, select=True): |
2363 """ |
2545 """ |
2364 Private method to select or unselect all log entries. |
2546 Private method to select or unselect all log entries. |
2365 |
2547 |
2366 @param select flag indicating to select all entries |
2548 @param select flag indicating to select all entries |
2367 @type bool |
2549 @type bool |
2368 """ |
2550 """ |
2369 blocked = self.logTree.blockSignals(True) |
2551 blocked = self.logTree.blockSignals(True) |
2370 for row in range(self.logTree.topLevelItemCount()): |
2552 for row in range(self.logTree.topLevelItemCount()): |
2371 self.logTree.topLevelItem(row).setSelected(select) |
2553 self.logTree.topLevelItem(row).setSelected(select) |
2372 self.logTree.blockSignals(blocked) |
2554 self.logTree.blockSignals(blocked) |
2373 self.on_logTree_itemSelectionChanged() |
2555 self.on_logTree_itemSelectionChanged() |
2374 |
2556 |
2375 def __actionMode(self): |
2557 def __actionMode(self): |
2376 """ |
2558 """ |
2377 Private method to get the selected action mode. |
2559 Private method to get the selected action mode. |
2378 |
2560 |
2379 @return selected action mode (string, one of filter or find) |
2561 @return selected action mode (string, one of filter or find) |
2380 """ |
2562 """ |
2381 return self.modeComboBox.itemData( |
2563 return self.modeComboBox.itemData(self.modeComboBox.currentIndex()) |
2382 self.modeComboBox.currentIndex()) |
2564 |
2383 |
|
2384 @pyqtSlot(int) |
2565 @pyqtSlot(int) |
2385 def on_modeComboBox_currentIndexChanged(self, index): |
2566 def on_modeComboBox_currentIndexChanged(self, index): |
2386 """ |
2567 """ |
2387 Private slot to react on mode changes. |
2568 Private slot to react on mode changes. |
2388 |
2569 |
2389 @param index index of the selected entry (integer) |
2570 @param index index of the selected entry (integer) |
2390 """ |
2571 """ |
2391 mode = self.modeComboBox.itemData(index) |
2572 mode = self.modeComboBox.itemData(index) |
2392 findMode = mode == "find" |
2573 findMode = mode == "find" |
2393 filterMode = mode == "filter" |
2574 filterMode = mode == "filter" |
2394 |
2575 |
2395 self.fromDate.setEnabled(filterMode) |
2576 self.fromDate.setEnabled(filterMode) |
2396 self.toDate.setEnabled(filterMode) |
2577 self.toDate.setEnabled(filterMode) |
2397 self.branchCombo.setEnabled(filterMode) |
2578 self.branchCombo.setEnabled(filterMode) |
2398 self.findPrevButton.setVisible(findMode) |
2579 self.findPrevButton.setVisible(findMode) |
2399 self.findNextButton.setVisible(findMode) |
2580 self.findNextButton.setVisible(findMode) |
2400 |
2581 |
2401 if findMode: |
2582 if findMode: |
2402 for topIndex in range(self.logTree.topLevelItemCount()): |
2583 for topIndex in range(self.logTree.topLevelItemCount()): |
2403 self.logTree.topLevelItem(topIndex).setHidden(False) |
2584 self.logTree.topLevelItem(topIndex).setHidden(False) |
2404 self.logTree.header().setSectionHidden(self.IconColumn, False) |
2585 self.logTree.header().setSectionHidden(self.IconColumn, False) |
2405 elif filterMode: |
2586 elif filterMode: |
2406 self.__filterLogs() |
2587 self.__filterLogs() |
2407 |
2588 |
2408 @pyqtSlot() |
2589 @pyqtSlot() |
2409 def on_findPrevButton_clicked(self): |
2590 def on_findPrevButton_clicked(self): |
2410 """ |
2591 """ |
2411 Private slot to find the previous item matching the entered criteria. |
2592 Private slot to find the previous item matching the entered criteria. |
2412 """ |
2593 """ |
2413 self.__findItem(True) |
2594 self.__findItem(True) |
2414 |
2595 |
2415 @pyqtSlot() |
2596 @pyqtSlot() |
2416 def on_findNextButton_clicked(self): |
2597 def on_findNextButton_clicked(self): |
2417 """ |
2598 """ |
2418 Private slot to find the next item matching the entered criteria. |
2599 Private slot to find the next item matching the entered criteria. |
2419 """ |
2600 """ |
2420 self.__findItem(False) |
2601 self.__findItem(False) |
2421 |
2602 |
2422 def __findItem(self, backwards=False, interactive=False): |
2603 def __findItem(self, backwards=False, interactive=False): |
2423 """ |
2604 """ |
2424 Private slot to find an item matching the entered criteria. |
2605 Private slot to find an item matching the entered criteria. |
2425 |
2606 |
2426 @param backwards flag indicating to search backwards (boolean) |
2607 @param backwards flag indicating to search backwards (boolean) |
2427 @param interactive flag indicating an interactive search (boolean) |
2608 @param interactive flag indicating an interactive search (boolean) |
2428 """ |
2609 """ |
2429 self.__findBackwards = backwards |
2610 self.__findBackwards = backwards |
2430 |
2611 |
2431 fieldIndex, searchRx, indexIsRole = self.__prepareFieldSearch() |
2612 fieldIndex, searchRx, indexIsRole = self.__prepareFieldSearch() |
2432 currentIndex = self.logTree.indexOfTopLevelItem( |
2613 currentIndex = self.logTree.indexOfTopLevelItem(self.logTree.currentItem()) |
2433 self.logTree.currentItem()) |
|
2434 if backwards: |
2614 if backwards: |
2435 if interactive: |
2615 if interactive: |
2436 indexes = range(currentIndex, -1, -1) |
2616 indexes = range(currentIndex, -1, -1) |
2437 else: |
2617 else: |
2438 indexes = range(currentIndex - 1, -1, -1) |
2618 indexes = range(currentIndex - 1, -1, -1) |
2439 else: |
2619 else: |
2440 if interactive: |
2620 if interactive: |
2441 indexes = range(currentIndex, self.logTree.topLevelItemCount()) |
2621 indexes = range(currentIndex, self.logTree.topLevelItemCount()) |
2442 else: |
2622 else: |
2443 indexes = range(currentIndex + 1, |
2623 indexes = range(currentIndex + 1, self.logTree.topLevelItemCount()) |
2444 self.logTree.topLevelItemCount()) |
2624 |
2445 |
|
2446 for index in indexes: |
2625 for index in indexes: |
2447 topItem = self.logTree.topLevelItem(index) |
2626 topItem = self.logTree.topLevelItem(index) |
2448 if indexIsRole: |
2627 if indexIsRole: |
2449 if fieldIndex == self.__changesRole: |
2628 if fieldIndex == self.__changesRole: |
2450 changes = topItem.data(0, self.__changesRole) |
2629 changes = topItem.data(0, self.__changesRole) |
2451 txt = "\n".join( |
2630 txt = "\n".join( |
2452 [c["path"] for c in changes] + |
2631 [c["path"] for c in changes] + [c["copyfrom"] for c in changes] |
2453 [c["copyfrom"] for c in changes] |
|
2454 ) |
2632 ) |
2455 else: |
2633 else: |
2456 # Find based on complete message text |
2634 # Find based on complete message text |
2457 txt = "\n".join(topItem.data(0, self.__messageRole)) |
2635 txt = "\n".join(topItem.data(0, self.__messageRole)) |
2458 else: |
2636 else: |
2485 # a changeset hash was clicked, show the item |
2664 # a changeset hash was clicked, show the item |
2486 changeset = url.path() |
2665 changeset = url.path() |
2487 searchStr = ":{0}".format(changeset[:12]) # max. 12 hash chars |
2666 searchStr = ":{0}".format(changeset[:12]) # max. 12 hash chars |
2488 # format must be in sync with item generation format |
2667 # format must be in sync with item generation format |
2489 searchFlags = Qt.MatchFlag.MatchContains |
2668 searchFlags = Qt.MatchFlag.MatchContains |
2490 items = self.logTree.findItems( |
2669 items = self.logTree.findItems(searchStr, searchFlags, self.RevisionColumn) |
2491 searchStr, searchFlags, self.RevisionColumn) |
|
2492 if items: |
2670 if items: |
2493 itm = items[0] |
2671 itm = items[0] |
2494 if itm.isHidden(): |
2672 if itm.isHidden(): |
2495 itm.setHidden(False) |
2673 itm.setHidden(False) |
2496 self.logTree.setCurrentItem(itm) |
2674 self.logTree.setCurrentItem(itm) |
2497 else: |
2675 else: |
2498 # load the next batch and try again |
2676 # load the next batch and try again |
2499 if not self.cancelled and self.nextButton.isEnabled(): |
2677 if not self.cancelled and self.nextButton.isEnabled(): |
2500 self.__addFinishCallback( |
2678 self.__addFinishCallback(lambda: self.__revisionClicked(url)) |
2501 lambda: self.__revisionClicked(url)) |
|
2502 self.on_nextButton_clicked() |
2679 self.on_nextButton_clicked() |
2503 |
2680 |
2504 ########################################################################### |
2681 ########################################################################### |
2505 ## Diff handling methods below |
2682 ## Diff handling methods below |
2506 ########################################################################### |
2683 ########################################################################### |
2507 |
2684 |
2508 def __generateDiffs(self, parent=1): |
2685 def __generateDiffs(self, parent=1): |
2509 """ |
2686 """ |
2510 Private slot to generate diff outputs for the selected item. |
2687 Private slot to generate diff outputs for the selected item. |
2511 |
2688 |
2512 @param parent number of parent to diff against |
2689 @param parent number of parent to diff against |
2513 @type int |
2690 @type int |
2514 """ |
2691 """ |
2515 self.diffEdit.setPlainText(self.tr("Generating differences ...")) |
2692 self.diffEdit.setPlainText(self.tr("Generating differences ...")) |
2516 self.diffLabel.setText(self.tr("Differences")) |
2693 self.diffLabel.setText(self.tr("Differences")) |
2517 self.diffSelectLabel.clear() |
2694 self.diffSelectLabel.clear() |
2518 self.diffHighlighter.regenerateRules() |
2695 self.diffHighlighter.regenerateRules() |
2519 |
2696 |
2520 selectedItems = self.logTree.selectedItems() |
2697 selectedItems = self.logTree.selectedItems() |
2521 if len(selectedItems) == 1: |
2698 if len(selectedItems) == 1: |
2522 currentItem = selectedItems[0] |
2699 currentItem = selectedItems[0] |
2523 rev2 = currentItem.text(self.RevisionColumn).split(":", 1)[0] |
2700 rev2 = currentItem.text(self.RevisionColumn).split(":", 1)[0] |
2524 parents = currentItem.data(0, self.__parentsRole) |
2701 parents = currentItem.data(0, self.__parentsRole) |
2525 if len(parents) >= parent: |
2702 if len(parents) >= parent: |
2526 self.diffLabel.setText( |
2703 self.diffLabel.setText( |
2527 self.tr("Differences to Parent {0}").format(parent)) |
2704 self.tr("Differences to Parent {0}").format(parent) |
|
2705 ) |
2528 rev1 = parents[parent - 1] |
2706 rev1 = parents[parent - 1] |
2529 |
2707 |
2530 self.__diffGenerator.start(self.__filename, [rev1, rev2], |
2708 self.__diffGenerator.start(self.__filename, [rev1, rev2], self.__bundle) |
2531 self.__bundle) |
2709 |
2532 |
|
2533 if len(parents) > 1: |
2710 if len(parents) > 1: |
2534 if parent == 1: |
2711 if parent == 1: |
2535 par1 = " 1 " |
2712 par1 = " 1 " |
2536 else: |
2713 else: |
2537 par1 = '<a href="diff:1"> 1 </a>' |
2714 par1 = '<a href="diff:1"> 1 </a>' |
2538 if parent == 2: |
2715 if parent == 2: |
2539 par2 = " 2 " |
2716 par2 = " 2 " |
2540 else: |
2717 else: |
2541 par2 = '<a href="diff:2"> 2 </a>' |
2718 par2 = '<a href="diff:2"> 2 </a>' |
2542 self.diffSelectLabel.setText( |
2719 self.diffSelectLabel.setText( |
2543 self.tr('Diff to Parent {0}{1}').format(par1, par2)) |
2720 self.tr("Diff to Parent {0}{1}").format(par1, par2) |
|
2721 ) |
2544 elif len(selectedItems) == 2: |
2722 elif len(selectedItems) == 2: |
2545 rev2 = int(selectedItems[0].text( |
2723 rev2 = int(selectedItems[0].text(self.RevisionColumn).split(":")[0]) |
2546 self.RevisionColumn).split(":")[0]) |
2724 rev1 = int(selectedItems[1].text(self.RevisionColumn).split(":")[0]) |
2547 rev1 = int(selectedItems[1].text( |
2725 |
2548 self.RevisionColumn).split(":")[0]) |
2726 self.__diffGenerator.start( |
2549 |
2727 self.__filename, [min(rev1, rev2), max(rev1, rev2)], self.__bundle |
2550 self.__diffGenerator.start(self.__filename, |
2728 ) |
2551 [min(rev1, rev2), max(rev1, rev2)], |
|
2552 self.__bundle) |
|
2553 else: |
2729 else: |
2554 self.diffEdit.clear() |
2730 self.diffEdit.clear() |
2555 |
2731 |
2556 def __generatorFinished(self): |
2732 def __generatorFinished(self): |
2557 """ |
2733 """ |
2558 Private slot connected to the finished signal of the diff generator. |
2734 Private slot connected to the finished signal of the diff generator. |
2559 """ |
2735 """ |
2560 diff, errors, fileSeparators = self.__diffGenerator.getResult() |
2736 diff, errors, fileSeparators = self.__diffGenerator.getResult() |
2561 |
2737 |
2562 if diff: |
2738 if diff: |
2563 self.diffEdit.setPlainText("".join(diff)) |
2739 self.diffEdit.setPlainText("".join(diff)) |
2564 elif errors: |
2740 elif errors: |
2565 self.diffEdit.setPlainText("".join(errors)) |
2741 self.diffEdit.setPlainText("".join(errors)) |
2566 else: |
2742 else: |
2567 self.diffEdit.setPlainText(self.tr('There is no difference.')) |
2743 self.diffEdit.setPlainText(self.tr("There is no difference.")) |
2568 |
2744 |
2569 self.saveLabel.setVisible(bool(diff)) |
2745 self.saveLabel.setVisible(bool(diff)) |
2570 |
2746 |
2571 if self.__diffUpdatesFiles: |
2747 if self.__diffUpdatesFiles: |
2572 for oldFileName, newFileName, lineNumber in fileSeparators: |
2748 for oldFileName, newFileName, lineNumber in fileSeparators: |
2573 if oldFileName == newFileName: |
2749 if oldFileName == newFileName: |
2574 fileName = oldFileName |
2750 fileName = oldFileName |
2575 elif oldFileName == "__NULL__": |
2751 elif oldFileName == "__NULL__": |
2620 # step 1: move cursor to end |
2796 # step 1: move cursor to end |
2621 tc = self.diffEdit.textCursor() |
2797 tc = self.diffEdit.textCursor() |
2622 tc.movePosition(QTextCursor.MoveOperation.End) |
2798 tc.movePosition(QTextCursor.MoveOperation.End) |
2623 self.diffEdit.setTextCursor(tc) |
2799 self.diffEdit.setTextCursor(tc) |
2624 self.diffEdit.ensureCursorVisible() |
2800 self.diffEdit.ensureCursorVisible() |
2625 |
2801 |
2626 # step 2: move cursor to desired line |
2802 # step 2: move cursor to desired line |
2627 tc = self.diffEdit.textCursor() |
2803 tc = self.diffEdit.textCursor() |
2628 delta = tc.blockNumber() - para |
2804 delta = tc.blockNumber() - para |
2629 tc.movePosition(QTextCursor.MoveOperation.PreviousBlock, |
2805 tc.movePosition( |
2630 QTextCursor.MoveMode.MoveAnchor, delta) |
2806 QTextCursor.MoveOperation.PreviousBlock, |
|
2807 QTextCursor.MoveMode.MoveAnchor, |
|
2808 delta, |
|
2809 ) |
2631 self.diffEdit.setTextCursor(tc) |
2810 self.diffEdit.setTextCursor(tc) |
2632 self.diffEdit.ensureCursorVisible() |
2811 self.diffEdit.ensureCursorVisible() |
2633 |
2812 |
2634 @pyqtSlot(str) |
2813 @pyqtSlot(str) |
2635 def on_diffSelectLabel_linkActivated(self, link): |
2814 def on_diffSelectLabel_linkActivated(self, link): |
2636 """ |
2815 """ |
2637 Private slot to handle the selection of a diff target. |
2816 Private slot to handle the selection of a diff target. |
2638 |
2817 |
2639 @param link activated link |
2818 @param link activated link |
2640 @type str |
2819 @type str |
2641 """ |
2820 """ |
2642 if ":" in link: |
2821 if ":" in link: |
2643 scheme, parent = link.split(":", 1) |
2822 scheme, parent = link.split(":", 1) |
2644 if scheme == "diff": |
2823 if scheme == "diff": |
2645 with contextlib.suppress(ValueError): |
2824 with contextlib.suppress(ValueError): |
2646 parent = int(parent) |
2825 parent = int(parent) |
2647 self.__generateDiffs(parent) |
2826 self.__generateDiffs(parent) |
2648 |
2827 |
2649 @pyqtSlot(str) |
2828 @pyqtSlot(str) |
2650 def on_saveLabel_linkActivated(self, link): |
2829 def on_saveLabel_linkActivated(self, link): |
2651 """ |
2830 """ |
2652 Private slot to handle the selection of the save link. |
2831 Private slot to handle the selection of the save link. |
2653 |
2832 |
2654 @param link activated link |
2833 @param link activated link |
2655 @type str |
2834 @type str |
2656 """ |
2835 """ |
2657 if ":" not in link: |
2836 if ":" not in link: |
2658 return |
2837 return |
2659 |
2838 |
2660 scheme, rest = link.split(":", 1) |
2839 scheme, rest = link.split(":", 1) |
2661 if scheme != "save" or rest != "me": |
2840 if scheme != "save" or rest != "me": |
2662 return |
2841 return |
2663 |
2842 |
2664 if self.projectMode: |
2843 if self.projectMode: |
2665 if self.__filename is None: |
2844 if self.__filename is None: |
2666 fname = "{0}.diff".format(os.path.splitext( |
2845 fname = "{0}.diff".format( |
2667 ericApp().getObject("Project").getProjectFile())[0]) |
2846 os.path.splitext(ericApp().getObject("Project").getProjectFile())[0] |
|
2847 ) |
2668 else: |
2848 else: |
2669 fname = self.vcs.splitPath(self.__filename)[0] |
2849 fname = self.vcs.splitPath(self.__filename)[0] |
2670 fname += "/{0}.diff".format(os.path.split(fname)[-1]) |
2850 fname += "/{0}.diff".format(os.path.split(fname)[-1]) |
2671 else: |
2851 else: |
2672 dname, fname = self.vcs.splitPath(self.__filename) |
2852 dname, fname = self.vcs.splitPath(self.__filename) |
2673 if fname != '.': |
2853 if fname != ".": |
2674 fname = "{0}.diff".format(self.__filename) |
2854 fname = "{0}.diff".format(self.__filename) |
2675 else: |
2855 else: |
2676 fname = dname |
2856 fname = dname |
2677 |
2857 |
2678 fname, selectedFilter = EricFileDialog.getSaveFileNameAndFilter( |
2858 fname, selectedFilter = EricFileDialog.getSaveFileNameAndFilter( |
2679 self, |
2859 self, |
2680 self.tr("Save Diff"), |
2860 self.tr("Save Diff"), |
2681 fname, |
2861 fname, |
2682 self.tr("Patch Files (*.diff)"), |
2862 self.tr("Patch Files (*.diff)"), |
2683 None, |
2863 None, |
2684 EricFileDialog.DontConfirmOverwrite) |
2864 EricFileDialog.DontConfirmOverwrite, |
2685 |
2865 ) |
|
2866 |
2686 if not fname: |
2867 if not fname: |
2687 return # user aborted |
2868 return # user aborted |
2688 |
2869 |
2689 fpath = pathlib.Path(fname) |
2870 fpath = pathlib.Path(fname) |
2690 if not fpath.suffix: |
2871 if not fpath.suffix: |
2691 ex = selectedFilter.split("(*")[1].split(")")[0] |
2872 ex = selectedFilter.split("(*")[1].split(")")[0] |
2692 if ex: |
2873 if ex: |
2693 fpath = fpath.with_suffix(ex) |
2874 fpath = fpath.with_suffix(ex) |
2694 if fpath.exists(): |
2875 if fpath.exists(): |
2695 res = EricMessageBox.yesNo( |
2876 res = EricMessageBox.yesNo( |
2696 self, |
2877 self, |
2697 self.tr("Save Diff"), |
2878 self.tr("Save Diff"), |
2698 self.tr("<p>The patch file <b>{0}</b> already exists." |
2879 self.tr( |
2699 " Overwrite it?</p>").format(fpath), |
2880 "<p>The patch file <b>{0}</b> already exists." " Overwrite it?</p>" |
2700 icon=EricMessageBox.Warning) |
2881 ).format(fpath), |
|
2882 icon=EricMessageBox.Warning, |
|
2883 ) |
2701 if not res: |
2884 if not res: |
2702 return |
2885 return |
2703 |
2886 |
2704 eol = ericApp().getObject("Project").getEolString() |
2887 eol = ericApp().getObject("Project").getEolString() |
2705 try: |
2888 try: |
2706 with fpath.open("w", encoding="utf-8", newline="") as f: |
2889 with fpath.open("w", encoding="utf-8", newline="") as f: |
2707 f.write(eol.join(self.diffEdit.toPlainText().splitlines())) |
2890 f.write(eol.join(self.diffEdit.toPlainText().splitlines())) |
2708 except OSError as why: |
2891 except OSError as why: |
2709 EricMessageBox.critical( |
2892 EricMessageBox.critical( |
2710 self, self.tr('Save Diff'), |
2893 self, |
|
2894 self.tr("Save Diff"), |
2711 self.tr( |
2895 self.tr( |
2712 '<p>The patch file <b>{0}</b> could not be saved.' |
2896 "<p>The patch file <b>{0}</b> could not be saved." |
2713 '<br>Reason: {1}</p>') |
2897 "<br>Reason: {1}</p>" |
2714 .format(fpath, str(why))) |
2898 ).format(fpath, str(why)), |
2715 |
2899 ) |
|
2900 |
2716 @pyqtSlot(str) |
2901 @pyqtSlot(str) |
2717 def on_sbsSelectLabel_linkActivated(self, link): |
2902 def on_sbsSelectLabel_linkActivated(self, link): |
2718 """ |
2903 """ |
2719 Private slot to handle selection of a side-by-side link. |
2904 Private slot to handle selection of a side-by-side link. |
2720 |
2905 |
2721 @param link text of the selected link |
2906 @param link text of the selected link |
2722 @type str |
2907 @type str |
2723 """ |
2908 """ |
2724 if ":" in link and self.__filename is not None: |
2909 if ":" in link and self.__filename is not None: |
2725 scheme, path = link.split(":", 1) |
2910 scheme, path = link.split(":", 1) |