diff options
author | Max Hohenegger | 2016-02-09 22:39:32 +0000 |
---|---|---|
committer | Matthias Sohn | 2016-02-22 21:50:05 +0000 |
commit | 159c34d8dfefcc5ddcfbf6912fdbb1a9aa9fa396 (patch) | |
tree | f60673dba247a5de33a3d1577b8b0c16a99e6492 /org.eclipse.egit.gitflow.ui/src/org/eclipse/egit | |
parent | 37062233f82dfa764bdef6b1cd706c6d74d3dd34 (diff) | |
download | egit-159c34d8dfefcc5ddcfbf6912fdbb1a9aa9fa396.tar.gz egit-159c34d8dfefcc5ddcfbf6912fdbb1a9aa9fa396.tar.xz egit-159c34d8dfefcc5ddcfbf6912fdbb1a9aa9fa396.zip |
Allow filtering of Gitflow feature branches in checkout and track dialog
Working with a larger number of feature branches can be tedious, if
there is no way of organizing them. A quick way of finding, known,
well-named branches, can be a simple text filter.
- replaced lists with filtered trees
- extended UI test
Change-Id: Ifc82c1fece1ed45b6ce929dcd39ecb913ce4615f
Signed-off-by: Max Hohenegger <eclipse@hohenegger.eu>
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
Diffstat (limited to 'org.eclipse.egit.gitflow.ui/src/org/eclipse/egit')
6 files changed, 298 insertions, 183 deletions
diff --git a/org.eclipse.egit.gitflow.ui/src/org/eclipse/egit/gitflow/ui/internal/actions/FeatureCheckoutHandler.java b/org.eclipse.egit.gitflow.ui/src/org/eclipse/egit/gitflow/ui/internal/actions/FeatureCheckoutHandler.java index 1ededa4713..94ec0226ae 100644 --- a/org.eclipse.egit.gitflow.ui/src/org/eclipse/egit/gitflow/ui/internal/actions/FeatureCheckoutHandler.java +++ b/org.eclipse.egit.gitflow.ui/src/org/eclipse/egit/gitflow/ui/internal/actions/FeatureCheckoutHandler.java @@ -10,7 +10,6 @@ package org.eclipse.egit.gitflow.ui.internal.actions; import static org.eclipse.egit.gitflow.ui.Activator.error; import static org.eclipse.egit.gitflow.ui.internal.JobFamilies.GITFLOW_FAMILY; -import static org.eclipse.jgit.lib.Constants.R_HEADS; import java.text.MessageFormat; import java.util.ArrayList; @@ -29,12 +28,13 @@ import org.eclipse.egit.gitflow.GitFlowRepository; import org.eclipse.egit.gitflow.op.FeatureCheckoutOperation; import org.eclipse.egit.gitflow.ui.internal.JobFamilies; import org.eclipse.egit.gitflow.ui.internal.UIText; -import org.eclipse.egit.gitflow.ui.internal.dialogs.AbstractGitFlowBranchSelectionDialog; +import org.eclipse.egit.gitflow.ui.internal.dialogs.FeatureBranchSelectionDialog; import org.eclipse.egit.ui.internal.branch.CleanupUncomittedChangesDialog; import org.eclipse.jface.window.Window; import org.eclipse.jgit.api.CheckoutResult; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; import org.eclipse.swt.widgets.Shell; @@ -55,15 +55,12 @@ public class FeatureCheckoutHandler extends AbstractHandler { final List<Ref> refs = gfRepo.getFeatureBranches(); - AbstractGitFlowBranchSelectionDialog<Ref> dialog = new AbstractGitFlowBranchSelectionDialog<Ref>( + FeatureBranchSelectionDialog dialog = new FeatureBranchSelectionDialog( HandlerUtil.getActiveShell(event), refs, UIText.FeatureCheckoutHandler_selectFeature, - UIText.FeatureCheckoutHandler_localFeatures) { - @Override - protected String getPrefix() { - return R_HEADS + gfRepo.getConfig().getFeaturePrefix(); - } - }; + UIText.FeatureCheckoutHandler_localFeatures, + Constants.R_HEADS + gfRepo.getConfig().getFeaturePrefix()); + if (dialog.open() != Window.OK) { return null; } diff --git a/org.eclipse.egit.gitflow.ui/src/org/eclipse/egit/gitflow/ui/internal/actions/FeatureTrackHandler.java b/org.eclipse.egit.gitflow.ui/src/org/eclipse/egit/gitflow/ui/internal/actions/FeatureTrackHandler.java index 46726251ef..dab77d89f6 100644 --- a/org.eclipse.egit.gitflow.ui/src/org/eclipse/egit/gitflow/ui/internal/actions/FeatureTrackHandler.java +++ b/org.eclipse.egit.gitflow.ui/src/org/eclipse/egit/gitflow/ui/internal/actions/FeatureTrackHandler.java @@ -9,6 +9,8 @@ package org.eclipse.egit.gitflow.ui.internal.actions; import static org.eclipse.egit.gitflow.op.GitFlowOperation.SEP; +import static org.eclipse.egit.gitflow.ui.Activator.error; +import static org.eclipse.egit.gitflow.ui.internal.JobFamilies.GITFLOW_FAMILY; import static org.eclipse.jgit.lib.Constants.DEFAULT_REMOTE_NAME; import static org.eclipse.jgit.lib.Constants.R_REMOTES; @@ -26,15 +28,9 @@ import org.eclipse.egit.core.internal.job.JobUtil; import org.eclipse.egit.gitflow.GitFlowRepository; import org.eclipse.egit.gitflow.op.FeatureListOperation; import org.eclipse.egit.gitflow.op.FeatureTrackOperation; - -import static org.eclipse.egit.gitflow.ui.Activator.error; - import org.eclipse.egit.gitflow.ui.Activator; - -import static org.eclipse.egit.gitflow.ui.internal.JobFamilies.GITFLOW_FAMILY; - import org.eclipse.egit.gitflow.ui.internal.UIText; -import org.eclipse.egit.gitflow.ui.internal.dialogs.AbstractGitFlowBranchSelectionDialog; +import org.eclipse.egit.gitflow.ui.internal.dialogs.FeatureBranchSelectionDialog; import org.eclipse.egit.ui.UIPreferences; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.window.Window; @@ -76,16 +72,12 @@ public class FeatureTrackHandler extends AbstractHandler { } refs.addAll(remoteFeatures); - AbstractGitFlowBranchSelectionDialog<Ref> dialog = new AbstractGitFlowBranchSelectionDialog<Ref>( - activeShell, refs, - UIText.FeatureTrackHandler_selectFeature, - UIText.FeatureTrackHandler_remoteFeatures) { - @Override - protected String getPrefix() { - return R_REMOTES + DEFAULT_REMOTE_NAME + SEP - + gfRepo.getConfig().getFeaturePrefix(); - } - }; + FeatureBranchSelectionDialog dialog = new FeatureBranchSelectionDialog( + HandlerUtil.getActiveShell(event), refs, + UIText.FeatureCheckoutHandler_selectFeature, + UIText.FeatureTrackHandler_remoteFeatures, + R_REMOTES + DEFAULT_REMOTE_NAME + SEP + gfRepo.getConfig().getFeaturePrefix()); + if (dialog.open() != Window.OK) { return Status.CANCEL_STATUS; } diff --git a/org.eclipse.egit.gitflow.ui/src/org/eclipse/egit/gitflow/ui/internal/dialogs/AbstractGitFlowBranchSelectionDialog.java b/org.eclipse.egit.gitflow.ui/src/org/eclipse/egit/gitflow/ui/internal/dialogs/AbstractGitFlowBranchSelectionDialog.java deleted file mode 100644 index e033121297..0000000000 --- a/org.eclipse.egit.gitflow.ui/src/org/eclipse/egit/gitflow/ui/internal/dialogs/AbstractGitFlowBranchSelectionDialog.java +++ /dev/null @@ -1,157 +0,0 @@ -/******************************************************************************* - * Copyright (C) 2015, Max Hohenegger <eclipse@hohenegger.eu> - * - * 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.gitflow.ui.internal.dialogs; - -import static org.eclipse.jface.dialogs.IDialogConstants.CANCEL_LABEL; -import static org.eclipse.jface.dialogs.IDialogConstants.OK_LABEL; - -import java.util.ArrayList; -import java.util.List; - -import org.eclipse.egit.ui.internal.CommonUtils; -import org.eclipse.egit.ui.internal.GitLabelProvider; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.jface.layout.GridDataFactory; -import org.eclipse.jface.viewers.ArrayContentProvider; -import org.eclipse.jface.viewers.DoubleClickEvent; -import org.eclipse.jface.viewers.IBaseLabelProvider; -import org.eclipse.jface.viewers.IDoubleClickListener; -import org.eclipse.jface.viewers.ISelectionChangedListener; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.SelectionChangedEvent; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.jface.viewers.ViewerComparator; -import org.eclipse.jface.viewers.ViewerFilter; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Shell; - -/** - * Select Git Flow branches. - * - * @param <T> - */ -public abstract class AbstractGitFlowBranchSelectionDialog<T> extends MessageDialog { - - private final List<T> nodes; - - private TableViewer branchesList; - - private List<T> selected = new ArrayList<T>(); - - /** - * @param parentShell - * @param nodes - * @param title - * @param message - */ - public AbstractGitFlowBranchSelectionDialog(Shell parentShell, List<T> nodes, - String title, String message) { - super(parentShell, title, null, message, MessageDialog.QUESTION, - new String[] { OK_LABEL, CANCEL_LABEL }, 0); - this.nodes = nodes; - } - - @Override - protected boolean isResizable() { - return true; - } - - @Override - protected Control createCustomArea(Composite parent) { - Composite area = new Composite(parent, SWT.NONE); - GridDataFactory.fillDefaults().grab(true, true).span(2, 1) - .applyTo(area); - area.setLayout(new GridLayout(1, false)); - branchesList = new TableViewer(area, SWT.SINGLE | SWT.H_SCROLL - | SWT.V_SCROLL | SWT.BORDER); - GridDataFactory.fillDefaults().grab(true, true) - .applyTo(branchesList.getControl()); - branchesList.setContentProvider(ArrayContentProvider.getInstance()); - branchesList.setLabelProvider(createLabelProvider()); - branchesList.setComparator(new ViewerComparator( - CommonUtils.STRING_ASCENDING_COMPARATOR)); - branchesList.setInput(nodes); - branchesList - .addSelectionChangedListener(new ISelectionChangedListener() { - @Override - public void selectionChanged(SelectionChangedEvent event) { - checkPage(); - } - }); - branchesList.addDoubleClickListener(new IDoubleClickListener() { - @Override - public void doubleClick(DoubleClickEvent event) { - buttonPressed(OK); - } - }); - - branchesList.addFilter(createFilter()); - return area; - } - - private ViewerFilter createFilter() { - return new ViewerFilter() { - @Override - public boolean select(Viewer viewer, Object parentElement, - Object element) { - return true; - } - }; - } - - private IBaseLabelProvider createLabelProvider() { - final String prefix = getPrefix(); - return new GitLabelProvider() { - @Override - public String getText(Object element) { - return super.getText(element).substring(prefix.length()); - } - }; - } - - /** - * @return Git Flow prefix for this dialog. E.g. feature, release, hotfix - */ - abstract protected String getPrefix(); - - private void checkPage() { - getButton(OK).setEnabled(!branchesList.getSelection().isEmpty()); - - } - - @SuppressWarnings("unchecked") - @Override - protected void buttonPressed(int buttonId) { - if (buttonId == OK) { - selected = ((IStructuredSelection) branchesList.getSelection()) - .toList(); - } - super.buttonPressed(buttonId); - } - - @Override - public void create() { - super.create(); - getButton(OK).setEnabled(false); - } - - /** - * @return the selected entry (single mode) - */ - public T getSelectedNode() { - if (selected.isEmpty()) { - return null; - } - return selected.get(0); - } -} diff --git a/org.eclipse.egit.gitflow.ui/src/org/eclipse/egit/gitflow/ui/internal/dialogs/BranchListContentProvider.java b/org.eclipse.egit.gitflow.ui/src/org/eclipse/egit/gitflow/ui/internal/dialogs/BranchListContentProvider.java new file mode 100644 index 0000000000..2d0104a69c --- /dev/null +++ b/org.eclipse.egit.gitflow.ui/src/org/eclipse/egit/gitflow/ui/internal/dialogs/BranchListContentProvider.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (C) 2016, Max Hohenegger <eclipse@hohenegger.eu> + * + * 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.gitflow.ui.internal.dialogs; + +import java.util.List; + +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.Viewer; + +class BranchListContentProvider implements ITreeContentProvider { + @Override + public void inputChanged(Viewer viewer, Object oldInput, + Object newInput) { + // do nothing + } + + @Override + public void dispose() { + // do nothing + } + + @Override + public boolean hasChildren(Object element) { + return false; + } + + @Override + public Object getParent(Object element) { + return null; + } + + @Override + public Object[] getElements(Object inputElement) { + if (inputElement instanceof List) { + return ((List) inputElement).toArray(); + } + return null; + } + + @Override + public Object[] getChildren(Object parentElement) { + return new Object[0]; + } +}
\ No newline at end of file diff --git a/org.eclipse.egit.gitflow.ui/src/org/eclipse/egit/gitflow/ui/internal/dialogs/FeatureBranchSelectionDialog.java b/org.eclipse.egit.gitflow.ui/src/org/eclipse/egit/gitflow/ui/internal/dialogs/FeatureBranchSelectionDialog.java new file mode 100644 index 0000000000..8c417b2437 --- /dev/null +++ b/org.eclipse.egit.gitflow.ui/src/org/eclipse/egit/gitflow/ui/internal/dialogs/FeatureBranchSelectionDialog.java @@ -0,0 +1,104 @@ +/******************************************************************************* + * Copyright (C) 2016, Max Hohenegger <eclipse@hohenegger.eu> + * + * 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.gitflow.ui.internal.dialogs; + +import static org.eclipse.jface.dialogs.IDialogConstants.CANCEL_LABEL; +import static org.eclipse.jface.dialogs.IDialogConstants.OK_LABEL; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.IDoubleClickListener; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jgit.lib.Ref; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Shell; + +/** + * Select Git Flow feature branches. + */ +public class FeatureBranchSelectionDialog extends MessageDialog { + private List<Ref> selected = new ArrayList<>(); + private FilteredBranchesWidget filteredFeatures; + + /** + * @param parentShell + * @param refs + * @param title + * @param message + * @param featurePrefix + */ + public FeatureBranchSelectionDialog(Shell parentShell, + List<Ref> refs, String title, String message, + String featurePrefix) { + super(parentShell, title, null, message, MessageDialog.QUESTION, + new String[] { OK_LABEL, CANCEL_LABEL }, 0); + filteredFeatures = new FilteredBranchesWidget(refs, featurePrefix); + } + + @Override + protected boolean isResizable() { + return true; + } + + @Override + protected Control createCustomArea(Composite parent) { + Control result = filteredFeatures.create(parent); + + TreeViewer viewer = filteredFeatures.getBranchesList(); + viewer.addSelectionChangedListener(new ISelectionChangedListener() { + + @Override + public void selectionChanged(SelectionChangedEvent event) { + checkPage(); + } + }); + viewer.addDoubleClickListener(new IDoubleClickListener() { + + @Override + public void doubleClick(DoubleClickEvent event) { + buttonPressed(OK); + } + }); + return result; + } + + @Override + protected void buttonPressed(int buttonId) { + if (buttonId == OK) { + selected = filteredFeatures.getSelection(); + } + super.buttonPressed(buttonId); + } + + @Override + public void create() { + super.create(); + getButton(OK).setEnabled(false); + } + + /** + * @return the selected entry (single mode) + */ + public Ref getSelectedNode() { + if (selected.isEmpty()) { + return null; + } + return selected.get(0); + } + + private void checkPage() { + getButton(OK).setEnabled(!filteredFeatures.getSelection().isEmpty()); + } +} diff --git a/org.eclipse.egit.gitflow.ui/src/org/eclipse/egit/gitflow/ui/internal/dialogs/FilteredBranchesWidget.java b/org.eclipse.egit.gitflow.ui/src/org/eclipse/egit/gitflow/ui/internal/dialogs/FilteredBranchesWidget.java new file mode 100644 index 0000000000..f4568b7c6e --- /dev/null +++ b/org.eclipse.egit.gitflow.ui/src/org/eclipse/egit/gitflow/ui/internal/dialogs/FilteredBranchesWidget.java @@ -0,0 +1,129 @@ +/******************************************************************************* + * Copyright (C) 2016, Max Hohenegger <eclipse@hohenegger.eu> + * + * 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.gitflow.ui.internal.dialogs; + +import static org.eclipse.egit.ui.internal.CommonUtils.STRING_ASCENDING_COMPARATOR; + +import java.util.List; + +import org.eclipse.egit.ui.internal.repository.tree.RepositoryTreeNodeType; +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerComparator; +import org.eclipse.jface.viewers.ViewerFilter; +import org.eclipse.jgit.lib.Ref; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.TreeColumn; +import org.eclipse.ui.dialogs.FilteredTree; +import org.eclipse.ui.dialogs.PatternFilter; + +/** + * Widget for viewing a filtered list of Gitflow branches. + */ +public class FilteredBranchesWidget { + private TreeViewer branchesViewer; + + private final List<Ref> refs; + + private String prefix; + + FilteredBranchesWidget(List<Ref> refs, String prefix) { + this.refs = refs; + this.prefix = prefix; + } + + Control create(Composite parent) { + Composite area = new Composite(parent, SWT.NONE); + GridDataFactory.fillDefaults().grab(true, true).span(2, 1).applyTo(area); + area.setLayout(new GridLayout(1, false)); + + PatternFilter filter = new PatternFilter() { + @Override + protected boolean isLeafMatch(Viewer viewer, Object element) { + TreeViewer treeViewer = (TreeViewer) viewer; + int numberOfColumns = treeViewer.getTree().getColumnCount(); + boolean isMatch = false; + for (int columnIndex = 0; columnIndex < numberOfColumns; columnIndex++) { + ColumnLabelProvider labelProvider = (ColumnLabelProvider) treeViewer + .getLabelProvider(columnIndex); + String labelText = labelProvider.getText(element); + isMatch |= wordMatches(labelText); + } + return isMatch; + } + }; + filter.setIncludeLeadingWildcard(true); + + final FilteredTree tree = new FilteredTree(area, SWT.MULTI | SWT.H_SCROLL + | SWT.V_SCROLL | SWT.BORDER | SWT.FULL_SELECTION, filter, + true); + tree.setQuickSelectionMode(true); + branchesViewer = tree.getViewer(); + + TreeColumn nameColumn = new TreeColumn(branchesViewer.getTree(), SWT.LEFT); + branchesViewer.getTree().setLinesVisible(false); + nameColumn.setAlignment(SWT.LEFT); + + GridDataFactory.fillDefaults().grab(true, true).applyTo(branchesViewer.getControl()); + + branchesViewer.setContentProvider(new BranchListContentProvider()); + branchesViewer.setLabelProvider(createLabelProvider()); + branchesViewer.setComparator(new ViewerComparator(STRING_ASCENDING_COMPARATOR)); + branchesViewer.setInput(refs); + + nameColumn.pack(); + + branchesViewer.addFilter(createFilter()); + return area; + } + + private ViewerFilter createFilter() { + return new ViewerFilter() { + @Override + public boolean select(Viewer viewer, Object parentElement, Object element) { + return true; + } + }; + } + + private ColumnLabelProvider createLabelProvider() { + return new ColumnLabelProvider() { + + @Override + public String getText(Object element) { + if (element instanceof Ref) { + String name = ((Ref) element).getName(); + return name.substring(prefix.length()); + } + return super.getText(element); + } + + @Override + public Image getImage(Object element) { + return RepositoryTreeNodeType.REF.getIcon(); + } + + }; + } + + @SuppressWarnings("unchecked") // conversion to conform to List<Ref> + List<Ref> getSelection() { + return branchesViewer.getStructuredSelection().toList(); + } + + TreeViewer getBranchesList() { + return branchesViewer; + } +} |