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