guicmds: Add a command wrapper for question() and information()
[git-cola.git] / cola / guicmds.py
blob2115728b61ea0ec82c76ff82c8be6ee75a37e1a5
1 import os
2 from PyQt4 import QtGui
4 import cola
5 import cola.app
6 from cola import core
7 from cola import gitcmd
8 from cola import gitcmds
9 from cola import qtutils
10 from cola import signals
11 from cola.views import itemlist
12 from cola.views import combo
15 def install_command_wrapper(parent):
16 wrapper = CommandWrapper(parent)
17 cola.factory().add_command_wrapper(wrapper)
20 class CommandWrapper(object):
21 def __init__(self, parent):
22 self.parent = parent
23 self.callbacks = {
24 signals.question: self._question,
25 signals.information: self._information,
28 def _question(self, title, msg):
29 return qtutils.question(self.parent, title, msg)
31 def _information(self, title, msg):
32 return qtutils.information(self.parent, title, msg)
35 def choose_from_combo(title, items):
36 """Quickly choose an item from a list using a combo box"""
37 parent = QtGui.QApplication.instance().activeWindow()
38 return combo.ComboView(parent,
39 title=title,
40 items=items).selected()
43 def choose_from_list(title, items=None, dblclick=None):
44 """Quickly choose an item from a list using a list widget"""
45 parent = QtGui.QApplication.instance().activeWindow()
46 return itemlist.ListView(parent,
47 title=title,
48 items=items,
49 dblclick=dblclick).selected()
52 def slot_with_parent(fn, parent):
53 """Return an argument-less method for calling fn(parent=parent)
55 :param fn: - Function reference, must accept 'parent' as a keyword
56 :param parent: - Qt parent widget
58 """
59 def slot():
60 fn(parent=parent)
61 return slot
64 def branch_delete():
65 """Launch the 'Delete Branch' dialog."""
66 branch = choose_from_combo('Delete Branch',
67 cola.model().local_branches)
68 if not branch:
69 return
70 cola.notifier().broadcast(signals.delete_branch, branch)
73 def branch_diff():
74 """Diff against an arbitrary revision, branch, tag, etc."""
75 branch = choose_from_combo('Select Branch, Tag, or Commit-ish',
76 ['HEAD^'] +
77 cola.model().all_branches() +
78 cola.model().tags)
79 if not branch:
80 return
81 cola.notifier().broadcast(signals.diff_mode, branch)
84 def browse_commits():
85 """Launch the 'Browse Commits' dialog."""
86 from cola.controllers.selectcommits import select_commits
88 revs, summaries = gitcmds.log_helper(all=True)
89 select_commits('Browse Commits', revs, summaries)
92 def browse_current():
93 """Launch the 'Browse Current Branch' dialog."""
94 from cola.controllers.repobrowser import browse_git_branch
96 browse_git_branch(gitcmds.current_branch())
99 def browse_other():
100 """Prompt for a branch and inspect content at that point in time."""
101 # Prompt for a branch to browse
102 from cola.controllers.repobrowser import browse_git_branch
104 branch = choose_from_combo('Browse Revision...', gitcmds.all_refs())
105 if not branch:
106 return
107 # Launch the repobrowser
108 browse_git_branch(branch)
111 def checkout_branch():
112 """Launch the 'Checkout Branch' dialog."""
113 branch = choose_from_combo('Checkout Branch',
114 cola.model().local_branches)
115 if not branch:
116 return
117 cola.notifier().broadcast(signals.checkout_branch, branch)
120 def cherry_pick():
121 """Launch the 'Cherry-Pick' dialog."""
122 from cola.controllers.selectcommits import select_commits
124 revs, summaries = gitcmds.log_helper(all=True)
125 commits = select_commits('Cherry-Pick Commit',
126 revs, summaries, multiselect=False)
127 if not commits:
128 return
129 cola.notifier().broadcast(signals.cherry_pick, commits)
132 def clone_repo(parent, spawn=True):
134 Present GUI controls for cloning a repository
136 A new cola session is invoked when 'spawn' is True.
139 url, ok = qtutils.prompt('Path or URL to clone (Env. $VARS okay)')
140 url = os.path.expandvars(url)
141 if not ok or not url:
142 return None
143 try:
144 # Pick a suitable basename by parsing the URL
145 newurl = url.replace('\\', '/')
146 default = newurl.rsplit('/', 1)[-1]
147 if default == '.git':
148 # The end of the URL is /.git, so assume it's a file path
149 default = os.path.basename(os.path.dirname(newurl))
150 if default.endswith('.git'):
151 # The URL points to a bare repo
152 default = default[:-4]
153 if url == '.':
154 # The URL is the current repo
155 default = os.path.basename(os.getcwd())
156 if not default:
157 raise
158 except:
159 cola.notifier().broadcast(signals.information,
160 'Error Cloning',
161 'Could not parse: "%s"' % url)
162 qtutils.log(1, 'Oops, could not parse git url: "%s"' % url)
163 return None
165 # Prompt the user for a directory to use as the parent directory
166 msg = 'Select a parent directory for the new clone'
167 dirname = qtutils.opendir_dialog(parent, msg, cola.model().getcwd())
168 if not dirname:
169 return None
170 count = 1
171 destdir = os.path.join(dirname, default)
172 olddestdir = destdir
173 if os.path.exists(destdir):
174 # An existing path can be specified
175 msg = ('"%s" already exists, cola will create a new directory' %
176 destdir)
177 cola.notifier().broadcast(signals.information,
178 'Directory Exists', msg)
180 # Make sure the new destdir doesn't exist
181 while os.path.exists(destdir):
182 destdir = olddestdir + str(count)
183 count += 1
184 cola.notifier().broadcast(signals.clone, url, destdir,
185 spawn=spawn)
186 return destdir
189 def export_patches():
190 """Run 'git format-patch' on a list of commits."""
191 from cola.controllers.selectcommits import select_commits
193 revs, summaries = gitcmds.log_helper()
194 to_export = select_commits('Export Patches', revs, summaries)
195 if not to_export:
196 return
197 to_export.reverse()
198 revs.reverse()
199 cola.notifier().broadcast(signals.format_patch, to_export, revs)
202 def diff_branch():
203 """Launches a diff against a branch."""
204 branch = choose_from_combo('Select Branch, Tag, or Commit-ish',
205 ['HEAD^'] +
206 cola.model().all_branches() +
207 cola.model().tags)
208 if not branch:
209 return
210 zfiles_str = gitcmd.instance().diff(branch, name_only=True,
211 no_color=True,
212 z=True).rstrip('\0')
213 files = zfiles_str.split('\0')
214 filename = choose_from_list('Select File', files)
215 if not filename:
216 return
217 cola.notifier().broadcast(signals.branch_mode, branch, filename)
220 def diff_expression():
221 """Diff using an arbitrary expression."""
222 expr = choose_from_combo('Enter Diff Expression',
223 cola.model().all_branches() +
224 cola.model().tags)
225 if not expr:
226 return
227 cola.notifier().broadcast(signals.diff_expr_mode, expr)
230 def goto_grep(line):
231 """Called when Search -> Grep's right-click 'goto' action."""
232 filename, line_number, contents = line.split(':', 2)
233 filename = core.encode(filename)
234 cola.notifier().broadcast(signals.edit, [filename], line_number=line_number)
237 def grep():
238 """Prompt and use 'git grep' to find the content."""
239 # This should be a command in cola.commands.
240 txt, ok = qtutils.prompt('grep')
241 if not ok:
242 return
243 cola.notifier().broadcast(signals.grep, txt)
246 def open_repo(parent):
247 """Spawn a new cola session."""
248 dirname = qtutils.opendir_dialog(parent,
249 'Open Git Repository...',
250 cola.model().getcwd())
251 if not dirname:
252 return
253 cola.notifier().broadcast(signals.open_repo, dirname)
255 def open_repo_slot(parent):
256 return slot_with_parent(open_repo, parent)
259 def load_commitmsg(parent):
260 """Load a commit message from a file."""
261 filename = qtutils.open_dialog(parent,
262 'Load Commit Message...',
263 cola.model().getcwd())
264 if filename:
265 cola.notifier().broadcast(signals.load_commit_message, filename)
267 def load_commitmsg_slot(parent):
268 return slot_with_parent(load_commitmsg, parent)
271 def rebase():
272 """Rebase onto a branch."""
273 branch = choose_from_combo('Rebase Branch',
274 cola.model().all_branches())
275 if not branch:
276 return
277 #TODO cmd
278 status, output = gitcmd.instance().rebase(branch,
279 with_stderr=True,
280 with_status=True)
281 qtutils.log(status, output)
284 def review_branch():
285 """Diff against an arbitrary revision, branch, tag, etc."""
286 branch = choose_from_combo('Select Branch, Tag, or Commit-ish',
287 cola.model().all_branches() +
288 cola.model().tags)
289 if not branch:
290 return
291 cola.notifier().broadcast(signals.review_branch_mode, branch)
294 def fetch(parent):
295 """Launch the 'fetch' remote dialog."""
296 from cola.controllers.remote import remote_action
298 remote_action(parent, 'fetch')
300 def fetch_slot(parent):
301 return slot_with_parent(fetch, parent)
304 def push(parent):
305 """Launch the 'push' remote dialog."""
306 from cola.controllers.remote import remote_action
308 remote_action(parent, 'push')
310 def push_slot(parent):
311 return slot_with_parent(push, parent)
314 def pull(parent):
315 """Launch the 'pull' remote dialog."""
316 from cola.controllers.remote import remote_action
318 remote_action(parent, 'pull')
320 def pull_slot(parent):
321 return slot_with_parent(pull, parent)