eric6/Plugins/VcsPlugins/vcsMercurial/HgBookmarksListDialog.py

changeset 6942
2602857055c5
parent 6645
ad476851d7e0
child 7192
a22eee00b052
equal deleted inserted replaced
6941:f99d60d6b59b 6942:2602857055c5
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2011 - 2019 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a dialog to show a list of bookmarks.
8 """
9
10 from __future__ import unicode_literals
11 try:
12 str = unicode
13 except NameError:
14 pass
15
16 import os
17
18 from PyQt5.QtCore import pyqtSlot, QProcess, Qt, QTimer, QCoreApplication, \
19 QPoint
20 from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QHeaderView, \
21 QTreeWidgetItem, QLineEdit, QMenu, QInputDialog
22
23 from E5Gui.E5Application import e5App
24 from E5Gui import E5MessageBox
25
26 from .Ui_HgBookmarksListDialog import Ui_HgBookmarksListDialog
27
28 import UI.PixmapCache
29 from Globals import strToQByteArray
30
31
32 class HgBookmarksListDialog(QDialog, Ui_HgBookmarksListDialog):
33 """
34 Class implementing a dialog to show a list of bookmarks.
35 """
36 def __init__(self, vcs, parent=None):
37 """
38 Constructor
39
40 @param vcs reference to the vcs object
41 @param parent parent widget (QWidget)
42 """
43 super(HgBookmarksListDialog, self).__init__(parent)
44 self.setupUi(self)
45 self.setWindowFlags(Qt.Window)
46
47 self.refreshButton = self.buttonBox.addButton(
48 self.tr("Refresh"), QDialogButtonBox.ActionRole)
49 self.refreshButton.setToolTip(
50 self.tr("Press to refresh the bookmarks display"))
51 self.refreshButton.setEnabled(False)
52 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False)
53 self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True)
54
55 self.process = QProcess()
56 self.vcs = vcs
57 self.__bookmarksList = None
58 self.__repoDir = None
59 self.__hgClient = vcs.getClient()
60 self.__bookmarksDefined = False
61 self.__currentRevision = ""
62
63 self.bookmarksList.headerItem().setText(
64 self.bookmarksList.columnCount(), "")
65 self.bookmarksList.header().setSortIndicator(3, Qt.AscendingOrder)
66
67 self.process.finished.connect(self.__procFinished)
68 self.process.readyReadStandardOutput.connect(self.__readStdout)
69 self.process.readyReadStandardError.connect(self.__readStderr)
70
71 self.show()
72 QCoreApplication.processEvents()
73
74 def closeEvent(self, e):
75 """
76 Protected slot implementing a close event handler.
77
78 @param e close event (QCloseEvent)
79 """
80 if self.__hgClient:
81 if self.__hgClient.isExecuting():
82 self.__hgClient.cancel()
83 else:
84 if self.process is not None and \
85 self.process.state() != QProcess.NotRunning:
86 self.process.terminate()
87 QTimer.singleShot(2000, self.process.kill)
88 self.process.waitForFinished(3000)
89
90 e.accept()
91
92 def start(self, path, bookmarksList):
93 """
94 Public slot to start the bookmarks command.
95
96 @param path name of directory to be listed (string)
97 @param bookmarksList reference to string list receiving the bookmarks
98 (list of strings)
99 """
100 self.bookmarksList.clear()
101 self.__bookmarksDefined = False
102
103 self.errorGroup.hide()
104
105 self.intercept = False
106 self.activateWindow()
107
108 self.__bookmarksList = bookmarksList
109 del self.__bookmarksList[:] # clear the list
110
111 dname, fname = self.vcs.splitPath(path)
112
113 # find the root of the repo
114 repodir = dname
115 while not os.path.isdir(os.path.join(repodir, self.vcs.adminDir)):
116 repodir = os.path.dirname(repodir)
117 if os.path.splitdrive(repodir)[1] == os.sep:
118 return
119 self.__repoDir = repodir
120
121 args = self.vcs.initCommand("bookmarks")
122
123 if self.__hgClient:
124 self.inputGroup.setEnabled(False)
125 self.inputGroup.hide()
126 self.refreshButton.setEnabled(False)
127
128 out, err = self.__hgClient.runcommand(args)
129 if err:
130 self.__showError(err)
131 if out:
132 for line in out.splitlines():
133 self.__processOutputLine(line)
134 if self.__hgClient.wasCanceled():
135 break
136 self.__finish()
137 else:
138 self.process.kill()
139 self.process.setWorkingDirectory(repodir)
140
141 self.process.start('hg', args)
142 procStarted = self.process.waitForStarted(5000)
143 if not procStarted:
144 self.inputGroup.setEnabled(False)
145 self.inputGroup.hide()
146 E5MessageBox.critical(
147 self,
148 self.tr('Process Generation Error'),
149 self.tr(
150 'The process {0} could not be started. '
151 'Ensure, that it is in the search path.'
152 ).format('hg'))
153 else:
154 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False)
155 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(True)
156 self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True)
157
158 self.inputGroup.setEnabled(True)
159 self.inputGroup.show()
160 self.refreshButton.setEnabled(False)
161
162 def __finish(self):
163 """
164 Private slot called when the process finished or the user pressed
165 the button.
166 """
167 if self.process is not None and \
168 self.process.state() != QProcess.NotRunning:
169 self.process.terminate()
170 QTimer.singleShot(2000, self.process.kill)
171 self.process.waitForFinished(3000)
172
173 self.inputGroup.setEnabled(False)
174 self.inputGroup.hide()
175 self.refreshButton.setEnabled(True)
176
177 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True)
178 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False)
179 self.buttonBox.button(QDialogButtonBox.Close).setDefault(True)
180 self.buttonBox.button(QDialogButtonBox.Close).setFocus(
181 Qt.OtherFocusReason)
182
183 if self.bookmarksList.topLevelItemCount() == 0:
184 # no bookmarks defined
185 self.__generateItem(
186 self.tr("no bookmarks defined"), "", "", "")
187 self.__bookmarksDefined = False
188 else:
189 self.__bookmarksDefined = True
190
191 self.__resizeColumns()
192 self.__resort()
193
194 # restore current item
195 if self.__currentRevision:
196 items = self.bookmarksList.findItems(
197 self.__currentRevision, Qt.MatchExactly, 0)
198 if items:
199 self.bookmarksList.setCurrentItem(items[0])
200 self.__currentRevision = ""
201 self.bookmarksList.setFocus(Qt.OtherFocusReason)
202
203 def on_buttonBox_clicked(self, button):
204 """
205 Private slot called by a button of the button box clicked.
206
207 @param button button that was clicked (QAbstractButton)
208 """
209 if button == self.buttonBox.button(QDialogButtonBox.Close):
210 self.close()
211 elif button == self.buttonBox.button(QDialogButtonBox.Cancel):
212 if self.__hgClient:
213 self.__hgClient.cancel()
214 else:
215 self.__finish()
216 elif button == self.refreshButton:
217 self.on_refreshButton_clicked()
218
219 def __procFinished(self, exitCode, exitStatus):
220 """
221 Private slot connected to the finished signal.
222
223 @param exitCode exit code of the process (integer)
224 @param exitStatus exit status of the process (QProcess.ExitStatus)
225 """
226 self.__finish()
227
228 def __resort(self):
229 """
230 Private method to resort the tree.
231 """
232 self.bookmarksList.sortItems(
233 self.bookmarksList.sortColumn(),
234 self.bookmarksList.header().sortIndicatorOrder())
235
236 def __resizeColumns(self):
237 """
238 Private method to resize the list columns.
239 """
240 self.bookmarksList.header().resizeSections(
241 QHeaderView.ResizeToContents)
242 self.bookmarksList.header().setStretchLastSection(True)
243
244 def __generateItem(self, revision, changeset, status, name):
245 """
246 Private method to generate a bookmark item in the bookmarks list.
247
248 @param revision revision of the bookmark (string)
249 @param changeset changeset of the bookmark (string)
250 @param status of the bookmark (string)
251 @param name name of the bookmark (string)
252 """
253 itm = QTreeWidgetItem(self.bookmarksList)
254 if revision[0].isdecimal():
255 # valid bookmark entry
256 itm.setData(0, Qt.DisplayRole, int(revision))
257 itm.setData(1, Qt.DisplayRole, changeset)
258 itm.setData(2, Qt.DisplayRole, status)
259 itm.setData(3, Qt.DisplayRole, name)
260 itm.setTextAlignment(0, Qt.AlignRight)
261 itm.setTextAlignment(1, Qt.AlignRight)
262 itm.setTextAlignment(2, Qt.AlignHCenter)
263 else:
264 # error message
265 itm.setData(0, Qt.DisplayRole, revision)
266
267 def __readStdout(self):
268 """
269 Private slot to handle the readyReadStdout signal.
270
271 It reads the output of the process, formats it and inserts it into
272 the contents pane.
273 """
274 self.process.setReadChannel(QProcess.StandardOutput)
275
276 while self.process.canReadLine():
277 s = str(self.process.readLine(), self.vcs.getEncoding(),
278 'replace').strip()
279 self.__processOutputLine(s)
280
281 def __processOutputLine(self, line):
282 """
283 Private method to process the lines of output.
284
285 @param line output line to be processed (string)
286 """
287 li = line.split()
288 if li[-1][0] in "1234567890":
289 # last element is a rev:changeset
290 rev, changeset = li[-1].split(":", 1)
291 del li[-1]
292 if li[0] == "*":
293 status = "current"
294 del li[0]
295 else:
296 status = ""
297 name = " ".join(li)
298 self.__generateItem(rev, changeset, status, name)
299 if self.__bookmarksList is not None:
300 self.__bookmarksList.append(name)
301
302 def __readStderr(self):
303 """
304 Private slot to handle the readyReadStderr signal.
305
306 It reads the error output of the process and inserts it into the
307 error pane.
308 """
309 if self.process is not None:
310 s = str(self.process.readAllStandardError(),
311 self.vcs.getEncoding(), 'replace')
312 self.__showError(s)
313
314 def __showError(self, out):
315 """
316 Private slot to show some error.
317
318 @param out error to be shown (string)
319 """
320 self.errorGroup.show()
321 self.errors.insertPlainText(out)
322 self.errors.ensureCursorVisible()
323
324 def on_passwordCheckBox_toggled(self, isOn):
325 """
326 Private slot to handle the password checkbox toggled.
327
328 @param isOn flag indicating the status of the check box (boolean)
329 """
330 if isOn:
331 self.input.setEchoMode(QLineEdit.Password)
332 else:
333 self.input.setEchoMode(QLineEdit.Normal)
334
335 @pyqtSlot()
336 def on_sendButton_clicked(self):
337 """
338 Private slot to send the input to the subversion process.
339 """
340 inputTxt = self.input.text()
341 inputTxt += os.linesep
342
343 if self.passwordCheckBox.isChecked():
344 self.errors.insertPlainText(os.linesep)
345 self.errors.ensureCursorVisible()
346 else:
347 self.errors.insertPlainText(inputTxt)
348 self.errors.ensureCursorVisible()
349
350 self.process.write(strToQByteArray(inputTxt))
351
352 self.passwordCheckBox.setChecked(False)
353 self.input.clear()
354
355 def on_input_returnPressed(self):
356 """
357 Private slot to handle the press of the return key in the input field.
358 """
359 self.intercept = True
360 self.on_sendButton_clicked()
361
362 def keyPressEvent(self, evt):
363 """
364 Protected slot to handle a key press event.
365
366 @param evt the key press event (QKeyEvent)
367 """
368 if self.intercept:
369 self.intercept = False
370 evt.accept()
371 return
372 super(HgBookmarksListDialog, self).keyPressEvent(evt)
373
374 @pyqtSlot()
375 def on_refreshButton_clicked(self):
376 """
377 Private slot to refresh the status display.
378 """
379 # save the current items commit ID
380 itm = self.bookmarksList.currentItem()
381 if itm is not None:
382 self.__currentRevision = itm.text(0)
383 else:
384 self.__currentRevision = ""
385
386 self.start(self.__repoDir, self.__bookmarksList)
387
388 @pyqtSlot(QPoint)
389 def on_bookmarksList_customContextMenuRequested(self, pos):
390 """
391 Private slot to handle the context menu request.
392
393 @param pos position the context menu was requetsed at
394 @type QPoint
395 """
396 itm = self.bookmarksList.itemAt(pos)
397 if itm is not None:
398 menu = QMenu(self.bookmarksList)
399 menu.addAction(
400 UI.PixmapCache.getIcon("vcsSwitch.png"),
401 self.tr("Switch to"), self.__switchTo)
402 menu.addSeparator()
403 menu.addAction(
404 UI.PixmapCache.getIcon("deleteBookmark.png"),
405 self.tr("Delete"), self.__deleteBookmark)
406 menu.addAction(
407 UI.PixmapCache.getIcon("renameBookmark.png"),
408 self.tr("Rename"), self.__renameBookmark)
409 menu.addSeparator()
410 act = menu.addAction(
411 UI.PixmapCache.getIcon("pullBookmark.png"),
412 self.tr("Pull"), self.__pullBookmark)
413 act.setEnabled(self.vcs.canPull())
414 act = menu.addAction(
415 UI.PixmapCache.getIcon("pushBookmark.png"),
416 self.tr("Push"), self.__pushBookmark)
417 act.setEnabled(self.vcs.canPush())
418 menu.popup(self.bookmarksList.mapToGlobal(pos))
419
420 def __switchTo(self):
421 """
422 Private slot to switch the working directory to the selected revision.
423 """
424 itm = self.bookmarksList.currentItem()
425 bookmark = itm.text(3).strip()
426 if bookmark:
427 shouldReopen = self.vcs.vcsUpdate(
428 self.__repoDir, revision=bookmark)
429 if shouldReopen:
430 res = E5MessageBox.yesNo(
431 None,
432 self.tr("Switch"),
433 self.tr(
434 """The project should be reread. Do this now?"""),
435 yesDefault=True)
436 if res:
437 e5App().getObject("Project").reopenProject()
438 return
439
440 self.on_refreshButton_clicked()
441
442 def __deleteBookmark(self):
443 """
444 Private slot to delete the selected bookmark.
445 """
446 itm = self.bookmarksList.currentItem()
447 bookmark = itm.text(3).strip()
448 if bookmark:
449 yes = E5MessageBox.yesNo(
450 self,
451 self.tr("Delete Bookmark"),
452 self.tr("""<p>Shall the bookmark <b>{0}</b> really be"""
453 """ deleted?</p>""").format(bookmark))
454 if yes:
455 self.vcs.hgBookmarkDelete(self.__repoDir, bookmark=bookmark)
456 self.on_refreshButton_clicked()
457
458 def __renameBookmark(self):
459 """
460 Private slot to rename the selected bookmark.
461 """
462 itm = self.bookmarksList.currentItem()
463 bookmark = itm.text(3).strip()
464 if bookmark:
465 newName, ok = QInputDialog.getText(
466 self,
467 self.tr("Rename Bookmark"),
468 self.tr("<p>Enter the new name for bookmark <b>{0}</b>:</p>")
469 .format(bookmark),
470 QLineEdit.Normal)
471 if ok and bool(newName):
472 self.vcs.hgBookmarkRename(self.__repoDir, (bookmark, newName))
473 self.on_refreshButton_clicked()
474
475 def __pullBookmark(self):
476 """
477 Private slot to pull the selected bookmark.
478 """
479 itm = self.bookmarksList.currentItem()
480 bookmark = itm.text(3).strip()
481 if bookmark:
482 self.vcs.hgBookmarkPull(self.__repoDir, bookmark=bookmark)
483 self.on_refreshButton_clicked()
484
485 def __pushBookmark(self):
486 """
487 Private slot to push the selected bookmark.
488 """
489 itm = self.bookmarksList.currentItem()
490 bookmark = itm.text(3).strip()
491 if bookmark:
492 self.vcs.hgBookmarkPush(self.__repoDir, bookmark=bookmark)
493 self.on_refreshButton_clicked()

eric ide

mercurial