qt: Make the 'text' field optional in create_button()
[git-cola.git] / cola / guicmds.py
blob77d6839ffcfb3ad78bb5e7892349ed72d0a477bb
1 import os
3 import cola
4 from cola import core
5 from cola import gitcmds
6 from cola import qtutils
7 from cola import signals
8 from cola.git import git
9 from cola.widgets.browse import BrowseDialog
10 from cola.widgets.combodlg import ComboDialog
13 def install_command_wrapper():
14 wrapper = CommandWrapper()
15 cola.factory().add_command_wrapper(wrapper)
18 class CommandWrapper(object):
19 def __init__(self):
20 self.callbacks = {
21 signals.confirm: qtutils.confirm,
22 signals.critical: qtutils.critical,
23 signals.information: qtutils.information,
24 signals.question: qtutils.question,
28 def choose_from_combo(title, items):
29 """Quickly choose an item from a list using a combo box"""
30 return ComboDialog(qtutils.active_window(), title=title, items=items).selected()
33 def slot_with_parent(fn, parent):
34 """Return an argument-less method for calling fn(parent=parent)
36 :param fn: - Function reference, must accept 'parent' as a keyword
37 :param parent: - Qt parent widget
39 """
40 def slot():
41 fn(parent=parent)
42 return slot
45 def branch_delete():
46 """Launch the 'Delete Branch' dialog."""
47 branch = choose_from_combo('Delete Branch',
48 cola.model().local_branches)
49 if not branch:
50 return
51 cola.notifier().broadcast(signals.delete_branch, branch)
54 def branch_diff():
55 """Diff against an arbitrary revision, branch, tag, etc."""
56 branch = choose_from_combo('Select Branch, Tag, or Commit-ish',
57 ['HEAD^'] +
58 cola.model().all_branches() +
59 cola.model().tags)
60 if not branch:
61 return
62 cola.notifier().broadcast(signals.diff_mode, branch)
65 def browse_current():
66 """Launch the 'Browse Current Branch' dialog."""
67 branch = gitcmds.current_branch()
68 BrowseDialog.browse(branch)
71 def browse_other():
72 """Prompt for a branch and inspect content at that point in time."""
73 # Prompt for a branch to browse
74 branch = choose_from_combo('Browse Revision...', gitcmds.all_refs())
75 if not branch:
76 return
77 BrowseDialog.browse(branch)
80 def checkout_branch():
81 """Launch the 'Checkout Branch' dialog."""
82 branch = choose_from_combo('Checkout Branch',
83 cola.model().local_branches)
84 if not branch:
85 return
86 cola.notifier().broadcast(signals.checkout_branch, branch)
89 def cherry_pick():
90 """Launch the 'Cherry-Pick' dialog."""
91 from cola.controllers.selectcommits import select_commits
93 revs, summaries = gitcmds.log_helper(all=True)
94 commits = select_commits('Cherry-Pick Commit',
95 revs, summaries, multiselect=False)
96 if not commits:
97 return
98 cola.notifier().broadcast(signals.cherry_pick, commits)
101 def clone_repo(spawn=True):
103 Present GUI controls for cloning a repository
105 A new cola session is invoked when 'spawn' is True.
108 url, ok = qtutils.prompt('Path or URL to clone (Env. $VARS okay)')
109 url = os.path.expandvars(core.encode(url))
110 if not ok or not url:
111 return None
112 try:
113 # Pick a suitable basename by parsing the URL
114 newurl = url.replace('\\', '/')
115 default = newurl.rsplit('/', 1)[-1]
116 if default == '.git':
117 # The end of the URL is /.git, so assume it's a file path
118 default = os.path.basename(os.path.dirname(newurl))
119 if default.endswith('.git'):
120 # The URL points to a bare repo
121 default = default[:-4]
122 if url == '.':
123 # The URL is the current repo
124 default = os.path.basename(os.getcwd())
125 if not default:
126 raise
127 except:
128 cola.notifier().broadcast(signals.information,
129 'Error Cloning',
130 'Could not parse: "%s"' % url)
131 qtutils.log(1, 'Oops, could not parse git url: "%s"' % url)
132 return None
134 # Prompt the user for a directory to use as the parent directory
135 msg = 'Select a parent directory for the new clone'
136 dirname = qtutils.opendir_dialog(msg, cola.model().getcwd())
137 if not dirname:
138 return None
139 count = 1
140 dirname = core.decode(dirname)
141 destdir = os.path.join(dirname, core.decode(default))
142 olddestdir = destdir
143 if os.path.exists(destdir):
144 # An existing path can be specified
145 msg = ('"%s" already exists, cola will create a new directory' %
146 destdir)
147 cola.notifier().broadcast(signals.information,
148 'Directory Exists', msg)
150 # Make sure the new destdir doesn't exist
151 while os.path.exists(destdir):
152 destdir = olddestdir + str(count)
153 count += 1
154 cola.notifier().broadcast(signals.clone, core.decode(url), destdir,
155 spawn=spawn)
156 return destdir
159 def export_patches():
160 """Run 'git format-patch' on a list of commits."""
161 from cola.controllers.selectcommits import select_commits
163 revs, summaries = gitcmds.log_helper()
164 to_export = select_commits('Export Patches', revs, summaries)
165 if not to_export:
166 return
167 to_export.reverse()
168 revs.reverse()
169 cola.notifier().broadcast(signals.format_patch, to_export, revs)
172 def diff_branch():
173 """Launches a diff against a branch."""
174 branch = choose_from_combo('Select Branch, Tag, or Commit-ish',
175 ['HEAD^'] +
176 cola.model().all_branches() +
177 cola.model().tags)
178 if not branch:
179 return
180 files = gitcmds.diff_filenames(branch)
181 if not files:
182 qtutils.critical('No Differences',
183 'No differences found for "%s"' % branch)
184 return
185 filename = BrowseDialog.select_file_from_list(files)
186 if not filename:
187 return
188 cola.notifier().broadcast(signals.branch_mode, branch, filename)
191 def diff_expression():
192 """Diff using an arbitrary expression."""
193 expr = choose_from_combo('Enter Diff Expression',
194 cola.model().all_branches() +
195 cola.model().tags)
196 if not expr:
197 return
198 cola.notifier().broadcast(signals.diff_expr_mode, expr)
201 def goto_grep(line):
202 """Called when Search -> Grep's right-click 'goto' action."""
203 filename, line_number, contents = line.split(':', 2)
204 filename = core.encode(filename)
205 cola.notifier().broadcast(signals.edit, [filename], line_number=line_number)
208 def grep():
209 """Prompt and use 'git grep' to find the content."""
210 # This should be a command in cola.cmds.
211 txt, ok = qtutils.prompt('grep')
212 if not ok:
213 return
214 cola.notifier().broadcast(signals.grep, txt)
217 def open_repo():
218 """Spawn a new cola session."""
219 dirname = qtutils.opendir_dialog('Open Git Repository...',
220 cola.model().getcwd())
221 if not dirname:
222 return
223 cola.notifier().broadcast(signals.open_repo, dirname)
226 def load_commitmsg():
227 """Load a commit message from a file."""
228 filename = qtutils.open_dialog('Load Commit Message...',
229 cola.model().getcwd())
230 if filename:
231 cola.notifier().broadcast(signals.load_commit_message, filename)
234 def rebase():
235 """Rebase onto a branch."""
236 branch = choose_from_combo('Rebase Branch',
237 cola.model().all_branches())
238 if not branch:
239 return
240 #TODO cmd
241 status, output = git.rebase(branch, with_stderr=True, with_status=True)
242 qtutils.log(status, output)
245 def review_branch():
246 """Diff against an arbitrary revision, branch, tag, etc."""
247 branch = choose_from_combo('Select Branch, Tag, or Commit-ish',
248 cola.model().all_branches() +
249 cola.model().tags)
250 if not branch:
251 return
252 cola.notifier().broadcast(signals.review_branch_mode, branch)
255 def fetch():
256 """Launch the 'fetch' remote dialog."""
257 from cola.controllers.remote import remote_action
259 return remote_action('fetch')
262 def push():
263 """Launch the 'push' remote dialog."""
264 from cola.controllers.remote import remote_action
266 return remote_action('push')
269 def pull():
270 """Launch the 'pull' remote dialog."""
271 from cola.controllers.remote import remote_action
273 return remote_action('pull')