Branch Selection Dialog: mark checked out branch
[egit.git] / org.eclipse.egit.ui / src / org / eclipse / egit / ui / internal / dialogs / BranchSelectionDialog.java
blob3fdc5c85dd8ad356ef35d4c3d1206785909bb519
1 /*******************************************************************************
2 * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
3 * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
4 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
5 * Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com>
7 * All rights reserved. This program and the accompanying materials
8 * are made available under the terms of the Eclipse Public License v1.0
9 * which accompanies this distribution, and is available at
10 * http://www.eclipse.org/legal/epl-v10.html
11 *******************************************************************************/
12 package org.eclipse.egit.ui.internal.dialogs;
14 import java.io.IOException;
15 import java.util.ArrayList;
16 import java.util.List;
18 import org.eclipse.egit.ui.Activator;
19 import org.eclipse.egit.ui.UIText;
20 import org.eclipse.egit.ui.internal.ValidationUtils;
21 import org.eclipse.egit.ui.internal.repository.RepositoriesViewContentProvider;
22 import org.eclipse.egit.ui.internal.repository.RepositoriesViewLabelProvider;
23 import org.eclipse.egit.ui.internal.repository.tree.LocalBranchesNode;
24 import org.eclipse.egit.ui.internal.repository.tree.RefNode;
25 import org.eclipse.egit.ui.internal.repository.tree.RemoteBranchesNode;
26 import org.eclipse.egit.ui.internal.repository.tree.RepositoryTreeNode;
27 import org.eclipse.egit.ui.internal.repository.tree.RepositoryTreeNodeType;
28 import org.eclipse.egit.ui.internal.repository.tree.TagNode;
29 import org.eclipse.egit.ui.internal.repository.tree.TagsNode;
30 import org.eclipse.jface.dialogs.IDialogConstants;
31 import org.eclipse.jface.dialogs.InputDialog;
32 import org.eclipse.jface.dialogs.TitleAreaDialog;
33 import org.eclipse.jface.layout.GridDataFactory;
34 import org.eclipse.jface.layout.GridLayoutFactory;
35 import org.eclipse.jface.resource.JFaceResources;
36 import org.eclipse.jface.viewers.IOpenListener;
37 import org.eclipse.jface.viewers.ISelectionChangedListener;
38 import org.eclipse.jface.viewers.IStructuredSelection;
39 import org.eclipse.jface.viewers.OpenEvent;
40 import org.eclipse.jface.viewers.SelectionChangedEvent;
41 import org.eclipse.jface.viewers.StructuredSelection;
42 import org.eclipse.jface.viewers.TreeViewer;
43 import org.eclipse.jface.window.Window;
44 import org.eclipse.jgit.lib.Constants;
45 import org.eclipse.jgit.lib.ObjectId;
46 import org.eclipse.jgit.lib.Ref;
47 import org.eclipse.jgit.lib.RefRename;
48 import org.eclipse.jgit.lib.RefUpdate;
49 import org.eclipse.jgit.lib.Repository;
50 import org.eclipse.jgit.lib.RefUpdate.Result;
51 import org.eclipse.osgi.util.NLS;
52 import org.eclipse.swt.SWT;
53 import org.eclipse.swt.events.SelectionAdapter;
54 import org.eclipse.swt.events.SelectionEvent;
55 import org.eclipse.swt.layout.GridLayout;
56 import org.eclipse.swt.widgets.Button;
57 import org.eclipse.swt.widgets.Composite;
58 import org.eclipse.swt.widgets.Shell;
59 import org.eclipse.ui.dialogs.FilteredTree;
60 import org.eclipse.ui.dialogs.PatternFilter;
62 /**
63 * The branch and reset selection dialog
65 public class BranchSelectionDialog extends TitleAreaDialog {
67 private final Repository repo;
69 private TreeViewer branchTree;
71 /**
72 * button which finally triggers the action
74 protected Button confirmationBtn;
76 private Button renameButton;
78 private Button newButton;
80 private String selectedBranch;
82 private final RepositoryTreeNode<Repository> localBranches;
84 private final RepositoryTreeNode<Repository> remoteBranches;
86 private final RepositoryTreeNode<Repository> tags;
88 /**
89 * Construct a dialog to select a branch to reset to or check out
90 * @param parentShell
91 * @param repo
93 public BranchSelectionDialog(Shell parentShell, Repository repo) {
94 super(parentShell);
95 this.repo = repo;
96 localBranches = new LocalBranchesNode(null, this.repo);
97 remoteBranches = new RemoteBranchesNode(null, this.repo);
98 tags = new TagsNode(null, this.repo);
101 @Override
102 protected Composite createDialogArea(Composite base) {
103 Composite parent = (Composite) super.createDialogArea(base);
104 parent.setLayout(GridLayoutFactory.fillDefaults().create());
106 // TODO deprecated constructor for now
107 FilteredTree tree = new FilteredTree(parent, SWT.SINGLE | SWT.BORDER, new PatternFilter());
108 branchTree = tree.getViewer();
109 new RepositoriesViewLabelProvider(branchTree);
110 branchTree.setContentProvider(new RepositoriesViewContentProvider());
112 GridDataFactory.fillDefaults().grab(true, true).hint(500, 300).applyTo(
113 tree);
114 branchTree.addSelectionChangedListener(new ISelectionChangedListener() {
116 public void selectionChanged(SelectionChangedEvent event) {
117 // enable the buttons depending on the selection
119 String refName = refNameFromDialog();
121 boolean tagSelected = refName != null
122 && refName.startsWith(Constants.R_TAGS);
124 boolean branchSelected = refName != null
125 && (refName.startsWith(Constants.R_HEADS) || refName
126 .startsWith(Constants.R_REMOTES));
128 // we don't allow reset on tags, but checkout
129 if (!canConfirmOnTag())
130 confirmationBtn.setEnabled(branchSelected);
131 else
132 confirmationBtn.setEnabled(branchSelected || tagSelected);
134 // we don't support rename on tags
135 if (renameButton != null) {
136 renameButton.setEnabled(branchSelected && !tagSelected
137 && !tagSelected);
140 // new branch can not be based on a tag
141 if (newButton != null) {
142 newButton.setEnabled(branchSelected && !tagSelected);
147 // double-click support
148 branchTree.addOpenListener(new IOpenListener() {
150 public void open(OpenEvent event) {
151 RepositoryTreeNode node = (RepositoryTreeNode) ((IStructuredSelection) branchTree
152 .getSelection()).getFirstElement();
153 if (node.getType() != RepositoryTreeNodeType.REF
154 && node.getType() != RepositoryTreeNodeType.TAG)
155 branchTree.setExpandedState(node, !branchTree
156 .getExpandedState(node));
157 else if (confirmationBtn.isEnabled())
158 okPressed();
163 createCustomArea(parent);
165 String rawTitle = getTitle();
166 String title = NLS.bind(rawTitle, new Object[] { repo.getDirectory() });
168 setTitle(title);
169 setMessage(getMessageText());
170 getShell().setText(title);
172 return parent;
175 @Override
176 public void create() {
177 super.create();
179 List<RepositoryTreeNode> roots = new ArrayList<RepositoryTreeNode>();
180 roots.add(localBranches);
181 roots.add(remoteBranches);
182 roots.add(tags);
184 branchTree.setInput(roots);
186 try {
187 // initially, we mark the current head if it can be determined
188 String fullBranch = repo.getFullBranch();
189 if (!markRef(fullBranch))
190 // if we can't determine a branch, we just expand local branches
191 branchTree.expandToLevel(localBranches, 1);
192 } catch (IOException e) {
193 // ignore
197 private boolean markRef(String refName) {
198 // selects the entry specified by the name
199 if (refName == null)
200 return false;
202 RepositoryTreeNode node;
203 try {
204 if (refName.startsWith(Constants.R_HEADS)) {
205 Ref ref = this.repo.getRef(refName);
206 node = new RefNode(localBranches, this.repo, ref);
207 } else {
208 String mappedRef = Activator.getDefault().getRepositoryUtil()
209 .mapCommitToRef(this.repo, refName, false);
210 if (mappedRef != null
211 && mappedRef.startsWith(Constants.R_REMOTES)) {
212 Ref ref = this.repo.getRef(mappedRef);
213 node = new RefNode(remoteBranches, this.repo, ref);
214 } else if (mappedRef != null
215 && mappedRef.startsWith(Constants.R_TAGS)) {
216 Ref ref = this.repo.getRef(mappedRef);
217 node = new TagNode(tags, this.repo, ref);
218 } else {
219 return false;
222 } catch (IOException e) {
223 return false;
226 branchTree.setSelection(new StructuredSelection(node), true);
227 return true;
231 * @return the selected refName
233 public String getRefName() {
234 return this.selectedBranch;
237 @Override
238 protected void okPressed() {
239 this.selectedBranch = refNameFromDialog();
240 super.okPressed();
243 private String refNameFromDialog() {
244 IStructuredSelection sel = (IStructuredSelection) branchTree
245 .getSelection();
246 if (sel.size() != 1)
247 return null;
248 RepositoryTreeNode node = (RepositoryTreeNode) sel.getFirstElement();
249 if (node.getType() == RepositoryTreeNodeType.REF
250 || node.getType() == RepositoryTreeNodeType.TAG) {
251 return ((Ref) node.getObject()).getName();
253 return null;
256 private InputDialog getRefNameInputDialog(String prompt, final String refPrefix) {
257 InputDialog labelDialog = new InputDialog(
258 getShell(),
259 UIText.BranchSelectionDialog_QuestionNewBranchTitle,
260 prompt,
261 null, ValidationUtils.getRefNameInputValidator(repo, refPrefix));
262 labelDialog.setBlockOnOpen(true);
263 return labelDialog;
266 @Override
267 protected void createButtonsForButtonBar(Composite parent) {
268 newButton = new Button(parent, SWT.PUSH);
269 newButton.setFont(JFaceResources.getDialogFont());
270 newButton.setText(UIText.BranchSelectionDialog_NewBranch);
271 setButtonLayoutData(newButton);
272 ((GridLayout)parent.getLayout()).numColumns++;
274 renameButton = new Button(parent, SWT.PUSH);
275 renameButton.setFont(JFaceResources.getDialogFont());
276 renameButton.setText(UIText.BranchSelectionDialog_Rename);
277 setButtonLayoutData(renameButton);
278 ((GridLayout)parent.getLayout()).numColumns++;
280 renameButton.addSelectionListener(new SelectionAdapter() {
281 public void widgetSelected(SelectionEvent e) {
283 String refName = refNameFromDialog();
284 String refPrefix;
286 // the button should be disabled anyway, but we check again
287 if (refName.equals(Constants.HEAD))
288 return;
290 if (refName.startsWith(Constants.R_HEADS))
291 refPrefix = Constants.R_HEADS;
292 else if (refName.startsWith(Constants.R_REMOTES))
293 refPrefix = Constants.R_REMOTES;
294 else if (refName.startsWith(Constants.R_TAGS))
295 refPrefix = Constants.R_TAGS;
296 else {
297 // the button should be disabled anyway, but we check again
298 return;
301 String branchName = refName.substring(refPrefix.length());
303 InputDialog labelDialog = getRefNameInputDialog(NLS
304 .bind(
305 UIText.BranchSelectionDialog_QuestionNewBranchNameMessage,
306 branchName, refPrefix), refPrefix);
307 if (labelDialog.open() == Window.OK) {
308 String newRefName = refPrefix + labelDialog.getValue();
309 try {
310 RefRename renameRef = repo.renameRef(refName, newRefName);
311 if (renameRef.rename() != Result.RENAMED) {
312 reportError(
313 null,
314 UIText.BranchSelectionDialog_ErrorCouldNotRenameRef,
315 refName, newRefName, renameRef
316 .getResult());
318 branchTree.refresh();
319 markRef(newRefName);
320 } catch (Throwable e1) {
321 reportError(
323 UIText.BranchSelectionDialog_ErrorCouldNotRenameRef,
324 refName, newRefName, e1.getMessage());
329 newButton.addSelectionListener(new SelectionAdapter() {
331 public void widgetSelected(SelectionEvent e) {
332 // check what ref name the user selected, if any.
333 String refName = refNameFromDialog();
335 // the button should be disabled anyway, but we check again
336 if (refName.equals(Constants.HEAD))
337 return;
338 if (refName.startsWith(Constants.R_TAGS))
339 // the button should be disabled anyway, but we check again
340 return;
342 InputDialog labelDialog = getRefNameInputDialog(
344 .bind(
345 UIText.BranchSelectionDialog_QuestionNewBranchMessage,
346 refName, Constants.R_HEADS),
347 Constants.R_HEADS);
349 if (labelDialog.open() == Window.OK) {
350 String newRefName = Constants.R_HEADS + labelDialog.getValue();
351 RefUpdate updateRef;
352 try {
353 updateRef = repo.updateRef(newRefName);
354 Ref startRef = repo.getRef(refName);
355 ObjectId startAt = repo.resolve(refName);
356 String startBranch;
357 if (startRef != null)
358 startBranch = refName;
359 else
360 startBranch = startAt.name();
361 startBranch = repo.shortenRefName(startBranch);
362 updateRef.setNewObjectId(startAt);
363 updateRef.setRefLogMessage("branch: Created from " + startBranch, false); //$NON-NLS-1$
364 updateRef.update();
365 branchTree.refresh();
366 markRef(newRefName);
367 } catch (Throwable e1) {
368 reportError(
370 UIText.BranchSelectionDialog_ErrorCouldNotCreateNewRef,
371 newRefName);
376 confirmationBtn = createButton(parent, IDialogConstants.OK_ID,
377 UIText.BranchSelectionDialog_OkCheckout, true);
378 createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false);
380 // can't advance without a selection
381 confirmationBtn.setEnabled(!branchTree.getSelection().isEmpty());
385 * @return the message shown above the refs tree
387 protected String getMessageText() {
388 return UIText.BranchSelectionDialog_Refs;
392 * Subclasses may add UI elements
393 * @param parent
395 protected void createCustomArea(Composite parent) {
396 // do nothing
400 * Subclasses may change the title of the dialog
401 * @return the title of the dialog
403 protected String getTitle() {
404 return UIText.BranchSelectionDialog_TitleCheckout;
409 * @return if the confirmation button is enabled when a tag is selected
411 protected boolean canConfirmOnTag() {
412 return true;
415 @Override
416 protected int getShellStyle() {
417 return super.getShellStyle() | SWT.RESIZE;
420 private void reportError(Throwable e, String message, Object... args) {
421 String msg = NLS.bind(message, args);
422 Activator.handleError(msg, e, true);