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'), enabled
=False)
83 self
.button_close
= qtutils
.close_button()
85 # Put them in a horizontal layout at the bottom.
86 self
.button_box
= QtWidgets
.QDialogButtonBox(self
)
87 self
.button_box
.addButton(
88 self
.button_abort
, QtWidgets
.QDialogButtonBox
.RejectRole
90 self
.button_box
.addButton(
91 self
.button_close
, QtWidgets
.QDialogButtonBox
.AcceptRole
94 # Connect the signals to the process
95 # pylint: disable=no-member
96 self
.proc
.readyReadStandardOutput
.connect(self
.read_stdout
)
97 self
.proc
.readyReadStandardError
.connect(self
.read_stderr
)
98 self
.proc
.finished
.connect(self
.proc_finished
)
99 self
.proc
.stateChanged
.connect(self
.proc_state_changed
)
101 qtutils
.connect_button(self
.button_abort
, self
.abort
)
102 qtutils
.connect_button(self
.button_close
, self
.close
)
104 self
._layout
= qtutils
.vbox(
105 defs
.margin
, defs
.spacing
, self
.output_text
, self
.button_box
107 self
.setLayout(self
._layout
)
109 self
.resize(720, 420)
111 def set_command(self
, command
):
112 self
.command
= command
115 """Runs the process"""
116 self
.proc
.start(self
.command
[0], self
.command
[1:])
118 def read_stdout(self
):
119 text
= self
.read_stream(self
.proc
.readAllStandardOutput
)
122 def read_stderr(self
):
123 text
= self
.read_stream(self
.proc
.readAllStandardError
)
126 def read_stream(self
, fn
):
128 text
= core
.decode(data
)
129 self
.append_text(text
)
132 def append_text(self
, text
):
133 cursor
= self
.output_text
.textCursor()
134 cursor
.movePosition(cursor
.End
)
135 cursor
.insertText(text
)
136 cursor
.movePosition(cursor
.End
)
137 self
.output_text
.setTextCursor(cursor
)
140 if self
.proc
.state() != QtCore
.QProcess
.NotRunning
:
141 # Terminate seems to do nothing in windows
142 self
.proc
.terminate()
144 QtCore
.QTimer
.singleShot(1000, self
.proc
.kill
)
146 def closeEvent(self
, event
):
147 if self
.proc
.state() != QtCore
.QProcess
.NotRunning
:
148 # The process is still running, make sure we really want to abort.
149 title
= N_('Abort Action')
151 'An action is still running.\n'
152 'Terminating it could result in data loss.'
154 info_text
= N_('Abort the action?')
155 ok_text
= N_('Abort Action')
156 if Interaction
.confirm(
157 title
, msg
, info_text
, ok_text
, default
=False, icon
=icons
.close()
166 return standard
.Dialog
.closeEvent(self
, event
)
168 def proc_state_changed(self
, newstate
):
169 # State of process has changed - change the abort button state.
170 if newstate
== QtCore
.QProcess
.NotRunning
:
171 self
.button_abort
.setEnabled(False)
173 self
.button_abort
.setEnabled(True)
175 def proc_finished(self
, status
):
176 self
.exitstatus
= status
179 class ActionDialog(standard
.Dialog
):
183 def __init__(self
, context
, parent
, name
, opts
):
184 standard
.Dialog
.__init
__(self
, parent
)
185 self
.context
= context
186 self
.action_name
= name
190 values
= self
.VALUES
[name
]
192 values
= self
.VALUES
[name
] = {}
194 self
.setWindowModality(Qt
.ApplicationModal
)
196 title
= opts
.get('title')
198 self
.setWindowTitle(os
.path
.expandvars(title
))
200 self
.prompt
= QtWidgets
.QLabel()
201 prompt
= opts
.get('prompt')
203 self
.prompt
.setText(os
.path
.expandvars(prompt
))
205 self
.argslabel
= QtWidgets
.QLabel()
206 if 'argprompt' not in opts
or opts
.get('argprompt') is True:
207 argprompt
= N_('Arguments')
209 argprompt
= opts
.get('argprompt')
210 self
.argslabel
.setText(argprompt
)
212 self
.argstxt
= LineEdit()
213 if self
.opts
.get('argprompt'):
215 # Remember the previous value
216 saved_value
= values
['argstxt']
217 self
.argstxt
.setText(saved_value
)
221 self
.argslabel
.setMinimumSize(10, 10)
222 self
.argstxt
.setMinimumSize(10, 10)
224 self
.argslabel
.hide()
227 (N_('Local Branch'), gitcmds
.branch_list(context
, remote
=False)),
228 (N_('Tracking Branch'), gitcmds
.branch_list(context
, remote
=True)),
229 (N_('Tag'), gitcmds
.tag_list(context
)),
232 if 'revprompt' not in opts
or opts
.get('revprompt') is True:
233 revprompt
= N_('Revision')
235 revprompt
= opts
.get('revprompt')
236 self
.revselect
= RevisionSelector(context
, self
, revs
)
237 self
.revselect
.set_revision_label(revprompt
)
239 if not opts
.get('revprompt'):
240 self
.revselect
.hide()
243 self
.closebtn
= qtutils
.close_button()
244 self
.runbtn
= qtutils
.create_button(
245 text
=N_('Run'), default
=True, icon
=icons
.ok()
248 self
.argslayt
= qtutils
.hbox(
249 defs
.margin
, defs
.spacing
, self
.argslabel
, self
.argstxt
252 self
.btnlayt
= qtutils
.hbox(
253 defs
.margin
, defs
.spacing
, qtutils
.STRETCH
, self
.closebtn
, self
.runbtn
256 self
.layt
= qtutils
.vbox(
264 self
.setLayout(self
.layt
)
266 # pylint: disable=no-member
267 self
.argstxt
.textChanged
.connect(self
._argstxt
_changed
)
268 qtutils
.connect_button(self
.closebtn
, self
.reject
)
269 qtutils
.connect_button(self
.runbtn
, self
.accept
)
271 # Widen the dialog by default
272 self
.resize(666, self
.height())
275 return self
.revselect
.revision()
278 return self
.argstxt
.text()
280 def _argstxt_changed(self
, value
):
281 """Store the argstxt value so that we can remember it between calls"""
282 self
.VALUES
[self
.action_name
]['argstxt'] = value
285 class RevisionSelector(QtWidgets
.QWidget
):
286 def __init__(self
, context
, parent
, revs
):
287 QtWidgets
.QWidget
.__init
__(self
, parent
)
289 self
.context
= context
291 self
._revdict
= dict(revs
)
293 self
._rev
_label
= QtWidgets
.QLabel(self
)
294 self
._revision
= completion
.GitRefLineEdit(context
, parent
=self
)
296 # Create the radio buttons
298 self
._radio
_btns
= {}
299 for label
, rev_list
in self
._revs
:
300 radio
= qtutils
.radio(text
=label
)
301 radio
.setObjectName(label
)
302 qtutils
.connect_button(radio
, self
._set
_revision
_list
)
303 radio_btns
.append(radio
)
304 self
._radio
_btns
[label
] = radio
305 radio_btns
.append(qtutils
.STRETCH
)
307 self
._rev
_list
= QtWidgets
.QListWidget()
308 label
, rev_list
= self
._revs
[0]
309 self
._radio
_btns
[label
].setChecked(True)
310 qtutils
.set_items(self
._rev
_list
, rev_list
)
312 self
._rev
_layt
= qtutils
.hbox(
313 defs
.no_margin
, defs
.spacing
, self
._rev
_label
, self
._revision
316 self
._radio
_layt
= qtutils
.hbox(defs
.margin
, defs
.spacing
, *radio_btns
)
318 self
._layt
= qtutils
.vbox(
325 self
.setLayout(self
._layt
)
327 # pylint: disable=no-member
328 self
._rev
_list
.itemSelectionChanged
.connect(self
.selection_changed
)
331 return self
._revision
.text()
333 def set_revision_label(self
, txt
):
334 self
._rev
_label
.setText(txt
)
336 def _set_revision_list(self
):
337 sender
= self
.sender().objectName()
338 revs
= self
._revdict
[sender
]
339 qtutils
.set_items(self
._rev
_list
, revs
)
341 def selection_changed(self
):
342 items
= self
._rev
_list
.selectedItems()
345 self
._revision
.setText(items
[0].text())