1
/*******************************************************************************
2 * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
3 * Copyright (C) 2007, Robin Rosenberg <me@lathund.dewire.com.dewire.com>
4 * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
5 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
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
.RepositoryTreeNode
;
24 import org
.eclipse
.egit
.ui
.internal
.repository
.RepositoryTreeNode
.RepositoryTreeNodeType
;
25 import org
.eclipse
.jface
.dialogs
.Dialog
;
26 import org
.eclipse
.jface
.dialogs
.IDialogConstants
;
27 import org
.eclipse
.jface
.dialogs
.InputDialog
;
28 import org
.eclipse
.jface
.layout
.GridDataFactory
;
29 import org
.eclipse
.jface
.layout
.GridLayoutFactory
;
30 import org
.eclipse
.jface
.resource
.JFaceResources
;
31 import org
.eclipse
.jface
.viewers
.IOpenListener
;
32 import org
.eclipse
.jface
.viewers
.ISelectionChangedListener
;
33 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
34 import org
.eclipse
.jface
.viewers
.OpenEvent
;
35 import org
.eclipse
.jface
.viewers
.SelectionChangedEvent
;
36 import org
.eclipse
.jface
.viewers
.StructuredSelection
;
37 import org
.eclipse
.jface
.viewers
.TreeViewer
;
38 import org
.eclipse
.jface
.window
.Window
;
39 import org
.eclipse
.jgit
.lib
.Constants
;
40 import org
.eclipse
.jgit
.lib
.ObjectId
;
41 import org
.eclipse
.jgit
.lib
.Ref
;
42 import org
.eclipse
.jgit
.lib
.RefRename
;
43 import org
.eclipse
.jgit
.lib
.RefUpdate
;
44 import org
.eclipse
.jgit
.lib
.Repository
;
45 import org
.eclipse
.jgit
.lib
.RefUpdate
.Result
;
46 import org
.eclipse
.osgi
.util
.NLS
;
47 import org
.eclipse
.swt
.SWT
;
48 import org
.eclipse
.swt
.events
.SelectionAdapter
;
49 import org
.eclipse
.swt
.events
.SelectionEvent
;
50 import org
.eclipse
.swt
.layout
.GridLayout
;
51 import org
.eclipse
.swt
.widgets
.Button
;
52 import org
.eclipse
.swt
.widgets
.Composite
;
53 import org
.eclipse
.swt
.widgets
.Label
;
54 import org
.eclipse
.swt
.widgets
.Shell
;
57 * The branch and reset selection dialog
60 public class BranchSelectionDialog
extends Dialog
{
62 private final Repository repo
;
64 private TreeViewer branchTree
;
67 * button which finally triggers the action
69 protected Button confirmationBtn
;
71 private Button renameButton
;
73 private Button newButton
;
75 private String selectedBranch
;
77 private final RepositoryTreeNode
<Repository
> localBranches
;
79 private final RepositoryTreeNode
<Repository
> remoteBranches
;
81 private final RepositoryTreeNode
<Repository
> tags
;
84 * Construct a dialog to select a branch to reset to or check out
88 public BranchSelectionDialog(Shell parentShell
, Repository repo
) {
91 localBranches
= new RepositoryTreeNode
<Repository
>(null,
92 RepositoryTreeNodeType
.LOCALBRANCHES
, this.repo
, this.repo
);
93 remoteBranches
= new RepositoryTreeNode
<Repository
>(null,
94 RepositoryTreeNodeType
.REMOTEBRANCHES
, this.repo
, this.repo
);
95 tags
= new RepositoryTreeNode
<Repository
>(null,
96 RepositoryTreeNodeType
.TAGS
, this.repo
, this.repo
);
100 protected Composite
createDialogArea(Composite base
) {
101 Composite parent
= (Composite
) super.createDialogArea(base
);
102 parent
.setLayout(GridLayoutFactory
.swtDefaults().create());
103 new Label(parent
, SWT
.NONE
).setText(getRefsLabel());
105 branchTree
= new TreeViewer(parent
, SWT
.SINGLE
| SWT
.BORDER
);
106 new RepositoriesViewLabelProvider(branchTree
);
107 branchTree
.setContentProvider(new RepositoriesViewContentProvider());
109 GridDataFactory
.fillDefaults().grab(true, true).hint(500, 300).applyTo(
110 branchTree
.getTree());
111 branchTree
.addSelectionChangedListener(new ISelectionChangedListener() {
113 public void selectionChanged(SelectionChangedEvent event
) {
114 // enable the buttons depending on the selection
116 String refName
= refNameFromDialog();
118 boolean tagSelected
= refName
!= null
119 && refName
.startsWith(Constants
.R_TAGS
);
121 boolean branchSelected
= refName
!= null
122 && (refName
.startsWith(Constants
.R_HEADS
) || refName
123 .startsWith(Constants
.R_REMOTES
));
125 // we don't allow reset on tags, but checkout
126 if (!canConfirmOnTag())
127 confirmationBtn
.setEnabled(branchSelected
);
129 confirmationBtn
.setEnabled(branchSelected
|| tagSelected
);
131 // we don't support rename on tags
132 if (renameButton
!= null) {
133 renameButton
.setEnabled(branchSelected
&& !tagSelected
137 // new branch can not be based on a tag
138 if (newButton
!= null) {
139 newButton
.setEnabled(branchSelected
&& !tagSelected
);
144 // double-click support
145 branchTree
.addOpenListener(new IOpenListener() {
147 public void open(OpenEvent event
) {
148 RepositoryTreeNode node
= (RepositoryTreeNode
) ((IStructuredSelection
) branchTree
149 .getSelection()).getFirstElement();
150 if (node
.getType() != RepositoryTreeNodeType
.REF
)
151 branchTree
.setExpandedState(node
, !branchTree
152 .getExpandedState(node
));
153 else if (confirmationBtn
.isEnabled())
159 createCustomArea(parent
);
161 String rawTitle
= getTitle();
164 NLS
.bind(rawTitle
, new Object
[] { repo
.getDirectory() }));
170 public void create() {
173 List
<RepositoryTreeNode
> roots
= new ArrayList
<RepositoryTreeNode
>();
174 roots
.add(localBranches
);
175 roots
.add(remoteBranches
);
178 branchTree
.setInput(roots
);
181 // initially, we mark the current head if it can be determined
182 String fullBranch
= repo
.getFullBranch();
183 if (!markRef(fullBranch
))
184 // if we can't determine a branch, we just expand local branches
185 branchTree
.expandToLevel(localBranches
, 1);
186 } catch (IOException e
) {
191 private boolean markRef(String refName
) {
192 // selects the entry specified by the name
197 actRef
= repo
.getRef(refName
);
198 } catch (IOException e
) {
206 RepositoryTreeNode
<Repository
> parentNode
;
207 if (refName
.startsWith(Constants
.R_HEADS
)) {
208 parentNode
= localBranches
;
209 // TODO fix this: if we are on a local branch or tag, we must do the
210 // indirection through the commit
211 } else if (refName
.startsWith(Constants
.R_REMOTES
)) {
212 parentNode
= remoteBranches
;
213 } else if (refName
.startsWith(Constants
.R_TAGS
)) {
219 RepositoryTreeNode
<Ref
> actNode
= new RepositoryTreeNode
<Ref
>(
220 parentNode
, RepositoryTreeNodeType
.REF
, repo
, actRef
);
221 branchTree
.setSelection(new StructuredSelection(actNode
), true);
226 * @return the selected refName
228 public String
getRefName() {
229 return this.selectedBranch
;
233 protected void okPressed() {
234 this.selectedBranch
= refNameFromDialog();
238 private String
refNameFromDialog() {
239 IStructuredSelection sel
= (IStructuredSelection
) branchTree
243 RepositoryTreeNode node
= (RepositoryTreeNode
) sel
.getFirstElement();
244 if (node
.getType() == RepositoryTreeNodeType
.REF
245 || node
.getType() == RepositoryTreeNodeType
.TAG
) {
246 return ((Ref
) node
.getObject()).getName();
251 private InputDialog
getRefNameInputDialog(String prompt
, final String refPrefix
) {
252 InputDialog labelDialog
= new InputDialog(
254 UIText
.BranchSelectionDialog_QuestionNewBranchTitle
,
256 null, ValidationUtils
.getRefNameInputValidator(repo
, refPrefix
));
257 labelDialog
.setBlockOnOpen(true);
262 protected void createButtonsForButtonBar(Composite parent
) {
263 newButton
= new Button(parent
, SWT
.PUSH
);
264 newButton
.setFont(JFaceResources
.getDialogFont());
265 newButton
.setText(UIText
.BranchSelectionDialog_NewBranch
);
266 setButtonLayoutData(newButton
);
267 ((GridLayout
)parent
.getLayout()).numColumns
++;
269 renameButton
= new Button(parent
, SWT
.PUSH
);
270 renameButton
.setFont(JFaceResources
.getDialogFont());
271 renameButton
.setText(UIText
.BranchSelectionDialog_Rename
);
272 setButtonLayoutData(renameButton
);
273 ((GridLayout
)parent
.getLayout()).numColumns
++;
275 renameButton
.addSelectionListener(new SelectionAdapter() {
276 public void widgetSelected(SelectionEvent e
) {
278 String refName
= refNameFromDialog();
281 // the button should be disabled anyway, but we check again
282 if (refName
.equals(Constants
.HEAD
))
285 if (refName
.startsWith(Constants
.R_HEADS
))
286 refPrefix
= Constants
.R_HEADS
;
287 else if (refName
.startsWith(Constants
.R_REMOTES
))
288 refPrefix
= Constants
.R_REMOTES
;
289 else if (refName
.startsWith(Constants
.R_TAGS
))
290 refPrefix
= Constants
.R_TAGS
;
292 // the button should be disabled anyway, but we check again
296 String branchName
= refName
.substring(refPrefix
.length());
298 InputDialog labelDialog
= getRefNameInputDialog(NLS
300 UIText
.BranchSelectionDialog_QuestionNewBranchNameMessage
,
301 branchName
, refPrefix
), refPrefix
);
302 if (labelDialog
.open() == Window
.OK
) {
303 String newRefName
= refPrefix
+ labelDialog
.getValue();
305 RefRename renameRef
= repo
.renameRef(refName
, newRefName
);
306 if (renameRef
.rename() != Result
.RENAMED
) {
309 UIText
.BranchSelectionDialog_ErrorCouldNotRenameRef
,
310 refName
, newRefName
, renameRef
313 branchTree
.refresh();
315 } catch (Throwable e1
) {
318 UIText
.BranchSelectionDialog_ErrorCouldNotRenameRef
,
319 refName
, newRefName
, e1
.getMessage());
324 newButton
.addSelectionListener(new SelectionAdapter() {
326 public void widgetSelected(SelectionEvent e
) {
327 // check what ref name the user selected, if any.
328 String refName
= refNameFromDialog();
330 // the button should be disabled anyway, but we check again
331 if (refName
.equals(Constants
.HEAD
))
333 if (refName
.startsWith(Constants
.R_TAGS
))
334 // the button should be disabled anyway, but we check again
337 InputDialog labelDialog
= getRefNameInputDialog(
340 UIText
.BranchSelectionDialog_QuestionNewBranchMessage
,
341 refName
, Constants
.R_HEADS
),
344 if (labelDialog
.open() == Window
.OK
) {
345 String newRefName
= Constants
.R_HEADS
+ labelDialog
.getValue();
348 updateRef
= repo
.updateRef(newRefName
);
349 Ref startRef
= repo
.getRef(refName
);
350 ObjectId startAt
= repo
.resolve(refName
);
352 if (startRef
!= null)
353 startBranch
= refName
;
355 startBranch
= startAt
.name();
356 startBranch
= repo
.shortenRefName(startBranch
);
357 updateRef
.setNewObjectId(startAt
);
358 updateRef
.setRefLogMessage("branch: Created from " + startBranch
, false); //$NON-NLS-1$
360 branchTree
.refresh();
362 } catch (Throwable e1
) {
365 UIText
.BranchSelectionDialog_ErrorCouldNotCreateNewRef
,
371 confirmationBtn
= createButton(parent
, IDialogConstants
.OK_ID
,
372 UIText
.BranchSelectionDialog_OkCheckout
, true);
373 createButton(parent
, IDialogConstants
.CANCEL_ID
, IDialogConstants
.CANCEL_LABEL
, false);
375 // can't advance without a selection
376 confirmationBtn
.setEnabled(!branchTree
.getSelection().isEmpty());
380 * @return the label shown above the refs tree
382 protected String
getRefsLabel() {
383 return UIText
.BranchSelectionDialog_Refs
;
387 * Subclasses may add UI elements
390 protected void createCustomArea(Composite parent
) {
395 * Subclasses may change the title of the dialog
396 * @return the title of the dialog
398 protected String
getTitle() {
399 return UIText
.BranchSelectionDialog_TitleCheckout
;
404 * @return if the confirmation button is enabled when a tag is selected
406 protected boolean canConfirmOnTag() {
411 protected int getShellStyle() {
412 return super.getShellStyle() | SWT
.RESIZE
;
415 private void reportError(Throwable e
, String message
, Object
... args
) {
416 String msg
= NLS
.bind(message
, args
);
417 Activator
.handleError(msg
, e
, true);