widgets.cfgactions: Remove global references
[git-cola.git] / cola / widgets / cfgactions.py
blob17fe5ad2c3fdd42e255cff141515f6d4899d5d7c
1 import os
2 from PyQt4 import QtCore
3 from PyQt4 import QtGui
4 from PyQt4.QtCore import SIGNAL
6 import cola
7 from cola import core
8 from cola import gitcfg
9 from cola import gitcmds
10 from cola import qt
11 from cola import qtutils
12 from cola import signals
13 from cola.views import revselect
14 from cola.views import standard
17 def install_command_wrapper(parent):
18 cmd_wrapper = ActionCommandWrapper(parent)
19 cola.factory().add_command_wrapper(cmd_wrapper)
22 def get_config_actions():
23 cfg = gitcfg.instance()
24 names = cfg.get_guitool_names()
25 return names or []
28 def run_command(parent, title, command):
29 """Show a command widget"""
31 view = GitCommandWidget(parent)
32 view.setWindowModality(QtCore.Qt.ApplicationModal)
33 view.set_command(command)
34 view.setWindowTitle(title)
35 if not parent:
36 qtutils.center_on_screen(view)
37 view.run()
38 view.show()
39 view.raise_()
40 view.exec_()
41 return (view.exitstatus, view.out, view.err)
44 class GitCommandWidget(standard.StandardDialog):
45 """Nice TextView that reads the output of a command syncronously"""
46 # Keep us in scope otherwise PyQt kills the widget
47 def __init__(self, parent=None):
48 standard.StandardDialog.__init__(self, parent=parent)
49 self.resize(720, 420)
51 # Construct the process
52 self.proc = QtCore.QProcess(self)
53 self.exitstatus = 0
54 self.out = ''
55 self.err = ''
57 self._layout = QtGui.QVBoxLayout(self)
58 self._layout.setContentsMargins(3, 3, 3, 3)
60 # Create the text browser
61 self.output_text = QtGui.QTextBrowser(self)
62 self.output_text.setAcceptDrops(False)
63 self.output_text.setTabChangesFocus(True)
64 self.output_text.setUndoRedoEnabled(False)
65 self.output_text.setReadOnly(True)
66 self.output_text.setAcceptRichText(False)
68 self._layout.addWidget(self.output_text)
70 # Create abort / close buttons
71 self.button_abort = QtGui.QPushButton(self)
72 self.button_abort.setText(self.tr('Abort'))
73 self.button_close = QtGui.QPushButton(self)
74 self.button_close.setText(self.tr('Close'))
76 # Put them in a horizontal layout at the bottom.
77 self.button_box = QtGui.QDialogButtonBox(self)
78 self.button_box.addButton(self.button_abort, QtGui.QDialogButtonBox.RejectRole)
79 self.button_box.addButton(self.button_close, QtGui.QDialogButtonBox.AcceptRole)
80 self._layout.addWidget(self.button_box)
82 # Connect the signals to the process
83 self.connect(self.proc, SIGNAL('readyReadStandardOutput()'), self.readOutput)
84 self.connect(self.proc, SIGNAL('readyReadStandardError()'), self.readErrors)
85 self.connect(self.proc, SIGNAL('finished(int)'), self.finishProc)
86 self.connect(self.proc, SIGNAL('stateChanged(QProcess::ProcessState)'), self.stateChanged)
88 # Connect the signlas to the buttons
89 self.connect(self.button_abort, SIGNAL('clicked()'), self.abortProc)
90 self.connect(self.button_close, SIGNAL('clicked()'), self.close)
91 # Start with abort disabled - will be enabled when the process is run.
92 self.button_abort.setEnabled(False)
94 def set_command(self, command):
95 self.command = command
97 def run(self):
98 """Runs the process"""
99 self.proc.start(self.command[0], QtCore.QStringList(self.command[1:]))
101 def readOutput(self):
102 rawbytes = self.proc.readAllStandardOutput()
103 data = ''
104 for b in rawbytes:
105 data += b
106 self.out += data
107 self.append_text(data)
109 def readErrors(self):
110 rawbytes = self.proc.readAllStandardError()
111 data = ''
112 for b in rawbytes:
113 data += b
114 self.err += data
115 self.append_text(data)
117 def append_text(self, txt):
118 cursor = self.output_text.textCursor()
119 cursor.movePosition(cursor.End)
120 cursor.insertText(core.decode(txt))
121 cursor.movePosition(cursor.End)
122 self.output_text.setTextCursor(cursor)
124 def abortProc(self):
125 if self.proc.state() != QtCore.QProcess.NotRunning:
126 # Terminate seems to do nothing in windows
127 self.proc.terminate()
128 # Kill the process.
129 QtCore.QTimer.singleShot(1000, self.proc, QtCore.SLOT('kill()'))
131 def closeEvent(self, event):
132 if self.proc.state() != QtCore.QProcess.NotRunning:
133 # The process is still running, make sure we really want to abort.
134 reply = QtGui.QMessageBox.question(self, 'Message',
135 self.tr('Abort process?'),
136 QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
137 if reply == QtGui.QMessageBox.Yes:
138 self.abortProc()
139 event.accept()
140 else:
141 event.ignore()
142 else:
143 event.accept()
145 def stateChanged(self, newstate):
146 # State of process has changed - change the abort button state.
147 if newstate == QtCore.QProcess.NotRunning:
148 self.button_abort.setEnabled(False)
149 else:
150 self.button_abort.setEnabled(True)
152 def finishProc(self, status ):
153 self.exitstatus = status
156 class ActionCommandWrapper(object):
157 def __init__(self, parent):
158 self.parent = parent
159 self.callbacks = {
160 signals.run_config_action: self._run_config_action,
161 signals.run_command: self._run_command,
164 def _run_command(self, title, cmd):
165 return run_command(self.parent, title, cmd)
167 def _run_config_action(self, name, opts):
168 dlg = ActionDialog(self.parent, name, opts)
169 dlg.show()
170 if dlg.exec_() != QtGui.QDialog.Accepted:
171 return False
172 rev = unicode(dlg.revision())
173 if rev:
174 opts['revision'] = rev
175 args = unicode(dlg.args())
176 if args:
177 opts['args'] = args
178 return True
181 class ActionDialog(standard.StandardDialog):
182 def __init__(self, parent, name, opts):
183 standard.StandardDialog.__init__(self, parent)
184 self.name = name
185 self.opts = opts
187 self.layt = QtGui.QVBoxLayout()
188 self.layt.setMargin(10)
189 self.setLayout(self.layt)
191 title = opts.get('title')
192 if title:
193 self.setWindowTitle(os.path.expandvars(title))
195 self.prompt = QtGui.QLabel()
197 prompt = opts.get('prompt')
198 if 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 = qtutils.tr('Arguments')
206 else:
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)
220 self.argstxt.hide()
221 self.argslabel.hide()
223 revs = (
224 ('Local Branch', gitcmds.branch_list(remote=False)),
225 ('Tracking Branch', gitcmds.branch_list(remote=True)),
226 ('Tag', gitcmds.tag_list()),
229 if 'revprompt' not in opts or opts.get('revprompt') is True:
230 revprompt = qtutils.tr('Revision')
231 else:
232 revprompt = opts.get('revprompt')
233 self.revselect = revselect.RevisionSelector(self, revs=revs)
234 self.revselect.set_revision_label(revprompt)
235 self.layt.addWidget(self.revselect)
237 if not opts.get('revprompt'):
238 self.revselect.hide()
240 # Close/Run buttons
241 self.btnlayt = QtGui.QHBoxLayout()
242 self.btnspacer = QtGui.QSpacerItem(1, 1,
243 QtGui.QSizePolicy.MinimumExpanding,
244 QtGui.QSizePolicy.Minimum)
245 self.btnlayt.addItem(self.btnspacer)
246 self.closebtn = qt.create_button(self.tr('Close'), self.btnlayt)
247 self.runbtn = qt.create_button(self.tr('Run'), self.btnlayt)
248 self.runbtn.setDefault(True)
249 self.layt.addLayout(self.btnlayt)
251 self.connect(self.closebtn, SIGNAL('clicked()'), self.reject)
252 self.connect(self.runbtn, SIGNAL('clicked()'), self.accept)
254 # Widen the dialog by default
255 self.resize(666, self.height())
257 def revision(self):
258 return self.revselect.revision()
260 def args(self):
261 return self.argstxt.text()