2 from PyQt4
import QtCore
3 from PyQt4
import QtGui
4 from PyQt4
.QtCore
import SIGNAL
8 from cola
import gitcfg
9 from cola
import gitcmds
11 from cola
import qtutils
12 from cola
import signals
13 from cola
.qt
import GitRefLineEdit
14 from cola
.widgets
import defs
15 from cola
.widgets
import standard
18 def install_command_wrapper():
19 cmd_wrapper
= ActionCommandWrapper()
20 cola
.factory().add_command_wrapper(cmd_wrapper
)
23 def get_config_actions():
24 cfg
= gitcfg
.instance()
25 names
= cfg
.get_guitool_names()
29 def run_command(title
, command
):
30 """Show a command widget"""
31 view
= GitCommandWidget(qtutils
.active_window())
32 view
.setWindowModality(QtCore
.Qt
.ApplicationModal
)
33 view
.set_command(command
)
34 view
.setWindowTitle(title
)
39 return (view
.exitstatus
, view
.out
, view
.err
)
42 class GitCommandWidget(standard
.Dialog
):
43 """Nice TextView that reads the output of a command syncronously"""
44 # Keep us in scope otherwise PyQt kills the widget
45 def __init__(self
, parent
=None):
46 standard
.Dialog
.__init
__(self
, parent
)
49 # Construct the process
50 self
.proc
= QtCore
.QProcess(self
)
55 self
._layout
= QtGui
.QVBoxLayout(self
)
56 self
._layout
.setContentsMargins(3, 3, 3, 3)
58 # Create the text browser
59 self
.output_text
= QtGui
.QTextBrowser(self
)
60 self
.output_text
.setAcceptDrops(False)
61 self
.output_text
.setTabChangesFocus(True)
62 self
.output_text
.setUndoRedoEnabled(False)
63 self
.output_text
.setReadOnly(True)
64 self
.output_text
.setAcceptRichText(False)
66 self
._layout
.addWidget(self
.output_text
)
68 # Create abort / close buttons
69 self
.button_abort
= QtGui
.QPushButton(self
)
70 self
.button_abort
.setText(self
.tr('Abort'))
71 self
.button_close
= QtGui
.QPushButton(self
)
72 self
.button_close
.setText(self
.tr('Close'))
74 # Put them in a horizontal layout at the bottom.
75 self
.button_box
= QtGui
.QDialogButtonBox(self
)
76 self
.button_box
.addButton(self
.button_abort
, QtGui
.QDialogButtonBox
.RejectRole
)
77 self
.button_box
.addButton(self
.button_close
, QtGui
.QDialogButtonBox
.AcceptRole
)
78 self
._layout
.addWidget(self
.button_box
)
80 # Connect the signals to the process
81 self
.connect(self
.proc
, SIGNAL('readyReadStandardOutput()'), self
.readOutput
)
82 self
.connect(self
.proc
, SIGNAL('readyReadStandardError()'), self
.readErrors
)
83 self
.connect(self
.proc
, SIGNAL('finished(int)'), self
.finishProc
)
84 self
.connect(self
.proc
, SIGNAL('stateChanged(QProcess::ProcessState)'), self
.stateChanged
)
86 # Connect the signlas to the buttons
87 self
.connect(self
.button_abort
, SIGNAL('clicked()'), self
.abortProc
)
88 self
.connect(self
.button_close
, SIGNAL('clicked()'), self
.close
)
89 # Start with abort disabled - will be enabled when the process is run.
90 self
.button_abort
.setEnabled(False)
92 def set_command(self
, command
):
93 self
.command
= command
96 """Runs the process"""
97 self
.proc
.start(self
.command
[0], QtCore
.QStringList(self
.command
[1:]))
100 rawbytes
= self
.proc
.readAllStandardOutput()
105 self
.append_text(data
)
107 def readErrors(self
):
108 rawbytes
= self
.proc
.readAllStandardError()
113 self
.append_text(data
)
115 def append_text(self
, txt
):
116 cursor
= self
.output_text
.textCursor()
117 cursor
.movePosition(cursor
.End
)
118 cursor
.insertText(core
.decode(txt
))
119 cursor
.movePosition(cursor
.End
)
120 self
.output_text
.setTextCursor(cursor
)
123 if self
.proc
.state() != QtCore
.QProcess
.NotRunning
:
124 # Terminate seems to do nothing in windows
125 self
.proc
.terminate()
127 QtCore
.QTimer
.singleShot(1000, self
.proc
, QtCore
.SLOT('kill()'))
129 def closeEvent(self
, event
):
130 if self
.proc
.state() != QtCore
.QProcess
.NotRunning
:
131 # The process is still running, make sure we really want to abort.
132 title
= 'Abort Action'
133 msg
= ('An action is still running.\n'
134 'Terminating it could result in data loss.')
135 info_text
= 'Abort the action?'
136 ok_text
= 'Abort Action'
137 if qtutils
.confirm(title
, msg
, info_text
, ok_text
,
138 default
=False, icon
=qtutils
.discard_icon()):
146 return standard
.Dialog
.closeEvent(self
, event
)
148 def stateChanged(self
, newstate
):
149 # State of process has changed - change the abort button state.
150 if newstate
== QtCore
.QProcess
.NotRunning
:
151 self
.button_abort
.setEnabled(False)
153 self
.button_abort
.setEnabled(True)
155 def finishProc(self
, status
):
156 self
.exitstatus
= status
159 class ActionCommandWrapper(object):
162 signals
.run_config_action
: self
.run_config_action
,
163 signals
.run_command
: run_command
,
166 def run_config_action(self
, name
, opts
):
167 dlg
= ActionDialog(qtutils
.active_window(), name
, opts
)
169 if dlg
.exec_() != QtGui
.QDialog
.Accepted
:
171 rev
= unicode(dlg
.revision())
173 opts
['revision'] = rev
174 args
= unicode(dlg
.args())
180 class ActionDialog(standard
.Dialog
):
181 def __init__(self
, parent
, name
, opts
):
182 standard
.Dialog
.__init
__(self
, parent
)
183 self
.setWindowModality(QtCore
.Qt
.ApplicationModal
)
187 self
.layt
= QtGui
.QVBoxLayout()
188 self
.layt
.setMargin(defs
.margin
)
189 self
.layt
.setSpacing(defs
.spacing
)
190 self
.setLayout(self
.layt
)
192 title
= opts
.get('title')
194 self
.setWindowTitle(os
.path
.expandvars(title
))
196 self
.prompt
= QtGui
.QLabel()
198 prompt
= opts
.get('prompt')
200 self
.prompt
.setText(os
.path
.expandvars(prompt
))
201 self
.layt
.addWidget(self
.prompt
)
204 self
.argslabel
= QtGui
.QLabel()
205 if 'argprompt' not in opts
or opts
.get('argprompt') is True:
206 argprompt
= qtutils
.tr('Arguments')
208 argprompt
= opts
.get('argprompt')
210 self
.argslabel
.setText(argprompt
)
212 self
.argstxt
= QtGui
.QLineEdit()
213 self
.argslayt
= QtGui
.QHBoxLayout()
214 self
.argslayt
.addWidget(self
.argslabel
)
215 self
.argslayt
.addWidget(self
.argstxt
)
216 self
.layt
.addLayout(self
.argslayt
)
218 if not self
.opts
.get('argprompt'):
219 self
.argslabel
.setMinimumSize(1, 1)
220 self
.argstxt
.setMinimumSize(1, 1)
222 self
.argslabel
.hide()
225 ('Local Branch', gitcmds
.branch_list(remote
=False)),
226 ('Tracking Branch', gitcmds
.branch_list(remote
=True)),
227 ('Tag', gitcmds
.tag_list()),
230 if 'revprompt' not in opts
or opts
.get('revprompt') is True:
231 revprompt
= qtutils
.tr('Revision')
233 revprompt
= opts
.get('revprompt')
234 self
.revselect
= RevisionSelector(self
, revs
)
235 self
.revselect
.set_revision_label(revprompt
)
236 self
.layt
.addWidget(self
.revselect
)
238 if not opts
.get('revprompt'):
239 self
.revselect
.hide()
242 self
.btnlayt
= QtGui
.QHBoxLayout()
243 self
.btnlayt
.addStretch()
244 self
.closebtn
= qt
.create_button(text
=self
.tr('Close'), layout
=self
.btnlayt
)
245 self
.runbtn
= qt
.create_button(text
=self
.tr('Run'), layout
=self
.btnlayt
)
246 self
.runbtn
.setDefault(True)
247 self
.layt
.addLayout(self
.btnlayt
)
249 self
.connect(self
.closebtn
, SIGNAL('clicked()'), self
.reject
)
250 self
.connect(self
.runbtn
, SIGNAL('clicked()'), self
.accept
)
252 # Widen the dialog by default
253 self
.resize(666, self
.height())
256 return self
.revselect
.revision()
259 return self
.argstxt
.text()
262 class RevisionSelector(QtGui
.QWidget
):
263 def __init__(self
, parent
, revs
):
264 QtGui
.QWidget
.__init
__(self
, parent
)
267 self
._revdict
= dict(revs
)
269 self
._layt
= QtGui
.QVBoxLayout()
270 self
._layt
.setMargin(0)
271 self
.setLayout(self
._layt
)
273 self
._rev
_layt
= QtGui
.QHBoxLayout()
274 self
._rev
_layt
.setMargin(0)
276 self
._rev
_label
= QtGui
.QLabel()
277 self
._rev
_layt
.addWidget(self
._rev
_label
)
279 self
._revision
= GitRefLineEdit()
280 self
._rev
_layt
.addWidget(self
._revision
)
282 self
._layt
.addLayout(self
._rev
_layt
)
284 self
._radio
_layt
= QtGui
.QHBoxLayout()
285 self
._radio
_btns
= {}
287 # Create the radio buttons
288 for label
, rev_list
in self
._revs
:
289 radio
= QtGui
.QRadioButton()
290 radio
.setText(self
.tr(label
))
291 radio
.setObjectName(label
)
292 self
.connect(radio
, SIGNAL('clicked()'), self
._set
_revision
_list
)
293 self
._radio
_layt
.addWidget(radio
)
294 self
._radio
_btns
[label
] = radio
296 self
._radio
_layt
.addStretch()
298 self
._layt
.addLayout(self
._radio
_layt
)
300 self
._rev
_list
= QtGui
.QListWidget()
301 self
._layt
.addWidget(self
._rev
_list
)
303 label
, rev_list
= self
._revs
[0]
304 self
._radio
_btns
[label
].setChecked(True)
305 qtutils
.set_items(self
._rev
_list
, rev_list
)
307 self
.connect(self
._rev
_list
, SIGNAL('itemSelectionChanged()'),
308 self
._rev
_list
_selection
_changed
)
311 return self
._revision
.text()
313 def set_revision_label(self
, txt
):
314 self
._rev
_label
.setText(txt
)
316 def _set_revision_list(self
):
317 sender
= str(self
.sender().objectName())
318 revs
= self
._revdict
[sender
]
319 qtutils
.set_items(self
._rev
_list
, revs
)
321 def _rev_list_selection_changed(self
):
322 items
= self
._rev
_list
.selectedItems()
325 self
._revision
.setText(items
[0].text())