2 from PyQt4
import QtCore
3 from PyQt4
import QtGui
4 from PyQt4
.QtCore
import Qt
5 from PyQt4
.QtCore
import SIGNAL
8 from cola
import gitcfg
9 from cola
import gitcmds
10 from cola
import qtutils
11 from cola
.i18n
import N_
12 from cola
.interaction
import Interaction
13 from cola
.qtutils
import create_button
14 from cola
.widgets
import defs
15 from cola
.widgets
import completion
16 from cola
.widgets
import standard
20 Interaction
.run_command
= staticmethod(run_command
)
21 Interaction
.confirm_config_action
= staticmethod(confirm_config_action
)
24 def get_config_actions():
25 cfg
= gitcfg
.instance()
26 return cfg
.get_guitool_names()
29 def confirm_config_action(name
, opts
):
30 dlg
= ActionDialog(qtutils
.active_window(), name
, opts
)
32 if dlg
.exec_() != QtGui
.QDialog
.Accepted
:
34 rev
= unicode(dlg
.revision())
36 opts
['revision'] = rev
37 args
= unicode(dlg
.args())
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 """Nice TextView that reads the output of a command syncronously"""
56 # Keep us in scope otherwise PyQt kills the widget
57 def __init__(self
, title
, parent
=None):
58 standard
.Dialog
.__init
__(self
, parent
)
61 self
.setWindowTitle(title
)
62 self
.setWindowModality(Qt
.ApplicationModal
)
64 # Construct the process
65 self
.proc
= QtCore
.QProcess(self
)
70 self
._layout
= QtGui
.QVBoxLayout(self
)
71 self
._layout
.setContentsMargins(3, 3, 3, 3)
73 # Create the text browser
74 self
.output_text
= QtGui
.QTextBrowser(self
)
75 self
.output_text
.setAcceptDrops(False)
76 self
.output_text
.setTabChangesFocus(True)
77 self
.output_text
.setUndoRedoEnabled(False)
78 self
.output_text
.setReadOnly(True)
79 self
.output_text
.setAcceptRichText(False)
81 self
._layout
.addWidget(self
.output_text
)
83 # Create abort / close buttons
84 self
.button_abort
= QtGui
.QPushButton(self
)
85 self
.button_abort
.setText(N_('Abort'))
86 self
.button_close
= QtGui
.QPushButton(self
)
87 self
.button_close
.setText(N_('Close'))
89 # Put them in a horizontal layout at the bottom.
90 self
.button_box
= QtGui
.QDialogButtonBox(self
)
91 self
.button_box
.addButton(self
.button_abort
, QtGui
.QDialogButtonBox
.RejectRole
)
92 self
.button_box
.addButton(self
.button_close
, QtGui
.QDialogButtonBox
.AcceptRole
)
93 self
._layout
.addWidget(self
.button_box
)
95 # Connect the signals to the process
96 self
.connect(self
.proc
, SIGNAL('readyReadStandardOutput()'),
98 self
.connect(self
.proc
, SIGNAL('readyReadStandardError()'),
100 self
.connect(self
.proc
, SIGNAL('finished(int)'), self
.finishProc
)
101 self
.connect(self
.proc
, SIGNAL('stateChanged(QProcess::ProcessState)'), self
.stateChanged
)
103 # Start with abort disabled - will be enabled when the process is run.
104 self
.button_abort
.setEnabled(False)
106 qtutils
.connect_button(self
.button_abort
, self
.abortProc
)
107 qtutils
.connect_button(self
.button_close
, self
.close
)
109 def set_command(self
, command
):
110 self
.command
= command
113 """Runs the process"""
114 self
.proc
.start(self
.command
[0], QtCore
.QStringList(self
.command
[1:]))
116 def read_stdout(self
):
117 rawbytes
= self
.proc
.readAllStandardOutput()
121 text
= core
.decode(data
)
123 self
.append_text(text
)
125 def read_stderr(self
):
126 rawbytes
= self
.proc
.readAllStandardError()
130 text
= core
.decode(data
)
132 self
.append_text(text
)
134 def append_text(self
, text
):
135 cursor
= self
.output_text
.textCursor()
136 cursor
.movePosition(cursor
.End
)
137 cursor
.insertText(text
)
138 cursor
.movePosition(cursor
.End
)
139 self
.output_text
.setTextCursor(cursor
)
142 if self
.proc
.state() != QtCore
.QProcess
.NotRunning
:
143 # Terminate seems to do nothing in windows
144 self
.proc
.terminate()
146 QtCore
.QTimer
.singleShot(1000, self
.proc
, QtCore
.SLOT('kill()'))
148 def closeEvent(self
, event
):
149 if self
.proc
.state() != QtCore
.QProcess
.NotRunning
:
150 # The process is still running, make sure we really want to abort.
151 title
= N_('Abort Action')
152 msg
= N_('An action is still running.\n'
153 'Terminating it could result in data loss.')
154 info_text
= N_('Abort the action?')
155 ok_text
= N_('Abort Action')
156 if qtutils
.confirm(title
, msg
, info_text
, ok_text
,
157 default
=False, icon
=qtutils
.discard_icon()):
165 return standard
.Dialog
.closeEvent(self
, event
)
167 def stateChanged(self
, newstate
):
168 # State of process has changed - change the abort button state.
169 if newstate
== QtCore
.QProcess
.NotRunning
:
170 self
.button_abort
.setEnabled(False)
172 self
.button_abort
.setEnabled(True)
174 def finishProc(self
, status
):
175 self
.exitstatus
= status
178 class ActionDialog(standard
.Dialog
):
179 def __init__(self
, parent
, name
, opts
):
180 standard
.Dialog
.__init
__(self
, parent
)
184 self
.setWindowModality(Qt
.ApplicationModal
)
186 self
.layt
= QtGui
.QVBoxLayout()
187 self
.layt
.setMargin(defs
.margin
)
188 self
.layt
.setSpacing(defs
.spacing
)
189 self
.setLayout(self
.layt
)
191 title
= opts
.get('title')
193 self
.setWindowTitle(os
.path
.expandvars(title
))
195 self
.prompt
= QtGui
.QLabel()
197 prompt
= opts
.get('prompt')
199 self
.prompt
.setText(os
.path
.expandvars(prompt
))
200 self
.layt
.addWidget(self
.prompt
)
203 self
.argslabel
= QtGui
.QLabel()
204 if 'argprompt' not in opts
or opts
.get('argprompt') is True:
205 argprompt
= N_('Arguments')
207 argprompt
= opts
.get('argprompt')
209 self
.argslabel
.setText(argprompt
)
211 self
.argstxt
= QtGui
.QLineEdit()
212 self
.argslayt
= QtGui
.QHBoxLayout()
213 self
.argslayt
.addWidget(self
.argslabel
)
214 self
.argslayt
.addWidget(self
.argstxt
)
215 self
.layt
.addLayout(self
.argslayt
)
217 if not self
.opts
.get('argprompt'):
218 self
.argslabel
.setMinimumSize(1, 1)
219 self
.argstxt
.setMinimumSize(1, 1)
221 self
.argslabel
.hide()
224 (N_('Local Branch'), gitcmds
.branch_list(remote
=False)),
225 (N_('Tracking Branch'), gitcmds
.branch_list(remote
=True)),
226 (N_('Tag'), gitcmds
.tag_list()),
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(self
, revs
)
234 self
.revselect
.set_revision_label(revprompt
)
235 self
.layt
.addWidget(self
.revselect
)
237 if not opts
.get('revprompt'):
238 self
.revselect
.hide()
241 self
.btnlayt
= QtGui
.QHBoxLayout()
242 self
.btnlayt
.addStretch()
243 self
.closebtn
= create_button(text
=N_('Close'), layout
=self
.btnlayt
)
244 self
.runbtn
= create_button(text
=N_('Run'), layout
=self
.btnlayt
)
245 self
.runbtn
.setDefault(True)
246 self
.layt
.addLayout(self
.btnlayt
)
248 # Widen the dialog by default
249 self
.resize(666, self
.height())
251 qtutils
.connect_button(self
.closebtn
, self
.reject
)
252 qtutils
.connect_button(self
.runbtn
, self
.accept
)
255 return self
.revselect
.revision()
258 return self
.argstxt
.text()
261 class RevisionSelector(QtGui
.QWidget
):
262 def __init__(self
, parent
, revs
):
263 QtGui
.QWidget
.__init
__(self
, parent
)
266 self
._revdict
= dict(revs
)
268 self
._layt
= QtGui
.QVBoxLayout()
269 self
._layt
.setMargin(0)
270 self
.setLayout(self
._layt
)
272 self
._rev
_layt
= QtGui
.QHBoxLayout()
273 self
._rev
_layt
.setMargin(0)
275 self
._rev
_label
= QtGui
.QLabel()
276 self
._rev
_layt
.addWidget(self
._rev
_label
)
278 self
._revision
= completion
.GitRefLineEdit()
279 self
._rev
_layt
.addWidget(self
._revision
)
281 self
._layt
.addLayout(self
._rev
_layt
)
283 self
._radio
_layt
= QtGui
.QHBoxLayout()
284 self
._radio
_btns
= {}
286 # Create the radio buttons
287 for label
, rev_list
in self
._revs
:
288 radio
= QtGui
.QRadioButton()
290 radio
.setObjectName(label
)
291 qtutils
.connect_button(radio
, self
._set
_revision
_list
)
292 self
._radio
_layt
.addWidget(radio
)
293 self
._radio
_btns
[label
] = radio
295 self
._radio
_layt
.addStretch()
297 self
._layt
.addLayout(self
._radio
_layt
)
299 self
._rev
_list
= QtGui
.QListWidget()
300 self
._layt
.addWidget(self
._rev
_list
)
302 label
, rev_list
= self
._revs
[0]
303 self
._radio
_btns
[label
].setChecked(True)
304 qtutils
.set_items(self
._rev
_list
, rev_list
)
306 self
.connect(self
._rev
_list
, SIGNAL('itemSelectionChanged()'),
307 self
._rev
_list
_selection
_changed
)
310 return self
._revision
.text()
312 def set_revision_label(self
, txt
):
313 self
._rev
_label
.setText(txt
)
315 def _set_revision_list(self
):
316 sender
= unicode(self
.sender().objectName())
317 revs
= self
._revdict
[sender
]
318 qtutils
.set_items(self
._rev
_list
, revs
)
320 def _rev_list_selection_changed(self
):
321 items
= self
._rev
_list
.selectedItems()
324 self
._revision
.setText(items
[0].text())