/******************************************************************************* * Copyright (C) 2007, Dave Watson * Copyright (C) 2007, Robin Rosenberg * Copyright (C) 2008, Shawn O. Pearce * Copyright (C) 2010, Chris Aniszczyk * Copyright (C) 2010, Mathias Kinzler * Copyright (C) 2011, Dariusz Luksza * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html *******************************************************************************/ package org.eclipse.egit.ui.internal.dialogs; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.eclipse.egit.ui.Activator; import org.eclipse.egit.ui.internal.repository.RepositoriesViewContentProvider; import org.eclipse.egit.ui.internal.repository.RepositoriesViewLabelProvider; import org.eclipse.egit.ui.internal.repository.tree.AdditionalRefsNode; import org.eclipse.egit.ui.internal.repository.tree.LocalNode; import org.eclipse.egit.ui.internal.repository.tree.RefNode; import org.eclipse.egit.ui.internal.repository.tree.RemoteTrackingNode; import org.eclipse.egit.ui.internal.repository.tree.RepositoryTreeNode; import org.eclipse.egit.ui.internal.repository.tree.RepositoryTreeNodeType; import org.eclipse.egit.ui.internal.repository.tree.TagNode; import org.eclipse.egit.ui.internal.repository.tree.TagsNode; import org.eclipse.jface.dialogs.TitleAreaDialog; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.jface.viewers.IOpenListener; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.OpenEvent; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.window.Window; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.dialogs.FilteredTree; import org.eclipse.ui.dialogs.PatternFilter; /** * The abstract base class to display a branch/tag selection dialog. *

* This will construct a tree similar to the Git Repositories View from which * the user can select an item. Concrete subclasses are responsible to check * whether the selection is valid and may add extra UI elements by overriding * {@link #createCustomArea(Composite)}. */ public abstract class AbstractBranchSelectionDialog extends TitleAreaDialog { /** The {@link Repository} used in the constructor */ protected final Repository repo; /** The tree */ protected TreeViewer branchTree; private String selectedBranch; private final String refToMark; private final RepositoryTreeNode localBranches; private final RepositoryTreeNode remoteBranches; private final RepositoryTreeNode tags; private final RepositoryTreeNode references; /** Determinate does local branches should be show or not */ protected static final int SHOW_LOCAL_BRANCHES = 1 << 1; /** Determinate does remote branches should be show or not */ protected static final int SHOW_REMOTE_BRANCHES = 1 << 2; /** Determinate does tags should be show or not */ protected static final int SHOW_TAGS = 1 << 3; /** Determinate does references shout be show or not */ protected static final int SHOW_REFERENCES = 1 << 4; /** Determinate does current should be selected or not */ protected static final int SELECT_CURRENT_REF = 1 << 5; /** Determinate does local branches should be expanded or not */ protected static final int EXPAND_LOCAL_BRANCHES_NODE = 1 << 6; /** Determinate does remote branches should be expanded or not */ protected static final int EXPAND_REMOTE_BRANCHES_NODE = 1 << 7; /** * Will allow select multiple branches. The implementer must override * {@link AbstractBranchSelectionDialog#refNameFromDialog()} to be able to * obtain list of selected branches */ protected static final int ALLOW_MULTISELECTION = 1 << 8; private final int settings; /** * Construct a dialog to select a branch. *

* The currently checked out {@link Ref} is marked if possible * * @param parentShell * @param repository * the {@link Repository} * @param settings * configuration options of this dialog like * {@link AbstractBranchSelectionDialog#SHOW_LOCAL_BRANCHES}, * {@link AbstractBranchSelectionDialog#SHOW_REMOTE_BRANCHES}, * {@link AbstractBranchSelectionDialog#SHOW_TAGS}, * {@link AbstractBranchSelectionDialog#SHOW_REFERENCES}, * {@link AbstractBranchSelectionDialog#SELECT_CURRENT_REF}, * {@link AbstractBranchSelectionDialog#EXPAND_LOCAL_BRANCHES_NODE}, * {@link AbstractBranchSelectionDialog#EXPAND_REMOTE_BRANCHES_NODE} */ public AbstractBranchSelectionDialog(Shell parentShell, Repository repository, int settings) { this(parentShell, repository, null, settings); setHelpAvailable(false); } /** * Construct a dialog to select a branch and specify a {@link Ref} to mark * * @param parentShell * @param repository * the {@link Repository} * @param refToMark * the name of the {@link Ref} to mark initially * @param settings * configuration options of this dialog like * {@link AbstractBranchSelectionDialog#SHOW_LOCAL_BRANCHES}, * {@link AbstractBranchSelectionDialog#SHOW_REMOTE_BRANCHES}, * {@link AbstractBranchSelectionDialog#SHOW_TAGS}, * {@link AbstractBranchSelectionDialog#SHOW_REFERENCES}, * {@link AbstractBranchSelectionDialog#SELECT_CURRENT_REF}, * {@link AbstractBranchSelectionDialog#EXPAND_LOCAL_BRANCHES_NODE}, * {@link AbstractBranchSelectionDialog#EXPAND_REMOTE_BRANCHES_NODE} */ public AbstractBranchSelectionDialog(Shell parentShell, Repository repository, String refToMark, int settings) { super(parentShell); this.repo = repository; this.settings = settings; localBranches = new LocalNode(null, this.repo); remoteBranches = new RemoteTrackingNode(null, this.repo); tags = new TagsNode(null, this.repo); references = new AdditionalRefsNode(null, this.repo); this.refToMark = refToMark; setHelpAvailable(false); } /** * Concrete subclasses should implement their check logic around this * * @param refName * the name of the currently selected {@link Ref}, may be null */ protected abstract void refNameSelected(String refName); /** * Subclasses must provide the title of the dialog * * @return the title of the dialog */ protected abstract String getTitle(); /** * @return the message shown above the refs tree */ protected abstract String getMessageText(); /** * Subclasses should provide the title of the dialog window *

* Defaults to {@link #getTitle()} * * @return the title of the dialog window */ protected String getWindowTitle() { return getTitle(); } @Override protected final Composite createDialogArea(Composite base) { Composite parent = (Composite) super.createDialogArea(base); parent.setLayout(GridLayoutFactory.fillDefaults().create()); int selectionModel = -1; if ((settings & ALLOW_MULTISELECTION) != 0) selectionModel = SWT.MULTI; else selectionModel = SWT.SINGLE; FilteredTree tree = new FilteredTree(parent, selectionModel | SWT.BORDER, new PatternFilter(), true); branchTree = tree.getViewer(); branchTree.setLabelProvider(new RepositoriesViewLabelProvider()); branchTree.setContentProvider(new RepositoriesViewContentProvider()); GridDataFactory.fillDefaults().grab(true, true).hint(500, 300).applyTo( tree); branchTree.addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { String refName = refNameFromDialog(); refNameSelected(refName); } }); // double-click support branchTree.addOpenListener(new IOpenListener() { public void open(OpenEvent event) { RepositoryTreeNode node = (RepositoryTreeNode) ((IStructuredSelection) branchTree .getSelection()).getFirstElement(); if (node == null) return; if (node.getType() != RepositoryTreeNodeType.REF && node.getType() != RepositoryTreeNodeType.TAG) branchTree.setExpandedState(node, !branchTree .getExpandedState(node)); else if (getButton(Window.OK).isEnabled()) buttonPressed(OK); } }); createCustomArea(parent); setTitle(getTitle()); setMessage(getMessageText()); getShell().setText(getWindowTitle()); applyDialogFont(parent); return parent; } /** * Concrete subclasses must issue a call to super.create() when overriding * this */ @Override public void create() { super.create(); // Initially disable OK button, as the required user inputs may not be // complete after the dialog is first shown. If automatic selections // happen after this (making the user inputs complete), the button will // be enabled. getButton(Window.OK).setEnabled(false); List roots = new ArrayList(); if ((settings & SHOW_LOCAL_BRANCHES) != 0) roots.add(localBranches); if ((settings & SHOW_REMOTE_BRANCHES) != 0) roots.add(remoteBranches); if ((settings & SHOW_TAGS) != 0) roots.add(tags); if ((settings & SHOW_REFERENCES) != 0) roots.add(references); branchTree.setInput(roots); try { if ((settings & SELECT_CURRENT_REF) != 0) if (refToMark != null) markRef(refToMark); else { // initially, we mark the current head if it can be determined String fullBranch = repo.getFullBranch(); markRef(fullBranch); } if ((settings & EXPAND_LOCAL_BRANCHES_NODE) != 0) // if we can't determine a branch, we just expand local // branches branchTree.expandToLevel(localBranches, 1); if ((settings & EXPAND_REMOTE_BRANCHES_NODE) != 0) // minor UX improvement to always expand remote branches node branchTree.expandToLevel(remoteBranches, 1); } catch (IOException e) { // ignore } } /** * Set the selection to a {@link Ref} if possible * * @param refName * the name of the {@link Ref} * @return true if the {@link Ref} with the given name was * found */ protected boolean markRef(String refName) { // selects the entry specified by the name if (refName == null) return false; RepositoryTreeNode node; try { if (refName.startsWith(Constants.R_HEADS)) { Ref ref = this.repo.getRef(refName); node = new RefNode(localBranches, this.repo, ref); } else { String mappedRef = Activator.getDefault().getRepositoryUtil() .mapCommitToRef(this.repo, refName, false); if (mappedRef != null && mappedRef.startsWith(Constants.R_REMOTES)) { Ref ref = this.repo.getRef(mappedRef); node = new RefNode(remoteBranches, this.repo, ref); } else if (mappedRef != null && mappedRef.startsWith(Constants.R_TAGS)) { Ref ref = this.repo.getRef(mappedRef); node = new TagNode(tags, this.repo, ref); } else { return false; } } } catch (IOException e) { return false; } branchTree.setSelection(new StructuredSelection(node), true); return true; } /** * Will only work after the dialog was closed with the OK button * * @return the selected refName */ public String getRefName() { return this.selectedBranch; } @Override protected void okPressed() { this.selectedBranch = refNameFromDialog(); super.okPressed(); } /** * @return the selected ref name from the tree, may be null */ protected String refNameFromDialog() { IStructuredSelection sel = (IStructuredSelection) branchTree .getSelection(); if (sel.size() != 1) return null; RepositoryTreeNode node = (RepositoryTreeNode) sel.getFirstElement(); if (node.getType() == RepositoryTreeNodeType.REF || node.getType() == RepositoryTreeNodeType.TAG || node.getType() == RepositoryTreeNodeType.ADDITIONALREF) return ((Ref) node.getObject()).getName(); return null; } /** * @return the selected {@link Ref} from the tree, may be null */ protected Ref refFromDialog() { IStructuredSelection sel = (IStructuredSelection) branchTree .getSelection(); if (sel.size() != 1) return null; RepositoryTreeNode node = (RepositoryTreeNode) sel.getFirstElement(); if (node.getType() == RepositoryTreeNodeType.REF || node.getType() == RepositoryTreeNodeType.TAG) { return ((Ref) node.getObject()); } return null; } /** * Subclasses may add UI elements * * @param parent */ protected void createCustomArea(Composite parent) { // do nothing } @Override protected int getShellStyle() { return super.getShellStyle() | SWT.RESIZE; } }