3 from qtpy
import QtCore
4 from qtpy
import QtWidgets
5 from qtpy
.QtCore
import Qt
10 from .. import qtutils
12 from ..interaction
import Interaction
14 from . import completion
15 from . import standard
16 from .text
import LineEdit
20 Interaction
.run_command
= staticmethod(run_command
)
21 Interaction
.confirm_config_action
= staticmethod(confirm_config_action
)
24 def get_config_actions(context
):
26 return cfg
.get_guitool_names_and_shortcuts()
29 def confirm_config_action(context
, name
, opts
):
30 dlg
= ActionDialog(context
, qtutils
.active_window(), name
, opts
)
32 if dlg
.exec_() != QtWidgets
.QDialog
.Accepted
:
36 opts
['revision'] = rev
43 def run_command(title
, command
):
44 """Show a command widget"""
45 view
= GitCommandWidget(title
, qtutils
.active_window())
46 view
.set_command(command
)
51 return (view
.exitstatus
, view
.out
, view
.err
)
54 class GitCommandWidget(standard
.Dialog
):
55 """Text viewer that reads the output of a command synchronously"""
57 # Keep us in scope otherwise PyQt kills the widget
58 def __init__(self
, title
, parent
=None):
59 standard
.Dialog
.__init
__(self
, parent
)
60 self
.setWindowTitle(title
)
61 if parent
is not None:
62 self
.setWindowModality(Qt
.ApplicationModal
)
64 # Construct the process
65 self
.proc
= QtCore
.QProcess(self
)
71 # Create the text browser
72 self
.output_text
= QtWidgets
.QTextBrowser(self
)
73 self
.output_text
.setAcceptDrops(False)
74 self
.output_text
.setTabChangesFocus(True)
75 self
.output_text
.setUndoRedoEnabled(False)
76 self
.output_text
.setReadOnly(True)
77 self
.output_text
.setAcceptRichText(False)
79 # Create abort / close buttons
80 # Start with abort disabled - will be enabled when the process is run.
81 self
.button_abort
= qtutils
.create_button(text
=N_('Abort'), enabled
=False)
82 self
.button_close
= qtutils
.close_button()
84 # Put them in a horizontal layout at the bottom.
85 self
.button_box
= QtWidgets
.QDialogButtonBox(self
)
86 self
.button_box
.addButton(
87 self
.button_abort
, QtWidgets
.QDialogButtonBox
.RejectRole
89 self
.button_box
.addButton(
90 self
.button_close
, 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(
103 defs
.margin
, defs
.spacing
, self
.output_text
, self
.button_box
105 self
.setLayout(self
._layout
)
107 self
.resize(720, 420)
109 def set_command(self
, command
):
110 self
.command
= command
113 """Runs the process"""
114 self
.proc
.start(self
.command
[0], self
.command
[1:])
116 def read_stdout(self
):
117 text
= self
.read_stream(self
.proc
.readAllStandardOutput
)
120 def read_stderr(self
):
121 text
= self
.read_stream(self
.proc
.readAllStandardError
)
124 def read_stream(self
, func
):
126 text
= core
.decode(data
)
127 self
.append_text(text
)
130 def append_text(self
, text
):
131 cursor
= self
.output_text
.textCursor()
132 cursor
.movePosition(cursor
.End
)
133 cursor
.insertText(text
)
134 cursor
.movePosition(cursor
.End
)
135 self
.output_text
.setTextCursor(cursor
)
138 if self
.proc
.state() != QtCore
.QProcess
.NotRunning
:
139 # Terminate seems to do nothing in windows
140 self
.proc
.terminate()
142 QtCore
.QTimer
.singleShot(1000, self
.proc
.kill
)
144 def closeEvent(self
, event
):
145 if self
.proc
.state() != QtCore
.QProcess
.NotRunning
:
146 # The process is still running, make sure we really want to abort.
147 title
= N_('Abort Action')
149 'An action is still running.\n'
150 'Terminating it could result in data loss.'
152 info_text
= N_('Abort the action?')
153 ok_text
= N_('Abort Action')
154 if Interaction
.confirm(
155 title
, msg
, info_text
, ok_text
, default
=False, icon
=icons
.close()
164 return standard
.Dialog
.closeEvent(self
, event
)
166 def proc_state_changed(self
, newstate
):
167 # State of process has changed - change the abort button state.
168 if newstate
== QtCore
.QProcess
.NotRunning
:
169 self
.button_abort
.setEnabled(False)
171 self
.button_abort
.setEnabled(True)
173 def proc_finished(self
, status
):
174 self
.exitstatus
= status
177 class ActionDialog(standard
.Dialog
):
180 def __init__(self
, context
, parent
, name
, opts
):
181 standard
.Dialog
.__init
__(self
, parent
)
182 self
.context
= context
183 self
.action_name
= name
187 values
= self
.VALUES
[name
]
189 values
= self
.VALUES
[name
] = {}
191 self
.setWindowModality(Qt
.ApplicationModal
)
193 title
= opts
.get('title')
195 self
.setWindowTitle(os
.path
.expandvars(title
))
197 self
.prompt
= QtWidgets
.QLabel()
198 prompt
= opts
.get('prompt')
200 self
.prompt
.setText(os
.path
.expandvars(prompt
))
202 self
.argslabel
= QtWidgets
.QLabel()
203 if 'argprompt' not in opts
or opts
.get('argprompt') is True:
204 argprompt
= N_('Arguments')
206 argprompt
= opts
.get('argprompt')
207 self
.argslabel
.setText(argprompt
)
209 self
.argstxt
= LineEdit()
210 if self
.opts
.get('argprompt'):
212 # Remember the previous value
213 saved_value
= values
['argstxt']
214 self
.argstxt
.setText(saved_value
)
218 self
.argslabel
.setMinimumSize(10, 10)
219 self
.argstxt
.setMinimumSize(10, 10)
221 self
.argslabel
.hide()
224 (N_('Local Branch'), gitcmds
.branch_list(context
, remote
=False)),
225 (N_('Tracking Branch'), gitcmds
.branch_list(context
, remote
=True)),
226 (N_('Tag'), gitcmds
.tag_list(context
)),
229 if 'revprompt' not in opts
or opts
.get('revprompt') is True:
230 revprompt
= N_('Revision')
232 revprompt
= opts
.get('revprompt')
233 self
.revselect
= RevisionSelector(context
, self
, revs
)
234 self
.revselect
.set_revision_label(revprompt
)
236 if not opts
.get('revprompt'):
237 self
.revselect
.hide()
240 self
.closebtn
= qtutils
.close_button()
241 self
.runbtn
= qtutils
.create_button(
242 text
=N_('Run'), default
=True, icon
=icons
.ok()
245 self
.argslayt
= qtutils
.hbox(
246 defs
.margin
, defs
.spacing
, self
.argslabel
, self
.argstxt
249 self
.btnlayt
= qtutils
.hbox(
250 defs
.margin
, defs
.spacing
, qtutils
.STRETCH
, self
.closebtn
, self
.runbtn
253 self
.layt
= qtutils
.vbox(
261 self
.setLayout(self
.layt
)
263 self
.argstxt
.textChanged
.connect(self
._argstxt
_changed
)
264 qtutils
.connect_button(self
.closebtn
, self
.reject
)
265 qtutils
.connect_button(self
.runbtn
, self
.accept
)
267 # Widen the dialog by default
268 self
.resize(666, self
.height())
271 return self
.revselect
.revision()
274 return self
.argstxt
.text()
276 def _argstxt_changed(self
, value
):
277 """Store the argstxt value so that we can remember it between calls"""
278 self
.VALUES
[self
.action_name
]['argstxt'] = value
281 class RevisionSelector(QtWidgets
.QWidget
):
282 def __init__(self
, context
, parent
, revs
):
283 QtWidgets
.QWidget
.__init
__(self
, parent
)
285 self
.context
= context
287 self
._revdict
= dict(revs
)
289 self
._rev
_label
= QtWidgets
.QLabel(self
)
290 self
._revision
= completion
.GitRefLineEdit(context
, parent
=self
)
292 # Create the radio buttons
294 self
._radio
_btns
= {}
295 for label
, rev_list
in self
._revs
:
296 radio
= qtutils
.radio(text
=label
)
297 radio
.setObjectName(label
)
298 qtutils
.connect_button(radio
, self
._set
_revision
_list
)
299 radio_btns
.append(radio
)
300 self
._radio
_btns
[label
] = radio
301 radio_btns
.append(qtutils
.STRETCH
)
303 self
._rev
_list
= QtWidgets
.QListWidget()
304 label
, rev_list
= self
._revs
[0]
305 self
._radio
_btns
[label
].setChecked(True)
306 qtutils
.set_items(self
._rev
_list
, rev_list
)
308 self
._rev
_layt
= qtutils
.hbox(
309 defs
.no_margin
, defs
.spacing
, self
._rev
_label
, self
._revision
312 self
._radio
_layt
= qtutils
.hbox(defs
.margin
, defs
.spacing
, *radio_btns
)
314 self
._layt
= qtutils
.vbox(
321 self
.setLayout(self
._layt
)
322 self
._rev
_list
.itemSelectionChanged
.connect(self
.selection_changed
)
325 return self
._revision
.text()
327 def set_revision_label(self
, txt
):
328 self
._rev
_label
.setText(txt
)
330 def _set_revision_list(self
):
331 sender
= self
.sender().objectName()
332 revs
= self
._revdict
[sender
]
333 qtutils
.set_items(self
._rev
_list
, revs
)
335 def selection_changed(self
):
336 items
= self
._rev
_list
.selectedItems()
339 self
._revision
.setText(items
[0].text())