eric6/Plugins/VcsPlugins/vcsMercurial/HgDiffDialog.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) 2010 - 2019 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a dialog to show the output of the hg diff command process.
8 """
9
10 from __future__ import unicode_literals
11 try:
12 str = unicode
13 except NameError:
14 pass
15
16 from PyQt5.QtCore import pyqtSlot, QFileInfo, Qt
17 from PyQt5.QtGui import QTextCursor, QCursor
18 from PyQt5.QtWidgets import QWidget, QDialogButtonBox, QApplication
19
20 from E5Gui import E5MessageBox, E5FileDialog
21 from E5Gui.E5Application import e5App
22
23 from .Ui_HgDiffDialog import Ui_HgDiffDialog
24 from .HgDiffHighlighter import HgDiffHighlighter
25 from .HgDiffGenerator import HgDiffGenerator
26
27 import Utilities
28 import Preferences
29
30
31 class HgDiffDialog(QWidget, Ui_HgDiffDialog):
32 """
33 Class implementing a dialog to show the output of the hg diff command
34 process.
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(HgDiffDialog, self).__init__(parent)
44 self.setupUi(self)
45
46 self.refreshButton = self.buttonBox.addButton(
47 self.tr("Refresh"), QDialogButtonBox.ActionRole)
48 self.refreshButton.setToolTip(
49 self.tr("Press to refresh the display"))
50 self.refreshButton.setEnabled(False)
51 self.buttonBox.button(QDialogButtonBox.Save).setEnabled(False)
52 self.buttonBox.button(QDialogButtonBox.Close).setDefault(True)
53
54 self.searchWidget.attachTextEdit(self.contents)
55
56 self.vcs = vcs
57
58 font = Preferences.getEditorOtherFonts("MonospacedFont")
59 self.contents.setFontFamily(font.family())
60 self.contents.setFontPointSize(font.pointSize())
61
62 self.highlighter = HgDiffHighlighter(self.contents.document())
63
64 self.__diffGenerator = HgDiffGenerator(vcs, self)
65 self.__diffGenerator.finished.connect(self.__generatorFinished)
66
67 def closeEvent(self, e):
68 """
69 Protected slot implementing a close event handler.
70
71 @param e close event (QCloseEvent)
72 """
73 self.__diffGenerator.stopProcess()
74 e.accept()
75
76 def start(self, fn, versions=None, bundle=None, qdiff=False,
77 refreshable=False):
78 """
79 Public slot to start the hg diff command.
80
81 @param fn filename to be diffed (string)
82 @keyparam versions list of versions to be diffed (list of up to
83 2 strings or None)
84 @keyparam bundle name of a bundle file (string)
85 @keyparam qdiff flag indicating qdiff command shall be used (boolean)
86 @keyparam refreshable flag indicating a refreshable diff (boolean)
87 """
88 self.refreshButton.setVisible(refreshable)
89
90 self.errorGroup.hide()
91 self.filename = fn
92
93 self.contents.clear()
94 self.filesCombo.clear()
95 self.highlighter.regenerateRules()
96
97 if qdiff:
98 self.setWindowTitle(self.tr("Patch Contents"))
99
100 self.raise_()
101 self.activateWindow()
102
103 QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
104 procStarted = self.__diffGenerator.start(
105 fn, versions=versions, bundle=bundle, qdiff=qdiff)
106 if not procStarted:
107 E5MessageBox.critical(
108 self,
109 self.tr('Process Generation Error'),
110 self.tr(
111 'The process {0} could not be started. '
112 'Ensure, that it is in the search path.'
113 ).format('hg'))
114
115 def __generatorFinished(self):
116 """
117 Private slot connected to the finished signal.
118 """
119 QApplication.restoreOverrideCursor()
120 self.refreshButton.setEnabled(True)
121
122 diff, errors, fileSeparators = self.__diffGenerator.getResult()
123
124 if diff:
125 self.contents.setPlainText("".join(diff))
126 else:
127 self.contents.setPlainText(
128 self.tr('There is no difference.'))
129
130 if errors:
131 self.errorGroup.show()
132 self.errors.setPlainText("".join(errors))
133 self.errors.ensureCursorVisible()
134
135 self.buttonBox.button(QDialogButtonBox.Save).setEnabled(bool(diff))
136 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True)
137 self.buttonBox.button(QDialogButtonBox.Close).setDefault(True)
138 self.buttonBox.button(QDialogButtonBox.Close).setFocus(
139 Qt.OtherFocusReason)
140
141 tc = self.contents.textCursor()
142 tc.movePosition(QTextCursor.Start)
143 self.contents.setTextCursor(tc)
144 self.contents.ensureCursorVisible()
145
146 self.filesCombo.addItem(self.tr("<Start>"), 0)
147 self.filesCombo.addItem(self.tr("<End>"), -1)
148 for oldFile, newFile, pos in sorted(fileSeparators):
149 if not oldFile:
150 self.filesCombo.addItem(newFile, pos)
151 elif oldFile != newFile:
152 self.filesCombo.addItem(
153 "{0}\n{1}".format(oldFile, newFile), pos)
154 else:
155 self.filesCombo.addItem(oldFile, pos)
156
157 def on_buttonBox_clicked(self, button):
158 """
159 Private slot called by a button of the button box clicked.
160
161 @param button button that was clicked (QAbstractButton)
162 """
163 if button == self.buttonBox.button(QDialogButtonBox.Save):
164 self.on_saveButton_clicked()
165 elif button == self.refreshButton:
166 self.on_refreshButton_clicked()
167
168 @pyqtSlot(int)
169 def on_filesCombo_activated(self, index):
170 """
171 Private slot to handle the selection of a file.
172
173 @param index activated row (integer)
174 """
175 para = self.filesCombo.itemData(index)
176
177 if para == 0:
178 tc = self.contents.textCursor()
179 tc.movePosition(QTextCursor.Start)
180 self.contents.setTextCursor(tc)
181 self.contents.ensureCursorVisible()
182 elif para == -1:
183 tc = self.contents.textCursor()
184 tc.movePosition(QTextCursor.End)
185 self.contents.setTextCursor(tc)
186 self.contents.ensureCursorVisible()
187 else:
188 # step 1: move cursor to end
189 tc = self.contents.textCursor()
190 tc.movePosition(QTextCursor.End)
191 self.contents.setTextCursor(tc)
192 self.contents.ensureCursorVisible()
193
194 # step 2: move cursor to desired line
195 tc = self.contents.textCursor()
196 delta = tc.blockNumber() - para
197 tc.movePosition(QTextCursor.PreviousBlock, QTextCursor.MoveAnchor,
198 delta)
199 self.contents.setTextCursor(tc)
200 self.contents.ensureCursorVisible()
201
202 @pyqtSlot()
203 def on_saveButton_clicked(self):
204 """
205 Private slot to handle the Save button press.
206
207 It saves the diff shown in the dialog to a file in the local
208 filesystem.
209 """
210 if isinstance(self.filename, list):
211 if len(self.filename) > 1:
212 fname = self.vcs.splitPathList(self.filename)[0]
213 else:
214 dname, fname = self.vcs.splitPath(self.filename[0])
215 if fname != '.':
216 fname = "{0}.diff".format(self.filename[0])
217 else:
218 fname = dname
219 else:
220 fname = self.vcs.splitPath(self.filename)[0]
221
222 fname, selectedFilter = E5FileDialog.getSaveFileNameAndFilter(
223 self,
224 self.tr("Save Diff"),
225 fname,
226 self.tr("Patch Files (*.diff)"),
227 None,
228 E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite))
229
230 if not fname:
231 return # user aborted
232
233 ext = QFileInfo(fname).suffix()
234 if not ext:
235 ex = selectedFilter.split("(*")[1].split(")")[0]
236 if ex:
237 fname += ex
238 if QFileInfo(fname).exists():
239 res = E5MessageBox.yesNo(
240 self,
241 self.tr("Save Diff"),
242 self.tr("<p>The patch file <b>{0}</b> already exists."
243 " Overwrite it?</p>").format(fname),
244 icon=E5MessageBox.Warning)
245 if not res:
246 return
247 fname = Utilities.toNativeSeparators(fname)
248
249 eol = e5App().getObject("Project").getEolString()
250 try:
251 f = open(fname, "w", encoding="utf-8", newline="")
252 f.write(eol.join(self.contents.toPlainText().splitlines()))
253 f.close()
254 except IOError as why:
255 E5MessageBox.critical(
256 self, self.tr('Save Diff'),
257 self.tr(
258 '<p>The patch file <b>{0}</b> could not be saved.'
259 '<br>Reason: {1}</p>')
260 .format(fname, str(why)))
261
262 @pyqtSlot()
263 def on_refreshButton_clicked(self):
264 """
265 Private slot to refresh the display.
266 """
267 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False)
268
269 self.buttonBox.button(QDialogButtonBox.Save).setEnabled(False)
270 self.refreshButton.setEnabled(False)
271
272 self.start(self.filename, refreshable=True)

eric ide

mercurial