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
.Collections
;
17 import java
.util
.List
;
19 import org
.eclipse
.egit
.core
.op
.ResetOperation
.ResetType
;
20 import org
.eclipse
.egit
.ui
.Activator
;
21 import org
.eclipse
.egit
.ui
.UIText
;
22 import org
.eclipse
.jface
.dialogs
.Dialog
;
23 import org
.eclipse
.jface
.dialogs
.IDialogConstants
;
24 import org
.eclipse
.jface
.dialogs
.IInputValidator
;
25 import org
.eclipse
.jface
.dialogs
.InputDialog
;
26 import org
.eclipse
.jface
.dialogs
.MessageDialog
;
27 import org
.eclipse
.jface
.layout
.GridDataFactory
;
28 import org
.eclipse
.jface
.layout
.GridLayoutFactory
;
29 import org
.eclipse
.jface
.resource
.JFaceResources
;
30 import org
.eclipse
.jface
.window
.Window
;
31 import org
.eclipse
.jgit
.lib
.Constants
;
32 import org
.eclipse
.jgit
.lib
.ObjectId
;
33 import org
.eclipse
.jgit
.lib
.Ref
;
34 import org
.eclipse
.jgit
.lib
.RefRename
;
35 import org
.eclipse
.jgit
.lib
.RefUpdate
;
36 import org
.eclipse
.jgit
.lib
.Repository
;
37 import org
.eclipse
.jgit
.lib
.RefUpdate
.Result
;
38 import org
.eclipse
.osgi
.util
.NLS
;
39 import org
.eclipse
.swt
.SWT
;
40 import org
.eclipse
.swt
.events
.DisposeEvent
;
41 import org
.eclipse
.swt
.events
.DisposeListener
;
42 import org
.eclipse
.swt
.events
.SelectionAdapter
;
43 import org
.eclipse
.swt
.events
.SelectionEvent
;
44 import org
.eclipse
.swt
.graphics
.Font
;
45 import org
.eclipse
.swt
.graphics
.FontData
;
46 import org
.eclipse
.swt
.layout
.GridLayout
;
47 import org
.eclipse
.swt
.layout
.RowLayout
;
48 import org
.eclipse
.swt
.widgets
.Button
;
49 import org
.eclipse
.swt
.widgets
.Composite
;
50 import org
.eclipse
.swt
.widgets
.Event
;
51 import org
.eclipse
.swt
.widgets
.Group
;
52 import org
.eclipse
.swt
.widgets
.Label
;
53 import org
.eclipse
.swt
.widgets
.Listener
;
54 import org
.eclipse
.swt
.widgets
.Shell
;
55 import org
.eclipse
.swt
.widgets
.Tree
;
56 import org
.eclipse
.swt
.widgets
.TreeItem
;
59 * The branch and reset selection dialog
62 public class BranchSelectionDialog
extends Dialog
{
64 private final Repository repo
;
66 private final boolean showResetType
;
68 private Tree branchTree
;
70 private Button confirmationBtn
;
72 private Button renameButton
;
74 private Button newButton
;
76 private String selectedBranch
;
78 private ResetType resetType
= ResetType
.MIXED
;
81 * Construct a dialog to select a branch to reset to or check out
84 * @param showReset true if the "reset" part should be shown
86 public BranchSelectionDialog(Shell parentShell
, Repository repo
, boolean showReset
) {
89 this.showResetType
= showReset
;
93 protected Composite
createDialogArea(Composite base
) {
94 Composite parent
= (Composite
) super.createDialogArea(base
);
95 parent
.setLayout(GridLayoutFactory
.swtDefaults().create());
96 new Label(parent
, SWT
.NONE
).setText(UIText
.BranchSelectionDialog_Refs
);
97 branchTree
= new Tree(parent
, SWT
.BORDER
);
98 branchTree
.setLayoutData(GridDataFactory
.fillDefaults().grab(true,true).hint(500, 300).create());
99 branchTree
.addSelectionListener(new SelectionAdapter() {
101 public void widgetSelected(SelectionEvent e
) {
102 // enable the buttons depending on the selection
103 boolean oneSelected
= branchTree
.getSelection().length
== 1;
105 String refName
= refNameFromDialog();
107 boolean headSelected
= Constants
.HEAD
.equals(refName
);
109 boolean tagSelected
= refName
!=null && refName
.startsWith(
112 boolean branchSelected
= refName
!=null && (refName
.startsWith(Constants
.R_HEADS
) || refName
.startsWith(Constants
.R_REMOTES
));
114 // TODO add support for checkout of tags
115 confirmationBtn
.setEnabled(oneSelected
&& branchSelected
&& !headSelected
118 if (!showResetType
) {
119 // we don't support rename on tags
120 renameButton
.setEnabled(oneSelected
&& branchSelected
&& !headSelected
&& !tagSelected
);
122 // new branch can not be based on a tag
123 newButton
.setEnabled(oneSelected
&& branchSelected
&& !tagSelected
);
129 buildResetGroup(parent
);
132 String rawTitle
= showResetType ? UIText
.BranchSelectionDialog_TitleReset
133 : UIText
.BranchSelectionDialog_TitleCheckout
;
135 NLS
.bind(rawTitle
, new Object
[] { repo
.getDirectory() }));
138 fillTreeWithBranches(null);
139 } catch (Throwable e
) {
140 Activator
.logError(UIText
.BranchSelectionDialog_ErrorCouldNotRefresh
, e
);
146 private void buildResetGroup(Composite parent
) {
147 Group g
= new Group(parent
, SWT
.NONE
);
148 g
.setText(UIText
.BranchSelectionDialog_ResetType
);
149 g
.setLayoutData(GridDataFactory
.swtDefaults().align(SWT
.CENTER
, SWT
.CENTER
).create());
150 g
.setLayout(new RowLayout(SWT
.VERTICAL
));
152 Button soft
= new Button(g
, SWT
.RADIO
);
153 soft
.setText(UIText
.BranchSelectionDialog_ResetTypeSoft
);
154 soft
.addListener(SWT
.Selection
, new Listener() {
155 public void handleEvent(Event event
) {
156 resetType
= ResetType
.SOFT
;
160 Button medium
= new Button(g
, SWT
.RADIO
);
161 medium
.setSelection(true);
162 medium
.setText(UIText
.BranchSelectionDialog_ResetTypeMixed
);
163 medium
.addListener(SWT
.Selection
, new Listener() {
164 public void handleEvent(Event event
) {
165 resetType
= ResetType
.MIXED
;
169 Button hard
= new Button(g
, SWT
.RADIO
);
170 hard
.setText(UIText
.BranchSelectionDialog_ResetTypeHard
);
171 hard
.addListener(SWT
.Selection
, new Listener() {
172 public void handleEvent(Event event
) {
173 resetType
= ResetType
.HARD
;
178 private void fillTreeWithBranches(String select
) throws IOException
{
179 String branch
= repo
.getFullBranch();
180 List
<String
> branches
= new ArrayList
<String
>(repo
.getAllRefs()
182 Collections
.sort(branches
);
184 TreeItem curItem
= null;
185 TreeItem curSubItem
= null;
186 String curPrefix
= null;
187 String curSubPrefix
= null;
188 TreeItem itemToSelect
= null;
190 for (String ref
: branches
) {
191 String shortName
= ref
;
192 if (ref
.startsWith(Constants
.R_HEADS
)) {
193 shortName
= ref
.substring(11);
194 if (!Constants
.R_HEADS
.equals(curPrefix
)) {
195 curPrefix
= Constants
.R_HEADS
;
198 curItem
= new TreeItem(branchTree
, SWT
.NONE
);
199 curItem
.setText(UIText
.BranchSelectionDialog_LocalBranches
);
201 } else if (ref
.startsWith(Constants
.R_REMOTES
)) {
202 shortName
= ref
.substring(13);
203 if (!Constants
.R_REMOTES
.equals(curPrefix
)) {
204 curPrefix
= Constants
.R_REMOTES
;
205 curItem
= new TreeItem(branchTree
, SWT
.NONE
);
206 curItem
.setText(UIText
.BranchSelectionDialog_RemoteBranches
);
211 int slashPos
= shortName
.indexOf("/"); //$NON-NLS-1$
213 String remoteName
= shortName
.substring(0, slashPos
);
214 shortName
= shortName
.substring(slashPos
+1);
215 if (!remoteName
.equals(curSubPrefix
)) {
216 curSubItem
= new TreeItem(curItem
, SWT
.NONE
);
217 curSubItem
.setText(remoteName
);
218 curSubPrefix
= remoteName
;
224 } else if (ref
.startsWith(Constants
.R_TAGS
)) {
225 shortName
= ref
.substring(10);
226 if (!Constants
.R_TAGS
.equals(curPrefix
)) {
227 curPrefix
= Constants
.R_TAGS
;
230 curItem
= new TreeItem(branchTree
, SWT
.NONE
);
231 curItem
.setText(UIText
.BranchSelectionDialog_Tags
);
236 item
= new TreeItem(branchTree
, SWT
.NONE
);
237 else if (curSubItem
== null)
238 item
= new TreeItem(curItem
, SWT
.NONE
);
239 else item
= new TreeItem(curSubItem
, SWT
.NONE
);
241 if (ref
.equals(branch
)) {
242 item
.setText(shortName
+ UIText
.BranchSelectionDialog_BranchSuffix_Current
);
243 FontData fd
= item
.getFont().getFontData()[0];
244 fd
.setStyle(fd
.getStyle() | SWT
.BOLD
);
245 final Font f
= new Font(getShell().getDisplay(), fd
);
247 item
.addDisposeListener(new DisposeListener() {
248 public void widgetDisposed(DisposeEvent e
) {
252 branchTree
.showItem(item
);
254 else item
.setText(shortName
);
255 if (ref
.equals(select
))
257 branchTree
.setLinesVisible(true);
259 if (itemToSelect
!= null) {
260 branchTree
.select(itemToSelect
);
261 branchTree
.showItem(itemToSelect
);
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 TreeItem
[] selection
= branchTree
.getSelection();
297 if (selection
.length
> 0) {
298 TreeItem item
= selection
[0];
299 return (String
) item
.getData();
304 private InputDialog
getRefNameInputDialog(String prompt
, final String refPrefix
) {
305 InputDialog labelDialog
= new InputDialog(
307 UIText
.BranchSelectionDialog_QuestionNewBranchTitle
,
309 null, new IInputValidator() {
310 public String
isValid(String newText
) {
311 if (newText
.length() == 0) {
312 // nothing entered, just don't let the user proceed,
313 // no need to prompt them with an error message
314 return ""; //$NON-NLS-1$
317 String testFor
= refPrefix
+ newText
;
319 if (repo
.resolve(testFor
) != null)
320 return UIText
.BranchSelectionDialog_ErrorAlreadyExists
;
321 } catch (IOException e1
) {
322 Activator
.logError(NLS
.bind(
323 UIText
.BranchSelectionDialog_ErrorCouldNotResolve
, testFor
), e1
);
324 return e1
.getMessage();
326 if (!Repository
.isValidRefName(testFor
))
327 return UIText
.BranchSelectionDialog_ErrorInvalidRefName
;
331 labelDialog
.setBlockOnOpen(true);
336 protected void createButtonsForButtonBar(Composite parent
) {
337 if (!showResetType
) {
338 newButton
= new Button(parent
, SWT
.PUSH
);
339 newButton
.setFont(JFaceResources
.getDialogFont());
340 newButton
.setText(UIText
.BranchSelectionDialog_NewBranch
);
341 setButtonLayoutData(newButton
);
342 ((GridLayout
)parent
.getLayout()).numColumns
++;
344 renameButton
= new Button(parent
, SWT
.PUSH
);
345 renameButton
.setFont(JFaceResources
.getDialogFont());
346 renameButton
.setText(UIText
.BranchSelectionDialog_Rename
);
347 setButtonLayoutData(renameButton
);
348 ((GridLayout
)parent
.getLayout()).numColumns
++;
350 renameButton
.addSelectionListener(new SelectionAdapter() {
351 public void widgetSelected(SelectionEvent e
) {
353 String refName
= refNameFromDialog();
356 // the button should be disabled anyway, but we check again
357 if (refName
.equals(Constants
.HEAD
))
360 if (refName
.startsWith(Constants
.R_HEADS
))
361 refPrefix
= Constants
.R_HEADS
;
362 else if (refName
.startsWith(Constants
.R_REMOTES
))
363 refPrefix
= Constants
.R_REMOTES
;
364 else if (refName
.startsWith(Constants
.R_TAGS
))
365 refPrefix
= Constants
.R_TAGS
;
367 // the button should be disabled anyway, but we check again
371 String branchName
= refName
.substring(refPrefix
.length());
373 InputDialog labelDialog
= getRefNameInputDialog(NLS
375 UIText
.BranchSelectionDialog_QuestionNewBranchNameMessage
,
376 branchName
, refPrefix
), refPrefix
);
377 if (labelDialog
.open() == Window
.OK
) {
378 String newRefName
= refPrefix
+ labelDialog
.getValue();
380 RefRename renameRef
= repo
.renameRef(refName
, newRefName
);
381 if (renameRef
.rename() != Result
.RENAMED
) {
384 UIText
.BranchSelectionDialog_BranchSelectionDialog_RenamedFailedTitle
,
385 UIText
.BranchSelectionDialog_ErrorCouldNotRenameRef
,
386 refName
, newRefName
, renameRef
389 } catch (Throwable e1
) {
392 UIText
.BranchSelectionDialog_BranchSelectionDialog_RenamedFailedTitle
,
393 UIText
.BranchSelectionDialog_ErrorCouldNotRenameRef
,
394 refName
, newRefName
, e1
.getMessage());
397 branchTree
.removeAll();
398 fillTreeWithBranches(newRefName
);
399 } catch (Throwable e1
) {
402 UIText
.BranchSelectionDialog_BranchSelectionDialog_RenamedFailedTitle
,
403 UIText
.BranchSelectionDialog_ErrorCouldNotRefreshBranchList
);
408 newButton
.addSelectionListener(new SelectionAdapter() {
410 public void widgetSelected(SelectionEvent e
) {
411 // check what ref name the user selected, if any.
412 String refName
= refNameFromDialog();
416 // the button should be disabled anyway, but we check again
417 if (refName
.equals(Constants
.HEAD
))
420 if (refName
.startsWith(Constants
.R_HEADS
))
421 refPrefix
= Constants
.R_HEADS
;
422 else if (refName
.startsWith(Constants
.R_REMOTES
))
423 refPrefix
= Constants
.R_REMOTES
;
425 // the button should be disabled anyway, but we check again
429 InputDialog labelDialog
= getRefNameInputDialog(
432 UIText
.BranchSelectionDialog_QuestionNewBranchMessage
,
433 refName
, refPrefix
), refPrefix
);
435 if (labelDialog
.open() == Window
.OK
) {
436 String newRefName
= Constants
.R_HEADS
+ labelDialog
.getValue();
439 updateRef
= repo
.updateRef(newRefName
);
440 Ref startRef
= repo
.getRef(refName
);
441 ObjectId startAt
= repo
.resolve(refName
);
443 if (startRef
!= null)
444 startBranch
= refName
;
446 startBranch
= startAt
.name();
447 startBranch
= repo
.shortenRefName(startBranch
);
448 updateRef
.setNewObjectId(startAt
);
449 updateRef
.setRefLogMessage("branch: Created from " + startBranch
, false); //$NON-NLS-1$
451 } catch (Throwable e1
) {
454 UIText
.BranchSelectionDialog_BranchSelectionDialog_CreateFailedTitle
,
455 UIText
.BranchSelectionDialog_ErrorCouldNotCreateNewRef
,
459 branchTree
.removeAll();
460 fillTreeWithBranches(newRefName
);
461 } catch (Throwable e1
) {
463 UIText
.BranchSelectionDialog_BranchSelectionDialog_CreateFailedTitle
,
464 UIText
.BranchSelectionDialog_ErrorCouldNotRefreshBranchList
);
470 confirmationBtn
= createButton(parent
, IDialogConstants
.OK_ID
,
471 showResetType ? UIText
.BranchSelectionDialog_OkReset
472 : UIText
.BranchSelectionDialog_OkCheckout
, true);
473 createButton(parent
, IDialogConstants
.CANCEL_ID
, IDialogConstants
.CANCEL_LABEL
, false);
475 // can't advance without a selection
476 confirmationBtn
.setEnabled(branchTree
.getSelectionCount() != 0);
480 protected int getShellStyle() {
481 return super.getShellStyle() | SWT
.RESIZE
;
484 private void reportError(Throwable e
, String title
, String message
,
486 String msg
= NLS
.bind(message
, args
);
487 MessageDialog
.openError(getShell(), title
, msg
);
488 Activator
.logError(msg
, e
);