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
.core
.op
.ResetOperation
.ResetType
;
19 import org
.eclipse
.egit
.ui
.Activator
;
20 import org
.eclipse
.egit
.ui
.UIText
;
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
.egit
.ui
.internal
.ValidationUtils
;
26 import org
.eclipse
.jface
.dialogs
.Dialog
;
27 import org
.eclipse
.jface
.dialogs
.IDialogConstants
;
28 import org
.eclipse
.jface
.dialogs
.InputDialog
;
29 import org
.eclipse
.jface
.dialogs
.MessageDialog
;
30 import org
.eclipse
.jface
.layout
.GridDataFactory
;
31 import org
.eclipse
.jface
.layout
.GridLayoutFactory
;
32 import org
.eclipse
.jface
.resource
.JFaceResources
;
33 import org
.eclipse
.jface
.viewers
.IOpenListener
;
34 import org
.eclipse
.jface
.viewers
.ISelectionChangedListener
;
35 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
36 import org
.eclipse
.jface
.viewers
.OpenEvent
;
37 import org
.eclipse
.jface
.viewers
.SelectionChangedEvent
;
38 import org
.eclipse
.jface
.viewers
.StructuredSelection
;
39 import org
.eclipse
.jface
.viewers
.TreeViewer
;
40 import org
.eclipse
.jface
.window
.Window
;
41 import org
.eclipse
.jgit
.lib
.Constants
;
42 import org
.eclipse
.jgit
.lib
.ObjectId
;
43 import org
.eclipse
.jgit
.lib
.Ref
;
44 import org
.eclipse
.jgit
.lib
.RefRename
;
45 import org
.eclipse
.jgit
.lib
.RefUpdate
;
46 import org
.eclipse
.jgit
.lib
.Repository
;
47 import org
.eclipse
.jgit
.lib
.RefUpdate
.Result
;
48 import org
.eclipse
.osgi
.util
.NLS
;
49 import org
.eclipse
.swt
.SWT
;
50 import org
.eclipse
.swt
.events
.SelectionAdapter
;
51 import org
.eclipse
.swt
.events
.SelectionEvent
;
52 import org
.eclipse
.swt
.layout
.GridLayout
;
53 import org
.eclipse
.swt
.layout
.RowLayout
;
54 import org
.eclipse
.swt
.widgets
.Button
;
55 import org
.eclipse
.swt
.widgets
.Composite
;
56 import org
.eclipse
.swt
.widgets
.Event
;
57 import org
.eclipse
.swt
.widgets
.Group
;
58 import org
.eclipse
.swt
.widgets
.Label
;
59 import org
.eclipse
.swt
.widgets
.Listener
;
60 import org
.eclipse
.swt
.widgets
.Shell
;
63 * The branch and reset selection dialog
66 public class BranchSelectionDialog
extends Dialog
{
68 private final Repository repo
;
70 private final boolean showResetType
;
72 private TreeViewer branchTree
;
74 private Button confirmationBtn
;
76 private Button renameButton
;
78 private Button newButton
;
80 private String selectedBranch
;
82 private ResetType resetType
= ResetType
.MIXED
;
84 private final RepositoryTreeNode
<Repository
> localBranches
;
86 private final RepositoryTreeNode
<Repository
> remoteBranches
;
88 private final RepositoryTreeNode
<Repository
> tags
;
91 * Construct a dialog to select a branch to reset to or check out
94 * @param showReset true if the "reset" part should be shown
96 public BranchSelectionDialog(Shell parentShell
, Repository repo
, boolean showReset
) {
99 this.showResetType
= showReset
;
100 localBranches
= new RepositoryTreeNode
<Repository
>(null,
101 RepositoryTreeNodeType
.LOCALBRANCHES
, this.repo
, this.repo
);
102 remoteBranches
= new RepositoryTreeNode
<Repository
>(null,
103 RepositoryTreeNodeType
.REMOTEBRANCHES
, this.repo
, this.repo
);
104 tags
= new RepositoryTreeNode
<Repository
>(null,
105 RepositoryTreeNodeType
.TAGS
, this.repo
, this.repo
);
109 protected Composite
createDialogArea(Composite base
) {
110 Composite parent
= (Composite
) super.createDialogArea(base
);
111 parent
.setLayout(GridLayoutFactory
.swtDefaults().create());
112 new Label(parent
, SWT
.NONE
).setText(UIText
.BranchSelectionDialog_Refs
);
114 branchTree
= new TreeViewer(parent
, SWT
.SINGLE
| SWT
.BORDER
);
115 new RepositoriesViewLabelProvider(branchTree
);
116 branchTree
.setContentProvider(new RepositoriesViewContentProvider());
118 GridDataFactory
.fillDefaults().grab(true, true).hint(500, 300).applyTo(
119 branchTree
.getTree());
120 branchTree
.addSelectionChangedListener(new ISelectionChangedListener() {
122 public void selectionChanged(SelectionChangedEvent event
) {
123 // enable the buttons depending on the selection
125 String refName
= refNameFromDialog();
127 boolean tagSelected
= refName
!= null
128 && refName
.startsWith(Constants
.R_TAGS
);
130 boolean branchSelected
= refName
!= null
131 && (refName
.startsWith(Constants
.R_HEADS
) || refName
132 .startsWith(Constants
.R_REMOTES
));
134 // we don't allow reset on tags, but checkout
136 confirmationBtn
.setEnabled(branchSelected
);
138 confirmationBtn
.setEnabled(branchSelected
|| tagSelected
);
140 if (!showResetType
) {
141 // we don't support rename on tags
142 renameButton
.setEnabled(branchSelected
&& !tagSelected
);
144 // new branch can not be based on a tag
145 newButton
.setEnabled(branchSelected
&& !tagSelected
);
150 // double-click support
151 branchTree
.addOpenListener(new IOpenListener() {
153 public void open(OpenEvent event
) {
154 RepositoryTreeNode node
= (RepositoryTreeNode
) ((IStructuredSelection
) branchTree
155 .getSelection()).getFirstElement();
156 if (node
.getType() != RepositoryTreeNodeType
.REF
)
157 branchTree
.setExpandedState(node
, !branchTree
158 .getExpandedState(node
));
159 else if (confirmationBtn
.isEnabled())
166 buildResetGroup(parent
);
169 String rawTitle
= showResetType ? UIText
.BranchSelectionDialog_TitleReset
170 : UIText
.BranchSelectionDialog_TitleCheckout
;
172 NLS
.bind(rawTitle
, new Object
[] { repo
.getDirectory() }));
178 public void create() {
181 List
<RepositoryTreeNode
> roots
= new ArrayList
<RepositoryTreeNode
>();
182 roots
.add(localBranches
);
183 roots
.add(remoteBranches
);
186 branchTree
.setInput(roots
);
189 // initially, we mark the current head if it can be determined
190 String fullBranch
= repo
.getFullBranch();
191 if (!markRef(fullBranch
))
192 // if we can't determine a branch, we just expand local branches
193 branchTree
.expandToLevel(localBranches
, 1);
194 } catch (IOException e
) {
199 private boolean markRef(String refName
) {
200 // selects the entry specified by the name
205 actRef
= repo
.getRef(refName
);
206 } catch (IOException e
) {
214 RepositoryTreeNode
<Repository
> parentNode
;
215 if (refName
.startsWith(Constants
.R_HEADS
)) {
216 parentNode
= localBranches
;
217 // TODO fix this: if we are on a local branch or tag, we must do the
218 // indirection through the commit
219 } else if (refName
.startsWith(Constants
.R_REMOTES
)) {
220 parentNode
= remoteBranches
;
221 } else if (refName
.startsWith(Constants
.R_TAGS
)) {
227 RepositoryTreeNode
<Ref
> actNode
= new RepositoryTreeNode
<Ref
>(
228 parentNode
, RepositoryTreeNodeType
.REF
, repo
, actRef
);
229 branchTree
.setSelection(new StructuredSelection(actNode
), true);
233 private void buildResetGroup(Composite parent
) {
234 Group g
= new Group(parent
, SWT
.NONE
);
235 g
.setText(UIText
.BranchSelectionDialog_ResetType
);
236 g
.setLayoutData(GridDataFactory
.swtDefaults().align(SWT
.CENTER
, SWT
.CENTER
).create());
237 g
.setLayout(new RowLayout(SWT
.VERTICAL
));
239 Button soft
= new Button(g
, SWT
.RADIO
);
240 soft
.setText(UIText
.BranchSelectionDialog_ResetTypeSoft
);
241 soft
.addListener(SWT
.Selection
, new Listener() {
242 public void handleEvent(Event event
) {
243 resetType
= ResetType
.SOFT
;
247 Button medium
= new Button(g
, SWT
.RADIO
);
248 medium
.setSelection(true);
249 medium
.setText(UIText
.BranchSelectionDialog_ResetTypeMixed
);
250 medium
.addListener(SWT
.Selection
, new Listener() {
251 public void handleEvent(Event event
) {
252 resetType
= ResetType
.MIXED
;
256 Button hard
= new Button(g
, SWT
.RADIO
);
257 hard
.setText(UIText
.BranchSelectionDialog_ResetTypeHard
);
258 hard
.addListener(SWT
.Selection
, new Listener() {
259 public void handleEvent(Event event
) {
260 resetType
= ResetType
.HARD
;
266 * @return the selected refName
268 public String
getRefName() {
269 return this.selectedBranch
;
273 * @return Type of Reset
275 public ResetType
getResetType() {
280 protected void okPressed() {
281 this.selectedBranch
= refNameFromDialog();
283 if (resetType
== ResetType
.HARD
) {
284 if (!MessageDialog
.openQuestion(getShell(),
285 UIText
.BranchSelectionDialog_ReallyResetTitle
,
286 UIText
.BranchSelectionDialog_ReallyResetMessage
)) {
295 private String
refNameFromDialog() {
296 IStructuredSelection sel
= (IStructuredSelection
) branchTree
300 RepositoryTreeNode node
= (RepositoryTreeNode
) sel
.getFirstElement();
301 if (node
.getType() == RepositoryTreeNodeType
.REF
302 || node
.getType() == RepositoryTreeNodeType
.TAG
) {
303 return ((Ref
) node
.getObject()).getName();
308 private InputDialog
getRefNameInputDialog(String prompt
, final String refPrefix
) {
309 InputDialog labelDialog
= new InputDialog(
311 UIText
.BranchSelectionDialog_QuestionNewBranchTitle
,
313 null, ValidationUtils
.getRefNameInputValidator(repo
, refPrefix
));
314 labelDialog
.setBlockOnOpen(true);
319 protected void createButtonsForButtonBar(Composite parent
) {
320 if (!showResetType
) {
321 newButton
= new Button(parent
, SWT
.PUSH
);
322 newButton
.setFont(JFaceResources
.getDialogFont());
323 newButton
.setText(UIText
.BranchSelectionDialog_NewBranch
);
324 setButtonLayoutData(newButton
);
325 ((GridLayout
)parent
.getLayout()).numColumns
++;
327 renameButton
= new Button(parent
, SWT
.PUSH
);
328 renameButton
.setFont(JFaceResources
.getDialogFont());
329 renameButton
.setText(UIText
.BranchSelectionDialog_Rename
);
330 setButtonLayoutData(renameButton
);
331 ((GridLayout
)parent
.getLayout()).numColumns
++;
333 renameButton
.addSelectionListener(new SelectionAdapter() {
334 public void widgetSelected(SelectionEvent e
) {
336 String refName
= refNameFromDialog();
339 // the button should be disabled anyway, but we check again
340 if (refName
.equals(Constants
.HEAD
))
343 if (refName
.startsWith(Constants
.R_HEADS
))
344 refPrefix
= Constants
.R_HEADS
;
345 else if (refName
.startsWith(Constants
.R_REMOTES
))
346 refPrefix
= Constants
.R_REMOTES
;
347 else if (refName
.startsWith(Constants
.R_TAGS
))
348 refPrefix
= Constants
.R_TAGS
;
350 // the button should be disabled anyway, but we check again
354 String branchName
= refName
.substring(refPrefix
.length());
356 InputDialog labelDialog
= getRefNameInputDialog(NLS
358 UIText
.BranchSelectionDialog_QuestionNewBranchNameMessage
,
359 branchName
, refPrefix
), refPrefix
);
360 if (labelDialog
.open() == Window
.OK
) {
361 String newRefName
= refPrefix
+ labelDialog
.getValue();
363 RefRename renameRef
= repo
.renameRef(refName
, newRefName
);
364 if (renameRef
.rename() != Result
.RENAMED
) {
367 UIText
.BranchSelectionDialog_ErrorCouldNotRenameRef
,
368 refName
, newRefName
, renameRef
371 branchTree
.refresh();
373 } catch (Throwable e1
) {
376 UIText
.BranchSelectionDialog_ErrorCouldNotRenameRef
,
377 refName
, newRefName
, e1
.getMessage());
382 newButton
.addSelectionListener(new SelectionAdapter() {
384 public void widgetSelected(SelectionEvent e
) {
385 // check what ref name the user selected, if any.
386 String refName
= refNameFromDialog();
388 // the button should be disabled anyway, but we check again
389 if (refName
.equals(Constants
.HEAD
))
391 if (refName
.startsWith(Constants
.R_TAGS
))
392 // the button should be disabled anyway, but we check again
395 InputDialog labelDialog
= getRefNameInputDialog(
398 UIText
.BranchSelectionDialog_QuestionNewBranchMessage
,
399 refName
, Constants
.R_HEADS
),
402 if (labelDialog
.open() == Window
.OK
) {
403 String newRefName
= Constants
.R_HEADS
+ labelDialog
.getValue();
406 updateRef
= repo
.updateRef(newRefName
);
407 Ref startRef
= repo
.getRef(refName
);
408 ObjectId startAt
= repo
.resolve(refName
);
410 if (startRef
!= null)
411 startBranch
= refName
;
413 startBranch
= startAt
.name();
414 startBranch
= repo
.shortenRefName(startBranch
);
415 updateRef
.setNewObjectId(startAt
);
416 updateRef
.setRefLogMessage("branch: Created from " + startBranch
, false); //$NON-NLS-1$
418 branchTree
.refresh();
420 } catch (Throwable e1
) {
423 UIText
.BranchSelectionDialog_ErrorCouldNotCreateNewRef
,
430 confirmationBtn
= createButton(parent
, IDialogConstants
.OK_ID
,
431 showResetType ? UIText
.BranchSelectionDialog_OkReset
432 : UIText
.BranchSelectionDialog_OkCheckout
, true);
433 createButton(parent
, IDialogConstants
.CANCEL_ID
, IDialogConstants
.CANCEL_LABEL
, false);
435 // can't advance without a selection
436 confirmationBtn
.setEnabled(!branchTree
.getSelection().isEmpty());
440 protected int getShellStyle() {
441 return super.getShellStyle() | SWT
.RESIZE
;
444 private void reportError(Throwable e
, String message
, Object
... args
) {
445 String msg
= NLS
.bind(message
, args
);
446 Activator
.handleError(msg
, e
, true);