1 from __future__
import division
, absolute_import
, unicode_literals
4 from qtpy
import QtCore
5 from qtpy
import QtWidgets
6 from qtpy
.QtCore
import Qt
11 from .. import qtutils
13 from ..interaction
import Interaction
15 from . import completion
16 from . import standard
17 from .text
import LineEdit
21 Interaction
.run_command
= staticmethod(run_command
)
22 Interaction
.confirm_config_action
= staticmethod(confirm_config_action
)
25 def get_config_actions(context
):
27 return cfg
.get_guitool_names_and_shortcuts()
30 def confirm_config_action(context
, name
, opts
):
31 dlg
= ActionDialog(context
, qtutils
.active_window(), name
, opts
)
33 if dlg
.exec_() != QtWidgets
.QDialog
.Accepted
:
37 opts
['revision'] = rev
44 def run_command(title
, command
):
45 """Show a command widget"""
46 view
= GitCommandWidget(title
, qtutils
.active_window())
47 view
.set_command(command
)
52 return (view
.exitstatus
, view
.out
, view
.err
)
55 class GitCommandWidget(standard
.Dialog
):
56 """Text viewer that reads the output of a command synchronously"""
58 # Keep us in scope otherwise PyQt kills the widget
59 def __init__(self
, title
, parent
=None):
60 standard
.Dialog
.__init
__(self
, parent
)
61 self
.setWindowTitle(title
)
62 if parent
is not None:
63 self
.setWindowModality(Qt
.ApplicationModal
)
65 # Construct the process
66 self
.proc
= QtCore
.QProcess(self
)
72 # Create the text browser
73 self
.output_text
= QtWidgets
.QTextBrowser(self
)
74 self
.output_text
.setAcceptDrops(False)
75 self
.output_text
.setTabChangesFocus(True)
76 self
.output_text
.setUndoRedoEnabled(False)
77 self
.output_text
.setReadOnly(True)
78 self
.output_text
.setAcceptRichText(False)
80 # Create abort / close buttons
81 # Start with abort disabled - will be enabled when the process is run.
82 self
.button_abort
= qtutils
.create_button(text
=N_('Abort'),
84 self
.button_close
= qtutils
.close_button()
86 # Put them in a horizontal layout at the bottom.
87 self
.button_box
= QtWidgets
.QDialogButtonBox(self
)
88 self
.button_box
.addButton(self
.button_abort
,
89 QtWidgets
.QDialogButtonBox
.RejectRole
)
90 self
.button_box
.addButton(self
.button_close
,
91 QtWidgets
.QDialogButtonBox
.AcceptRole
)
93 # Connect the signals to the process
94 self
.proc
.readyReadStandardOutput
.connect(self
.read_stdout
)
95 self
.proc
.readyReadStandardError
.connect(self
.read_stderr
)
96 self
.proc
.finished
.connect(self
.proc_finished
)
97 self
.proc
.stateChanged
.connect(self
.proc_state_changed
)
99 qtutils
.connect_button(self
.button_abort
, self
.abort
)
100 qtutils
.connect_button(self
.button_close
, self
.close
)
102 self
._layout
= qtutils
.vbox(defs
.margin
, defs
.spacing
,
103 self
.output_text
, self
.button_box
)
104 self
.setLayout(self
._layout
)
106 self
.resize(720, 420)
108 def set_command(self
, command
):
109 self
.command
= command
112 """Runs the process"""
113 self
.proc
.start(self
.command
[0], self
.command
[1:])
115 def read_stdout(self
):
116 text
= self
.read_stream(self
.proc
.readAllStandardOutput
)
119 def read_stderr(self
):
120 text
= self
.read_stream(self
.proc
.readAllStandardError
)
123 def read_stream(self
, fn
):
125 text
= core
.decode(data
)
126 self
.append_text(text
)
129 def append_text(self
, text
):
130 cursor
= self
.output_text
.textCursor()
131 cursor
.movePosition(cursor
.End
)
132 cursor
.insertText(text
)
133 cursor
.movePosition(cursor
.End
)
134 self
.output_text
.setTextCursor(cursor
)
137 if self
.proc
.state() != QtCore
.QProcess
.NotRunning
:
138 # Terminate seems to do nothing in windows
139 self
.proc
.terminate()
141 QtCore
.QTimer
.singleShot(1000, self
.proc
.kill
)
143 def closeEvent(self
, event
):
144 if self
.proc
.state() != QtCore
.QProcess
.NotRunning
:
145 # The process is still running, make sure we really want to abort.
146 title
= N_('Abort Action')
147 msg
= N_('An action is still running.\n'
148 'Terminating it could result in data loss.')
149 info_text
= N_('Abort the action?')
150 ok_text
= N_('Abort Action')
151 if Interaction
.confirm(title
, msg
, info_text
, ok_text
,
152 default
=False, icon
=icons
.close()):
160 return standard
.Dialog
.closeEvent(self
, event
)
162 def proc_state_changed(self
, newstate
):
163 # State of process has changed - change the abort button state.
164 if newstate
== QtCore
.QProcess
.NotRunning
:
165 self
.button_abort
.setEnabled(False)
167 self
.button_abort
.setEnabled(True)
169 def proc_finished(self
, status
):
170 self
.exitstatus
= status
173 class ActionDialog(standard
.Dialog
):
177 def __init__(self
, context
, parent
, name
, opts
):
178 standard
.Dialog
.__init
__(self
, parent
)
179 self
.context
= context
180 self
.action_name
= name
184 values
= self
.VALUES
[name
]
186 values
= self
.VALUES
[name
] = {}
188 self
.setWindowModality(Qt
.ApplicationModal
)
190 title
= opts
.get('title')
192 self
.setWindowTitle(os
.path
.expandvars(title
))
194 self
.prompt
= QtWidgets
.QLabel()
195 prompt
= opts
.get('prompt')
197 self
.prompt
.setText(os
.path
.expandvars(prompt
))
199 self
.argslabel
= QtWidgets
.QLabel()
200 if 'argprompt' not in opts
or opts
.get('argprompt') is True:
201 argprompt
= N_('Arguments')
203 argprompt
= opts
.get('argprompt')
204 self
.argslabel
.setText(argprompt
)
206 self
.argstxt
= LineEdit()
207 if self
.opts
.get('argprompt'):
209 # Remember the previous value
210 saved_value
= values
['argstxt']
211 self
.argstxt
.setText(saved_value
)
215 self
.argslabel
.setMinimumSize(1, 1)
216 self
.argstxt
.setMinimumSize(1, 1)
218 self
.argslabel
.hide()
221 (N_('Local Branch'), gitcmds
.branch_list(context
, remote
=False)),
222 (N_('Tracking Branch'), gitcmds
.branch_list(context
, remote
=True)),
223 (N_('Tag'), gitcmds
.tag_list(context
)),
226 if 'revprompt' not in opts
or opts
.get('revprompt') is True:
227 revprompt
= N_('Revision')
229 revprompt
= opts
.get('revprompt')
230 self
.revselect
= RevisionSelector(context
, self
, revs
)
231 self
.revselect
.set_revision_label(revprompt
)
233 if not opts
.get('revprompt'):
234 self
.revselect
.hide()
237 self
.closebtn
= qtutils
.close_button()
238 self
.runbtn
= qtutils
.create_button(text
=N_('Run'), default
=True,
241 self
.argslayt
= qtutils
.hbox(defs
.margin
, defs
.spacing
,
242 self
.argslabel
, self
.argstxt
)
244 self
.btnlayt
= qtutils
.hbox(defs
.margin
, defs
.spacing
, qtutils
.STRETCH
,
245 self
.closebtn
, self
.runbtn
)
247 self
.layt
= qtutils
.vbox(defs
.margin
, defs
.spacing
,
248 self
.prompt
, self
.argslayt
,
249 self
.revselect
, self
.btnlayt
)
250 self
.setLayout(self
.layt
)
252 self
.argstxt
.textChanged
.connect(self
._argstxt
_changed
)
253 qtutils
.connect_button(self
.closebtn
, self
.reject
)
254 qtutils
.connect_button(self
.runbtn
, self
.accept
)
256 # Widen the dialog by default
257 self
.resize(666, self
.height())
260 return self
.revselect
.revision()
263 return self
.argstxt
.text()
265 def _argstxt_changed(self
, value
):
266 """Store the argstxt value so that we can remember it between calls"""
267 self
.VALUES
[self
.action_name
]['argstxt'] = value
270 class RevisionSelector(QtWidgets
.QWidget
):
272 def __init__(self
, context
, parent
, revs
):
273 QtWidgets
.QWidget
.__init
__(self
, parent
)
275 self
.context
= context
277 self
._revdict
= dict(revs
)
279 self
._rev
_label
= QtWidgets
.QLabel(self
)
280 self
._revision
= completion
.GitRefLineEdit(context
, parent
=self
)
282 # Create the radio buttons
284 self
._radio
_btns
= {}
285 for label
, rev_list
in self
._revs
:
286 radio
= qtutils
.radio(text
=label
)
287 radio
.setObjectName(label
)
288 qtutils
.connect_button(radio
, self
._set
_revision
_list
)
289 radio_btns
.append(radio
)
290 self
._radio
_btns
[label
] = radio
291 radio_btns
.append(qtutils
.STRETCH
)
293 self
._rev
_list
= QtWidgets
.QListWidget()
294 label
, rev_list
= self
._revs
[0]
295 self
._radio
_btns
[label
].setChecked(True)
296 qtutils
.set_items(self
._rev
_list
, rev_list
)
298 self
._rev
_layt
= qtutils
.hbox(defs
.no_margin
, defs
.spacing
,
299 self
._rev
_label
, self
._revision
)
301 self
._radio
_layt
= qtutils
.hbox(defs
.margin
, defs
.spacing
,
304 self
._layt
= qtutils
.vbox(defs
.no_margin
, defs
.spacing
,
305 self
._rev
_layt
, self
._radio
_layt
,
307 self
.setLayout(self
._layt
)
309 self
._rev
_list
.itemSelectionChanged
.connect(self
.selection_changed
)
312 return self
._revision
.text()
314 def set_revision_label(self
, txt
):
315 self
._rev
_label
.setText(txt
)
317 def _set_revision_list(self
):
318 sender
= self
.sender().objectName()
319 revs
= self
._revdict
[sender
]
320 qtutils
.set_items(self
._rev
_list
, revs
)
322 def selection_changed(self
):
323 items
= self
._rev
_list
.selectedItems()
326 self
._revision
.setText(items
[0].text())