diff options
author | Stephen Elsemore | 2013-06-27 20:19:28 +0000 |
---|---|---|
committer | Matthias Sohn | 2013-08-11 00:30:41 +0000 |
commit | 1ef79b6d9abed44ee7facf4c665f608d85cb67cd (patch) | |
tree | 9d1b321ecc8d7f81b53b5564be6aafafc9b6e457 | |
parent | 94fb639496f53183ebda495a01b61d663f453de8 (diff) | |
download | egit-1ef79b6d9abed44ee7facf4c665f608d85cb67cd.tar.gz egit-1ef79b6d9abed44ee7facf4c665f608d85cb67cd.tar.xz egit-1ef79b6d9abed44ee7facf4c665f608d85cb67cd.zip |
[stagingView] Add presentation options (list, tree, compact tree)
This adds different options for presenting the unstaged and staged files
in the staging view.
"List" is the same as before, just a flat list of files, e.g.
file1 - src/org/eclipse
file2 - src/org/eclipse
"Tree" shows the files in a full folder tree, e.g.
* src
* org
* eclipse
* file1
* file2
"Compact Tree" shows the files in a folder tree where folders that have
just one subfolder do not result in a node, e.g.
* src/org/eclipse
* file1
* file2
When an expanded node is dragged from one tree to another
(stage/unstage), the expanded state should be preserved.
CQ: 7377
Bug: 407607
Also-by: Robin Stocker <robin@nibor.org>
Change-Id: I6509f2ee0f63fccf0391418cc3d41223e71c41de
Signed-off-by: Stephen Elsemore <selsemore@collab.net>
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
Signed-off-by: Robin Stocker <robin@nibor.org>
13 files changed, 1089 insertions, 253 deletions
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/util/ResourceUtil.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/util/ResourceUtil.java index 0d362ee317..775d1a896e 100644 --- a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/util/ResourceUtil.java +++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/util/ResourceUtil.java @@ -58,8 +58,7 @@ public class ResourceUtil { IFile file = getFileForLocationURI(root, uri); if (file != null) return file; - IContainer[] containers = root.findContainersForLocationURI(uri); - return getExistingResourceWithShortestPath(containers); + return getContainerForLocationURI(root, uri); } /** @@ -77,6 +76,20 @@ public class ResourceUtil { } /** + * Return the corresponding container if it exists. + * <p> + * The returned container will be relative to the most nested non-closed project. + * + * @param location + * @return the container, or null + */ + public static IContainer getContainerForLocation(IPath location) { + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + URI uri = URIUtil.toURI(location); + return getContainerForLocationURI(root, uri); + } + + /** * Get the {@link IFile} corresponding to the arguments if it exists. * <p> * The returned file will be relative to the most nested non-closed project. @@ -160,6 +173,12 @@ public class ResourceUtil { return getExistingResourceWithShortestPath(files); } + private static IContainer getContainerForLocationURI(IWorkspaceRoot root, + URI uri) { + IContainer[] containers = root.findContainersForLocationURI(uri); + return getExistingResourceWithShortestPath(containers); + } + private static <T extends IResource> T getExistingResourceWithShortestPath( T[] resources) { int shortestPathSegmentCount = Integer.MAX_VALUE; diff --git a/org.eclipse.egit.ui/icons/elcl16/compactLayout.gif b/org.eclipse.egit.ui/icons/elcl16/compactLayout.gif Binary files differnew file mode 100644 index 0000000000..45b1ea5591 --- /dev/null +++ b/org.eclipse.egit.ui/icons/elcl16/compactLayout.gif diff --git a/org.eclipse.egit.ui/icons/elcl16/flatLayout.gif b/org.eclipse.egit.ui/icons/elcl16/flatLayout.gif Binary files differnew file mode 100644 index 0000000000..bdef879284 --- /dev/null +++ b/org.eclipse.egit.ui/icons/elcl16/flatLayout.gif diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/PluginPreferenceInitializer.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/PluginPreferenceInitializer.java index fa98d05bbe..4b11dfc0f4 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/PluginPreferenceInitializer.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/PluginPreferenceInitializer.java @@ -16,6 +16,7 @@ import java.io.File; import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer; import org.eclipse.egit.ui.internal.decorators.GitLightweightDecorator; +import org.eclipse.egit.ui.internal.staging.StagingView; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jgit.util.FS; @@ -95,6 +96,8 @@ public class PluginPreferenceInitializer extends AbstractPreferenceInitializer { store.setDefault(UIPreferences.HISTORY_SHOW_TAG_SEQUENCE, false); store.setDefault(UIPreferences.BLAME_IGNORE_WHITESPACE, false); store.setDefault(UIPreferences.REMOTE_CONNECTION_TIMEOUT, 30 /* seconds */); + store.setDefault(UIPreferences.STAGING_VIEW_PRESENTATION, + StagingView.Presentation.LIST.name()); store.setDefault(UIPreferences.STAGING_VIEW_FILENAME_MODE, true); store.setDefault(UIPreferences.CLONE_WIZARD_STORE_SECURESTORE, false); store.setDefault(UIPreferences.COMMIT_DIALOG_HISTORY_SIZE, 10); diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/UIPreferences.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/UIPreferences.java index 4f195c6b83..92a322b752 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/UIPreferences.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/UIPreferences.java @@ -190,6 +190,8 @@ public class UIPreferences { /** */ public static final String STAGING_VIEW_FILENAME_MODE = "StagingView_FileNameMode"; //$NON-NLS-1$ /** */ + public static final String STAGING_VIEW_PRESENTATION = "StagingView_Presentation"; //$NON-NLS-1$ + /** */ public static final String PAGE_COMMIT_PREFERENCES = "org.eclipse.egit.ui.internal.preferences.CommitDialogPreferencePage"; //$NON-NLS-1$ /** */ public static final String BLAME_IGNORE_WHITESPACE = "Blame_IgnoreWhitespace"; //$NON-NLS-1$ diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/UIIcons.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/UIIcons.java index 99bf8fd112..00d6f35467 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/UIIcons.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/UIIcons.java @@ -231,6 +231,12 @@ public class UIIcons { /** Hierarchy layout icon */ public final static ImageDescriptor HIERARCHY; + /** Flat presentation icon */ + public final static ImageDescriptor FLAT; + + /** Compact tree presentation icon */ + public final static ImageDescriptor COMPACT; + /** base URL */ public final static URL base; @@ -312,6 +318,8 @@ public class UIIcons { CLEAN = map("obj16/clean_obj.gif"); //$NON-NLS-1$ STASH = map("obj16/stash.png"); //$NON-NLS-1$ HIERARCHY = map("elcl16/hierarchicalLayout.gif"); //$NON-NLS-1$ + FLAT = map("elcl16/flatLayout.gif"); //$NON-NLS-1$ + COMPACT = map("elcl16/compactLayout.gif"); //$NON-NLS-1$ } private static ImageDescriptor map(final String icon) { diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/UIText.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/UIText.java index 9f31a1e476..fe1c0f4eb8 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/UIText.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/UIText.java @@ -4669,6 +4669,18 @@ public class UIText extends NLS { public static String StagingView_DeleteItemMenuLabel; /** */ + public static String StagingView_Presentation; + + /** */ + public static String StagingView_List; + + /** */ + public static String StagingView_Tree; + + /** */ + public static String StagingView_CompactTree; + + /** */ public static String StagingView_Find; /** */ diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/StagingEntry.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/StagingEntry.java index 99f86413ad..2b2e91680d 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/StagingEntry.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/StagingEntry.java @@ -30,6 +30,9 @@ import org.eclipse.jgit.lib.Repository; * A staged/unstaged entry in the table */ public class StagingEntry implements IAdaptable, IProblemDecoratable, IDecoratableResource { + private String name; + private StagingFolderEntry parent; + /** * State of the node */ @@ -125,13 +128,20 @@ public class StagingEntry implements IAdaptable, IProblemDecoratable, IDecoratab } /** - * @return the full path for this node + * @return the repo-relative path for this file */ public String getPath() { return path; } /** + * @return the repo-relative path of the parent + */ + public IPath getParentPath() { + return new Path(path).removeLastSegments(1); + } + + /** * @return the repository for this node */ public Repository getRepository() { @@ -167,6 +177,21 @@ public class StagingEntry implements IAdaptable, IProblemDecoratable, IDecoratab return absolutePath; } + /** + * @return parent StagingFolderEntry + */ + public StagingFolderEntry getParent() { + return parent; + } + + /** + * @param parent + * StagingFolderEntry + */ + public void setParent(StagingFolderEntry parent) { + this.parent = parent; + } + public int getProblemSeverity() { IFile file = getFile(); if (file == null) @@ -192,8 +217,11 @@ public class StagingEntry implements IAdaptable, IProblemDecoratable, IDecoratab } public String getName() { - // Not used in StagingViewLabelProvider - return null; + if (name == null) { + IPath parsed = Path.fromOSString(getPath()); + name = parsed.lastSegment(); + } + return name; } public String getRepositoryName() { diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/StagingFolderEntry.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/StagingFolderEntry.java new file mode 100644 index 0000000000..1f6dd43b98 --- /dev/null +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/StagingFolderEntry.java @@ -0,0 +1,125 @@ +/******************************************************************************* + * Copyright (C) 2013, Stephen Elsemore <selsemore@collab.net> and others. + * + * 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.staging; + +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.IPath; +import org.eclipse.egit.core.internal.util.ResourceUtil; +import org.eclipse.egit.ui.internal.decorators.IProblemDecoratable; + +/** + * A staged/unstaged folder entry in the tree + */ +public class StagingFolderEntry implements IAdaptable, IProblemDecoratable { + private final IPath repoLocation; + private final IPath repoRelativePath; + private final IPath nodePath; + + private StagingFolderEntry parent; + + /** + * @param repoLocation + * @param repoRelativePath + * @param nodePath + */ + public StagingFolderEntry(IPath repoLocation, IPath repoRelativePath, + IPath nodePath) { + this.repoLocation = repoLocation; + this.repoRelativePath = repoRelativePath; + this.nodePath = nodePath; + } + + /** + * @return the container corresponding to the entry, if it exists in the + * workspace, null otherwise. + */ + public IContainer getContainer() { + return ResourceUtil.getContainerForLocation(getLocation()); + } + + public int getProblemSeverity() { + IContainer container = getContainer(); + if (container == null) + return SEVERITY_NONE; + + try { + return container.findMaxProblemSeverity(IMarker.PROBLEM, true, + IResource.DEPTH_ONE); + } catch (CoreException e) { + return SEVERITY_NONE; + } + } + + public Object getAdapter(Class adapter) { + if (adapter == IResource.class || adapter == IContainer.class) + return getContainer(); + else if (adapter == IPath.class) + return getLocation(); + return null; + } + + /** + * @return the repo-relative path of this folder + */ + public IPath getPath() { + return repoRelativePath; + } + + /** + * @return the absolute path corresponding to the folder entry + */ + public IPath getLocation() { + return repoLocation.append(repoRelativePath); + } + + /** + * @return the repo-relative path of the parent folder entry + */ + public IPath getParentPath() { + return repoRelativePath.removeLastSegments(nodePath.segmentCount()); + } + + /** + * @return the path of the node, relative to its parent + */ + public IPath getNodePath() { + return nodePath; + } + + /** + * @return the parent folder entry + */ + public StagingFolderEntry getParent() { + return parent; + } + + /** + * @param parent + */ + public void setParent(StagingFolderEntry parent) { + this.parent = parent; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof StagingFolderEntry) + return ((StagingFolderEntry) obj).getLocation().equals(getLocation()); + return super.equals(obj); + } + + @Override + public int hashCode() { + return getLocation().hashCode(); + } + +}
\ No newline at end of file diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/StagingView.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/StagingView.java index c1122d7c44..9f5fe86257 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/StagingView.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/StagingView.java @@ -18,12 +18,14 @@ import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.EnumSet; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.eclipse.core.commands.operations.IUndoContext; import org.eclipse.core.expressions.IEvaluationContext; +import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; @@ -83,17 +85,17 @@ import org.eclipse.jface.preference.IPersistentPreferenceStore; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.util.LocalSelectionTransfer; import org.eclipse.jface.viewers.ContentViewer; -import org.eclipse.jface.viewers.DecoratingStyledCellLabelProvider; -import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider; -import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider; +import org.eclipse.jface.viewers.DecoratingLabelProvider; import org.eclipse.jface.viewers.IBaseLabelProvider; +import org.eclipse.jface.viewers.ILabelDecorator; +import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.viewers.IOpenListener; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.OpenEvent; import org.eclipse.jface.viewers.StructuredSelection; -import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.jgit.api.AddCommand; @@ -135,15 +137,16 @@ import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.VerifyEvent; import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.layout.RowLayout; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.layout.RowLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.Tree; import org.eclipse.ui.IActionBars; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; @@ -192,9 +195,9 @@ public class StagingView extends ViewPart implements IShowInSource { private Section commitMessageSection; - private TableViewer stagedTableViewer; + private TreeViewer stagedViewer; - private TableViewer unstagedTableViewer; + private TreeViewer unstagedViewer; private ToggleableWarningLabel warningLabel; @@ -212,8 +215,47 @@ public class StagingView extends ViewPart implements IShowInSource { private ISelectionListener selectionChangedListener; + private ToolBarManager unstagedToolBarManager; + + private ToolBarManager stagedToolBarManager; + + private Action listPresentationAction; + + private Action treePresentationAction; + + private Action compactTreePresentationAction; + + private Action unstagedExpandAllAction; + + private Action unstagedCollapseAllAction; + + private Action stagedExpandAllAction; + + private Action stagedCollapseAllAction; + private Repository currentRepository; + private Presentation presentation = Presentation.LIST; + + private Set<IPath> pathsToExpandInStaged = new HashSet<IPath>(); + + private Set<IPath> pathsToExpandInUnstaged = new HashSet<IPath>(); + + /** + * Presentation mode of the staged/unstaged files. + */ + public enum Presentation { + /** Show files in flat list */ + LIST, + /** Show folder structure in full tree */ + TREE, + /** + * Show folder structure in compact tree (folders with only one child + * are folded into parent) + */ + COMPACT_TREE; + } + static class StagingViewUpdate { Repository repository; IndexDiffData indexDiff; @@ -276,6 +318,34 @@ public class StagingView extends ViewPart implements IShowInSource { } } + class TreeDecoratingLabelProvider extends DecoratingLabelProvider { + + ILabelProvider provider; + + ILabelDecorator decorator; + + public TreeDecoratingLabelProvider(ILabelProvider provider, + ILabelDecorator decorator) { + super(provider, decorator); + this.provider = provider; + this.decorator = decorator; + } + + public Image getColumnImage(Object element) { + Image image = provider.getImage(element); + if (image != null && decorator != null) { + Image decorated = decorator.decorateImage(image, element); + if (decorated != null) + return decorated; + } + return image; + } + + public String getText(Object element) { + return provider.getText(element); + } + } + static class StagingViewSearchThread extends Thread { private StagingView stagingView; @@ -386,29 +456,30 @@ public class StagingView extends ViewPart implements IShowInSource { unstagedSection = toolkit.createSection(stagingSashForm, ExpandableComposite.TITLE_BAR); - Composite unstagedTableComposite = toolkit + createUnstagedToolBarComposite(); + + Composite unstagedComposite = toolkit .createComposite(unstagedSection); - toolkit.paintBordersFor(unstagedTableComposite); - unstagedSection.setClient(unstagedTableComposite); + toolkit.paintBordersFor(unstagedComposite); + unstagedSection.setClient(unstagedComposite); GridLayoutFactory.fillDefaults().extendedMargins(2, 2, 2, 2) - .applyTo(unstagedTableComposite); + .applyTo(unstagedComposite); - unstagedTableViewer = new TableViewer(toolkit.createTable( - unstagedTableComposite, SWT.FULL_SELECTION | SWT.MULTI)); + unstagedViewer = createTree(unstagedComposite); GridDataFactory.fillDefaults().grab(true, true) - .applyTo(unstagedTableViewer.getControl()); - unstagedTableViewer.getTable().setData(FormToolkit.KEY_DRAW_BORDER, + .applyTo(unstagedViewer.getControl()); + unstagedViewer.getTree().setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TREE_BORDER); - unstagedTableViewer.getTable().setLinesVisible(true); - unstagedTableViewer.setLabelProvider(createLabelProvider(unstagedTableViewer)); - unstagedTableViewer.setContentProvider(new StagingViewContentProvider( + unstagedViewer.getTree().setLinesVisible(true); + unstagedViewer.setLabelProvider(createLabelProvider(unstagedViewer)); + unstagedViewer.setContentProvider(new StagingViewContentProvider(this, true)); - unstagedTableViewer.addDragSupport(DND.DROP_MOVE | DND.DROP_COPY + unstagedViewer.addDragSupport(DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_LINK, new Transfer[] { LocalSelectionTransfer.getTransfer(), FileTransfer.getInstance() }, new StagingDragListener( - unstagedTableViewer)); - unstagedTableViewer.addDropSupport(DND.DROP_MOVE, + unstagedViewer)); + unstagedViewer.addDropSupport(DND.DROP_MOVE, new Transfer[] { LocalSelectionTransfer.getTransfer() }, new DropTargetAdapter() { public void drop(DropTargetEvent event) { @@ -419,8 +490,7 @@ public class StagingView extends ViewPart implements IShowInSource { event.detail = DND.DROP_COPY; if (event.data instanceof IStructuredSelection) { final IStructuredSelection selection = (IStructuredSelection) event.data; - if (selection.getFirstElement() instanceof StagingEntry) - unstage(selection); + unstage(selection); } } @@ -428,7 +498,7 @@ public class StagingView extends ViewPart implements IShowInSource { event.detail = DND.DROP_MOVE; } }); - unstagedTableViewer.addOpenListener(new IOpenListener() { + unstagedViewer.addOpenListener(new IOpenListener() { public void open(OpenEvent event) { compareWith(event); } @@ -441,14 +511,7 @@ public class StagingView extends ViewPart implements IShowInSource { Composite commitMessageToolbarComposite = toolkit .createComposite(commitMessageSection); commitMessageToolbarComposite.setBackground(null); - RowLayout commitMessageRowLayout = new RowLayout(); - commitMessageRowLayout.marginHeight = 0; - commitMessageRowLayout.marginWidth = 0; - commitMessageRowLayout.marginTop = 0; - commitMessageRowLayout.marginBottom = 0; - commitMessageRowLayout.marginLeft = 0; - commitMessageRowLayout.marginRight = 0; - commitMessageToolbarComposite.setLayout(commitMessageRowLayout); + commitMessageToolbarComposite.setLayout(createRowLayoutWithoutMargin()); commitMessageSection.setTextClient(commitMessageToolbarComposite); ToolBarManager commitMessageToolBarManager = new ToolBarManager( SWT.FLAT | SWT.HORIZONTAL); @@ -597,28 +660,30 @@ public class StagingView extends ViewPart implements IShowInSource { stagedSection = toolkit.createSection(stagingSashForm, ExpandableComposite.TITLE_BAR); - Composite stagedTableComposite = toolkit.createComposite(stagedSection); - toolkit.paintBordersFor(stagedTableComposite); - stagedSection.setClient(stagedTableComposite); + + createStagedToolBarComposite(); + + Composite stagedComposite = toolkit.createComposite(stagedSection); + toolkit.paintBordersFor(stagedComposite); + stagedSection.setClient(stagedComposite); GridLayoutFactory.fillDefaults().extendedMargins(2, 2, 2, 2) - .applyTo(stagedTableComposite); + .applyTo(stagedComposite); - stagedTableViewer = new TableViewer(toolkit.createTable( - stagedTableComposite, SWT.FULL_SELECTION | SWT.MULTI)); + stagedViewer = createTree(stagedComposite); GridDataFactory.fillDefaults().grab(true, true) - .applyTo(stagedTableViewer.getControl()); - stagedTableViewer.getTable().setData(FormToolkit.KEY_DRAW_BORDER, + .applyTo(stagedViewer.getControl()); + stagedViewer.getTree().setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TREE_BORDER); - stagedTableViewer.getTable().setLinesVisible(true); - stagedTableViewer.setLabelProvider(createLabelProvider(stagedTableViewer)); - stagedTableViewer.setContentProvider(new StagingViewContentProvider( + stagedViewer.getTree().setLinesVisible(true); + stagedViewer.setLabelProvider(createLabelProvider(stagedViewer)); + stagedViewer.setContentProvider(new StagingViewContentProvider(this, false)); - stagedTableViewer.addDragSupport( + stagedViewer.addDragSupport( DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_LINK, new Transfer[] { LocalSelectionTransfer.getTransfer(), FileTransfer.getInstance() }, new StagingDragListener( - stagedTableViewer)); - stagedTableViewer.addDropSupport(DND.DROP_MOVE, + stagedViewer)); + stagedViewer.addDropSupport(DND.DROP_MOVE, new Transfer[] { LocalSelectionTransfer.getTransfer() }, new DropTargetAdapter() { public void drop(DropTargetEvent event) { @@ -644,7 +709,7 @@ public class StagingView extends ViewPart implements IShowInSource { event.detail = DND.DROP_MOVE; } }); - stagedTableViewer.addOpenListener(new IOpenListener() { + stagedViewer.addOpenListener(new IOpenListener() { public void open(OpenEvent event) { compareWith(event); } @@ -682,8 +747,8 @@ public class StagingView extends ViewPart implements IShowInSource { updateToolbar(); enableCommitWidgets(false); - createPopupMenu(unstagedTableViewer); - createPopupMenu(stagedTableViewer); + createPopupMenu(unstagedViewer); + createPopupMenu(stagedViewer); final ICommitMessageComponentNotifications listener = new ICommitMessageComponentNotifications() { @@ -745,41 +810,116 @@ public class StagingView extends ViewPart implements IShowInSource { selectionChangedListener.selectionChanged(part, selection); } - site.setSelectionProvider(unstagedTableViewer); + site.setSelectionProvider(unstagedViewer); ViewerFilter filter = new ViewerFilter() { @Override public boolean select(Viewer viewer, Object parentElement, Object element) { - if (element instanceof StagingEntry) { - if (filterText != null && filterText.getText() != null - && filterText.getText().trim().length() > 0) { - return ((StagingEntry) element) - .getPath() - .toUpperCase() - .contains( - filterText.getText().trim() - .toUpperCase()); - } - } + StagingViewContentProvider contentProvider = getContentProvider((TreeViewer) viewer); + if (element instanceof StagingEntry) + return contentProvider.isInFilter((StagingEntry) element); + else if (element instanceof StagingFolderEntry) + return contentProvider + .hasVisibleChildren((StagingFolderEntry) element); return true; } }; - unstagedTableViewer.addFilter(filter); - stagedTableViewer.addFilter(filter); + unstagedViewer.addFilter(filter); + stagedViewer.addFilter(filter); + } + + private void createUnstagedToolBarComposite() { + Composite unstagedToolbarComposite = toolkit + .createComposite(unstagedSection); + unstagedToolbarComposite.setBackground(null); + unstagedToolbarComposite.setLayout(createRowLayoutWithoutMargin()); + unstagedSection.setTextClient(unstagedToolbarComposite); + unstagedExpandAllAction = new Action(UIText.UIUtils_ExpandAll, + IAction.AS_PUSH_BUTTON) { + public void run() { + unstagedViewer.expandAll(); + } + }; + unstagedExpandAllAction.setImageDescriptor(UIIcons.EXPAND_ALL); + + unstagedCollapseAllAction = new Action(UIText.UIUtils_CollapseAll, + IAction.AS_PUSH_BUTTON) { + public void run() { + unstagedViewer.collapseAll(); + } + }; + unstagedCollapseAllAction.setImageDescriptor(UIIcons.COLLAPSEALL); + + unstagedToolBarManager = new ToolBarManager(SWT.FLAT | SWT.HORIZONTAL); + + unstagedToolBarManager.add(unstagedExpandAllAction); + unstagedToolBarManager.add(unstagedCollapseAllAction); + + unstagedToolBarManager.update(true); + unstagedToolBarManager.createControl(unstagedToolbarComposite); + } + + private void createStagedToolBarComposite() { + Composite stagedToolbarComposite = toolkit + .createComposite(stagedSection); + stagedToolbarComposite.setBackground(null); + stagedToolbarComposite.setLayout(createRowLayoutWithoutMargin()); + stagedSection.setTextClient(stagedToolbarComposite); + stagedExpandAllAction = new Action(UIText.UIUtils_ExpandAll, + IAction.AS_PUSH_BUTTON) { + public void run() { + stagedViewer.expandAll(); + } + }; + stagedExpandAllAction.setImageDescriptor(UIIcons.EXPAND_ALL); + + stagedCollapseAllAction = new Action(UIText.UIUtils_CollapseAll, + IAction.AS_PUSH_BUTTON) { + public void run() { + stagedViewer.collapseAll(); + } + }; + stagedCollapseAllAction.setImageDescriptor(UIIcons.COLLAPSEALL); + + stagedToolBarManager = new ToolBarManager(SWT.FLAT | SWT.HORIZONTAL); + + stagedToolBarManager.add(stagedExpandAllAction); + stagedToolBarManager.add(stagedCollapseAllAction); + stagedToolBarManager.update(true); + stagedToolBarManager.createControl(stagedToolbarComposite); + } + + private static RowLayout createRowLayoutWithoutMargin() { + RowLayout layout = new RowLayout(); + layout.marginHeight = 0; + layout.marginWidth = 0; + layout.marginTop = 0; + layout.marginBottom = 0; + layout.marginLeft = 0; + layout.marginRight = 0; + return layout; + } + + /** + * @return selected repository + */ + public Repository getCurrentRepository() { + return currentRepository; } public ShowInContext getShowInContext() { - if (stagedTableViewer != null && stagedTableViewer.getTable().isFocusControl()) - return getShowInContext(stagedTableViewer); - else if (unstagedTableViewer != null && unstagedTableViewer.getTable().isFocusControl()) - return getShowInContext(unstagedTableViewer); + if (stagedViewer != null && stagedViewer.getTree().isFocusControl()) + return getShowInContext(stagedViewer); + else if (unstagedViewer != null + && unstagedViewer.getTree().isFocusControl()) + return getShowInContext(unstagedViewer); else return null; } - private ShowInContext getShowInContext(TableViewer tableViewer) { - IStructuredSelection selection = (IStructuredSelection) tableViewer.getSelection(); + private ShowInContext getShowInContext(TreeViewer treeViewer) { + IStructuredSelection selection = (IStructuredSelection) treeViewer.getSelection(); List<Object> elements = new ArrayList<Object>(); for (Object selectedElement : selection.toList()) { if (selectedElement instanceof StagingEntry) { @@ -789,6 +929,13 @@ public class StagingView extends ViewPart implements IShowInSource { elements.add(file); else elements.add(entry.getLocation()); + } else if (selectedElement instanceof StagingFolderEntry) { + StagingFolderEntry entry = (StagingFolderEntry) selectedElement; + IContainer container = entry.getContainer(); + if (container != null) + elements.add(container); + else + elements.add(entry.getLocation()); } } return new ShowInContext(null, new StructuredSelection(elements)); @@ -920,10 +1067,9 @@ public class StagingView extends ViewPart implements IShowInSource { public void run() { final boolean enable = isChecked(); - getLabelProvider(stagedTableViewer).setFileNameMode(enable); - getLabelProvider(unstagedTableViewer).setFileNameMode(enable); - stagedTableViewer.refresh(); - unstagedTableViewer.refresh(); + getLabelProvider(stagedViewer).setFileNameMode(enable); + getLabelProvider(unstagedViewer).setFileNameMode(enable); + refreshViewers(); getPreferenceStore().setValue( UIPreferences.STAGING_VIEW_FILENAME_MODE, enable); } @@ -932,6 +1078,81 @@ public class StagingView extends ViewPart implements IShowInSource { UIPreferences.STAGING_VIEW_FILENAME_MODE)); IMenuManager dropdownMenu = actionBars.getMenuManager(); + MenuManager presentationMenu = new MenuManager( + UIText.StagingView_Presentation); + listPresentationAction = new Action(UIText.StagingView_List, + IAction.AS_RADIO_BUTTON) { + public void run() { + presentation = Presentation.LIST; + getPreferenceStore().setValue( + UIPreferences.STAGING_VIEW_PRESENTATION, + Presentation.LIST.name()); + treePresentationAction.setChecked(false); + compactTreePresentationAction.setChecked(false); + setExpandCollapseActionsVisible(false); + refreshViewers(); + } + }; + listPresentationAction.setImageDescriptor(UIIcons.FLAT); + presentationMenu.add(listPresentationAction); + + treePresentationAction = new Action(UIText.StagingView_Tree, + IAction.AS_RADIO_BUTTON) { + public void run() { + presentation = Presentation.TREE; + getPreferenceStore().setValue( + UIPreferences.STAGING_VIEW_PRESENTATION, + Presentation.TREE.name()); + listPresentationAction.setChecked(false); + compactTreePresentationAction.setChecked(false); + setExpandCollapseActionsVisible(true); + refreshViewers(); + } + }; + treePresentationAction.setImageDescriptor(UIIcons.HIERARCHY); + presentationMenu.add(treePresentationAction); + + compactTreePresentationAction = new Action(UIText.StagingView_CompactTree, + IAction.AS_RADIO_BUTTON) { + public void run() { + presentation = Presentation.COMPACT_TREE; + getPreferenceStore().setValue( + UIPreferences.STAGING_VIEW_PRESENTATION, + Presentation.COMPACT_TREE.name()); + listPresentationAction.setChecked(false); + treePresentationAction.setChecked(false); + setExpandCollapseActionsVisible(true); + refreshViewers(); + } + }; + compactTreePresentationAction.setImageDescriptor(UIIcons.COMPACT); + presentationMenu.add(compactTreePresentationAction); + + String presentationString = getPreferenceStore().getString( + UIPreferences.STAGING_VIEW_PRESENTATION); + if (presentationString.length() > 0) { + try { + presentation = Presentation.valueOf(presentationString); + } catch (IllegalArgumentException e) { + // Use already set value of presentation + } + } + switch (presentation) { + case LIST: + listPresentationAction.setChecked(true); + setExpandCollapseActionsVisible(false); + break; + case TREE: + treePresentationAction.setChecked(true); + break; + case COMPACT_TREE: + compactTreePresentationAction.setChecked(true); + break; + default: + break; + } + dropdownMenu.add(presentationMenu); + dropdownMenu.add(new Separator()); dropdownMenu.add(openNewCommitsAction); dropdownMenu.add(columnLayoutAction); dropdownMenu.add(fileNameModeAction); @@ -947,13 +1168,35 @@ public class StagingView extends ViewPart implements IShowInSource { actionBars.updateActionBars(); } - private IBaseLabelProvider createLabelProvider(TableViewer tableViewer) { - StagingViewLabelProvider baseProvider = new StagingViewLabelProvider(); + private void setExpandCollapseActionsVisible(boolean visible) { + for (IContributionItem item : unstagedToolBarManager.getItems()) + item.setVisible(visible); + for (IContributionItem item : stagedToolBarManager.getItems()) + item.setVisible(visible); + unstagedExpandAllAction.setEnabled(visible); + unstagedCollapseAllAction.setEnabled(visible); + stagedExpandAllAction.setEnabled(visible); + stagedCollapseAllAction.setEnabled(visible); + unstagedToolBarManager.update(true); + stagedToolBarManager.update(true); + } + + private TreeViewer createTree(Composite composite) { + Tree tree = toolkit.createTree(composite, SWT.FULL_SELECTION + | SWT.MULTI); + tree.setLinesVisible(true); + TreeViewer treeViewer = new TreeViewer(tree); + return treeViewer; + } + + private IBaseLabelProvider createLabelProvider(TreeViewer treeViewer) { + StagingViewLabelProvider baseProvider = new StagingViewLabelProvider( + this); baseProvider.setFileNameMode(getPreferenceStore().getBoolean( UIPreferences.STAGING_VIEW_FILENAME_MODE)); - ProblemLabelDecorator decorator = new ProblemLabelDecorator(tableViewer); - return new DecoratingStyledCellLabelProvider(baseProvider, decorator, null); + ProblemLabelDecorator decorator = new ProblemLabelDecorator(treeViewer); + return new TreeDecoratingLabelProvider(baseProvider, decorator); } private IPreferenceStore getPreferenceStore() { @@ -962,9 +1205,9 @@ public class StagingView extends ViewPart implements IShowInSource { private StagingViewLabelProvider getLabelProvider(ContentViewer viewer) { IBaseLabelProvider base = viewer.getLabelProvider(); - IStyledLabelProvider styled = ((DelegatingStyledCellLabelProvider) base) - .getStyledStringProvider(); - return (StagingViewLabelProvider) styled; + ILabelProvider labelProvider = ((TreeDecoratingLabelProvider) base) + .getLabelProvider(); + return (StagingViewLabelProvider) labelProvider; } private StagingViewContentProvider getContentProvider(ContentViewer viewer) { @@ -972,22 +1215,22 @@ public class StagingView extends ViewPart implements IShowInSource { } private void updateSectionText() { - stagedSection.setText(MessageFormat.format( - UIText.StagingView_StagedChanges, - getSectionCount(stagedTableViewer))); + stagedSection.setText(MessageFormat + .format(UIText.StagingView_StagedChanges, + getSectionCount(stagedViewer))); unstagedSection.setText(MessageFormat.format( UIText.StagingView_UnstagedChanges, - getSectionCount(unstagedTableViewer))); + getSectionCount(unstagedViewer))); } - private String getSectionCount(TableViewer viewer) { - int stagingEntryCount = ((StagingViewContentProvider) viewer - .getContentProvider()).getStagingEntryCount(); - int itemCount = viewer.getTable().getItemCount(); - if (itemCount == stagingEntryCount) - return Integer.toString(itemCount); + private String getSectionCount(TreeViewer viewer) { + StagingViewContentProvider contentProvider = getContentProvider(viewer); + int count = contentProvider.getCount(); + int shownCount = contentProvider.getShownCount(); + if (shownCount == count) + return Integer.toString(count); else - return itemCount + "/" + stagingEntryCount; //$NON-NLS-1$ + return shownCount + "/" + count; //$NON-NLS-1$ } private void updateMessage() { @@ -1010,7 +1253,8 @@ public class StagingView extends ViewPart implements IShowInSource { private void compareWith(OpenEvent event) { IStructuredSelection selection = (IStructuredSelection) event .getSelection(); - if (selection.isEmpty()) + if (selection.isEmpty() + || !(selection.getFirstElement() instanceof StagingEntry)) return; StagingEntry stagingEntry = (StagingEntry) selection.getFirstElement(); if (stagingEntry.isSubmodule()) @@ -1034,36 +1278,58 @@ public class StagingView extends ViewPart implements IShowInSource { } } - private void createPopupMenu(final TableViewer tableViewer) { + private void createPopupMenu(final TreeViewer treeViewer) { final MenuManager menuMgr = new MenuManager(); menuMgr.setRemoveAllWhenShown(true); - Control control = tableViewer.getControl(); + Control control = treeViewer.getControl(); control.setMenu(menuMgr.createContextMenu(control)); menuMgr.addMenuListener(new IMenuListener() { public void menuAboutToShow(IMenuManager manager) { - IStructuredSelection selection = (IStructuredSelection) tableViewer.getSelection(); + IStructuredSelection selection = (IStructuredSelection) treeViewer.getSelection(); if (selection.isEmpty()) return; + List<StagingEntry> stagingEntryList = new ArrayList<StagingEntry>(); + boolean submoduleSelected = false; - for (Object item : selection.toArray()) - if (((StagingEntry) item).isSubmodule()) { - submoduleSelected = true; - break; + boolean folderSelected = false; + for (Object element : selection.toArray()) { + if (element instanceof StagingFolderEntry) { + StagingFolderEntry folder = (StagingFolderEntry) element; + folderSelected = true; + StagingViewContentProvider contentProvider = getContentProvider(treeViewer); + List<StagingEntry> stagingEntries = contentProvider + .getStagingEntriesFiltered(folder); + for (StagingEntry stagingEntry : stagingEntries) { + if (!stagingEntryList.contains(stagingEntry)) + stagingEntryList.add(stagingEntry); + } + } else if (element instanceof StagingEntry) { + StagingEntry entry = (StagingEntry) element; + if (entry.isSubmodule()) + submoduleSelected = true; + if (!stagingEntryList.contains(entry)) + stagingEntryList.add(entry); } + } - Action openWorkingTreeVersion = new Action( - UIText.CommitFileDiffViewer_OpenWorkingTreeVersionInEditorMenuLabel) { - @Override - public void run() { - openSelectionInEditor(tableViewer.getSelection()); - } - }; - openWorkingTreeVersion.setEnabled(!submoduleSelected); - menuMgr.add(openWorkingTreeVersion); + final IStructuredSelection fileSelection = new StructuredSelection( + stagingEntryList); - Set<StagingEntry.Action> availableActions = getAvailableActions(selection); + if (!folderSelected) { + Action openWorkingTreeVersion = new Action( + UIText.CommitFileDiffViewer_OpenWorkingTreeVersionInEditorMenuLabel) { + @Override + public void run() { + openSelectionInEditor(fileSelection); + } + }; + openWorkingTreeVersion.setEnabled(!submoduleSelected); + menuMgr.add(openWorkingTreeVersion); + } + + Set<StagingEntry.Action> availableActions = getAvailableActions(fileSelection); boolean addReplaceWithFileInGitIndex = availableActions.contains(StagingEntry.Action.REPLACE_WITH_FILE_IN_GIT_INDEX); boolean addReplaceWithHeadRevision = availableActions.contains(StagingEntry.Action.REPLACE_WITH_HEAD_REVISION); @@ -1077,34 +1343,42 @@ public class StagingView extends ViewPart implements IShowInSource { menuMgr.add(new Action(UIText.StagingView_StageItemMenuLabel) { @Override public void run() { - stage((IStructuredSelection) tableViewer.getSelection()); + stage(fileSelection); } }); if (addUnstage) menuMgr.add(new Action(UIText.StagingView_UnstageItemMenuLabel) { @Override public void run() { - unstage((IStructuredSelection) tableViewer.getSelection()); + unstage(fileSelection); } }); - boolean selectionIncludesNonWorkspaceResources = selectionIncludesNonWorkspaceResources(tableViewer.getSelection()); + boolean selectionIncludesNonWorkspaceResources = selectionIncludesNonWorkspaceResources(fileSelection); if (addReplaceWithFileInGitIndex) if (selectionIncludesNonWorkspaceResources) - menuMgr.add(new ReplaceAction(UIText.StagingView_replaceWithFileInGitIndex, selection, false)); + menuMgr.add(new ReplaceAction( + UIText.StagingView_replaceWithFileInGitIndex, + fileSelection, false)); else - menuMgr.add(createItem(ActionCommands.DISCARD_CHANGES_ACTION, tableViewer)); // replace with index + menuMgr.add(createItem( + ActionCommands.DISCARD_CHANGES_ACTION, + fileSelection)); // replace with index if (addReplaceWithHeadRevision) if (selectionIncludesNonWorkspaceResources) - menuMgr.add(new ReplaceAction(UIText.StagingView_replaceWithHeadRevision, selection, true)); + menuMgr.add(new ReplaceAction( + UIText.StagingView_replaceWithHeadRevision, + fileSelection, true)); else - menuMgr.add(createItem(ActionCommands.REPLACE_WITH_HEAD_ACTION, tableViewer)); + menuMgr.add(createItem( + ActionCommands.REPLACE_WITH_HEAD_ACTION, + fileSelection)); if (addIgnore) - menuMgr.add(new IgnoreAction(selection)); + menuMgr.add(new IgnoreAction(fileSelection)); if (addDelete) - menuMgr.add(new DeleteAction(selection)); + menuMgr.add(new DeleteAction(fileSelection)); if (addLaunchMergeTool) - menuMgr.add(createItem(ActionCommands.MERGE_TOOL_ACTION, tableViewer)); - + menuMgr.add(createItem(ActionCommands.MERGE_TOOL_ACTION, + fileSelection)); menuMgr.add(new Separator()); menuMgr.add(createShowInMenu()); } @@ -1113,14 +1387,37 @@ public class StagingView extends ViewPart implements IShowInSource { } /** + * @return selected presentation + */ + Presentation getPresentation() { + return presentation; + } + + /** + * @return the trimmed string which is the current filter, empty string for + * no filter + */ + String getFilterString() { + if (filterText != null) + return filterText.getText().trim(); + else + return ""; //$NON-NLS-1$ + } + + /** * Refresh the unstaged and staged viewers */ public void refreshViewers() { - Display.getDefault().asyncExec(new Runnable() { + Display.getDefault().syncExec(new Runnable() { public void run() { - unstagedTableViewer.refresh(); - stagedTableViewer.refresh(); + Object[] unstagedExpanded = unstagedViewer + .getExpandedElements(); + Object[] stagedExpanded = stagedViewer.getExpandedElements(); + unstagedViewer.refresh(); + stagedViewer.refresh(); updateSectionText(); + unstagedViewer.setExpandedElements(unstagedExpanded); + stagedViewer.setExpandedElements(stagedExpanded); } }); } @@ -1198,7 +1495,7 @@ public class StagingView extends ViewPart implements IShowInSource { @Override public boolean isEnabled() { - if (!unstagedTableViewer.getTable().isFocusControl()) + if (!unstagedViewer.getTree().isFocusControl()) return false; IStructuredSelection selection = getSelection(); @@ -1206,6 +1503,8 @@ public class StagingView extends ViewPart implements IShowInSource { return false; for (Object element : selection.toList()) { + if (!(element instanceof StagingEntry)) + return false; StagingEntry entry = (StagingEntry) element; if (!entry.getAvailableActions().contains(StagingEntry.Action.DELETE)) return false; @@ -1215,7 +1514,7 @@ public class StagingView extends ViewPart implements IShowInSource { } private IStructuredSelection getSelection() { - return (IStructuredSelection) unstagedTableViewer.getSelection(); + return (IStructuredSelection) unstagedViewer.getSelection(); } } @@ -1238,8 +1537,11 @@ public class StagingView extends ViewPart implements IShowInSource { List<String> result = new ArrayList<String>(); Iterator iterator = selection.iterator(); while (iterator.hasNext()) { - StagingEntry stagingEntry = (StagingEntry) iterator.next(); - result.add(stagingEntry.getPath()); + Object selectedItem = iterator.next(); + if (selectedItem instanceof StagingEntry) { + StagingEntry stagingEntry = (StagingEntry) selectedItem; + result.add(stagingEntry.getPath()); + } } return result.toArray(new String[result.size()]); } @@ -1315,7 +1617,8 @@ public class StagingView extends ViewPart implements IShowInSource { return availableActions; } - private CommandContributionItem createItem(String itemAction, final TableViewer tableViewer) { + private CommandContributionItem createItem(String itemAction, + final ISelection selection) { IWorkbench workbench = PlatformUI.getWorkbench(); CommandContributionItemParameter itemParam = new CommandContributionItemParameter( workbench, null, itemAction, STYLE_PUSH); @@ -1325,7 +1628,7 @@ public class StagingView extends ViewPart implements IShowInSource { IHandlerService hsr = (IHandlerService) activeWorkbenchWindow .getService(IHandlerService.class); IEvaluationContext ctx = hsr.getCurrentState(); - ctx.addVariable(ACTIVE_MENU_SELECTION_NAME, tableViewer.getSelection()); + ctx.addVariable(ACTIVE_MENU_SELECTION_NAME, selection); return new CommandContributionItem(itemParam); } @@ -1359,31 +1662,26 @@ public class StagingView extends ViewPart implements IShowInSource { } private void stage(IStructuredSelection selection) { + StagingViewContentProvider contentProvider = getContentProvider(unstagedViewer); Git git = new Git(currentRepository); Iterator iterator = selection.iterator(); List<String> addPaths = new ArrayList<String>(); List<String> rmPaths = new ArrayList<String>(); + resetPathsToExpand(); while (iterator.hasNext()) { Object element = iterator.next(); if (element instanceof StagingEntry) { StagingEntry entry = (StagingEntry) element; - switch (entry.getState()) { - case ADDED: - case CHANGED: - case REMOVED: - // already staged - break; - case CONFLICTING: - case MODIFIED: - case PARTIALLY_MODIFIED: - case UNTRACKED: - addPaths.add(entry.getPath()); - break; - case MISSING: - case MISSING_AND_CHANGED: - rmPaths.add(entry.getPath()); - break; - } + selectEntryForStaging(entry, addPaths, rmPaths); + addPathAndParentPaths(entry.getParentPath(), pathsToExpandInStaged); + } else if (element instanceof StagingFolderEntry) { + StagingFolderEntry folder = (StagingFolderEntry) element; + List<StagingEntry> entries = contentProvider + .getStagingEntriesFiltered(folder); + for (StagingEntry entry : entries) + selectEntryForStaging(entry, addPaths, rmPaths); + addExpandedPathsBelowFolder(folder, unstagedViewer, + pathsToExpandInStaged); } else { IResource resource = AdapterUtils.adapt(element, IResource.class); if (resource != null) { @@ -1430,6 +1728,27 @@ public class StagingView extends ViewPart implements IShowInSource { } } + private void selectEntryForStaging(StagingEntry entry, + List<String> addPaths, List<String> rmPaths) { + switch (entry.getState()) { + case ADDED: + case CHANGED: + case REMOVED: + // already staged + break; + case CONFLICTING: + case MODIFIED: + case PARTIALLY_MODIFIED: + case UNTRACKED: + addPaths.add(entry.getPath()); + break; + case MISSING: + case MISSING_AND_CHANGED: + rmPaths.add(entry.getPath()); + break; + } + } + private void unstage(IStructuredSelection selection) { if (selection.isEmpty()) return; @@ -1464,7 +1783,7 @@ public class StagingView extends ViewPart implements IShowInSource { } try { - updateDirCache(selection, headRev, edit); + processUnstageSelection(selection, headRev, edit); try { edit.commit(); @@ -1479,44 +1798,84 @@ public class StagingView extends ViewPart implements IShowInSource { } } - private void updateDirCache(IStructuredSelection selection, + private void processUnstageSelection(IStructuredSelection selection, final RevCommit headRev, final DirCacheEditor edit) { - Iterator iterator = selection.iterator(); - while (iterator.hasNext()) { - StagingEntry entry = (StagingEntry) iterator.next(); - switch (entry.getState()) { - case ADDED: - edit.add(new DirCacheEditor.DeletePath(entry.getPath())); - break; - case CHANGED: - case REMOVED: - // set the index object id/file mode back to our head revision - try { - final TreeWalk tw = TreeWalk.forPath(currentRepository, - entry.getPath(), headRev.getTree()); - if (tw != null) - edit.add(new DirCacheEditor.PathEdit(entry.getPath()) { - @Override - public void apply(DirCacheEntry ent) { - ent.setFileMode(tw.getFileMode(0)); - ent.setObjectId(tw.getObjectId(0)); - // for index & working tree compare - ent.setLastModified(0); - } - }); - } catch (IOException e) { - // TODO fix text - MessageDialog.openError(getSite().getShell(), - UIText.CommitAction_MergeHeadErrorTitle, - UIText.CommitAction_ErrorReadingMergeMsg); - } - break; - default: - // unstaged + resetPathsToExpand(); + for (Object element : selection.toList()) { + if (element instanceof StagingEntry) { + StagingEntry entry = (StagingEntry) element; + updateDirCache(headRev, edit, entry); + addPathAndParentPaths(entry.getParentPath(), pathsToExpandInUnstaged); + } else if (element instanceof StagingFolderEntry) { + StagingFolderEntry folder = (StagingFolderEntry) element; + List<StagingEntry> entries = getContentProvider(stagedViewer) + .getStagingEntriesFiltered(folder); + for (StagingEntry entry : entries) + updateDirCache(headRev, edit, entry); + addExpandedPathsBelowFolder(folder, stagedViewer, + pathsToExpandInUnstaged); } } } + private void updateDirCache(final RevCommit headRev, + final DirCacheEditor edit, final StagingEntry entry) { + switch (entry.getState()) { + case ADDED: + edit.add(new DirCacheEditor.DeletePath(entry.getPath())); + break; + case CHANGED: + case REMOVED: + // set the index object id/file mode back to our head revision + try { + final TreeWalk tw = TreeWalk.forPath(currentRepository, + entry.getPath(), headRev.getTree()); + if (tw != null) + edit.add(new DirCacheEditor.PathEdit(entry.getPath()) { + @Override + public void apply(DirCacheEntry ent) { + ent.setFileMode(tw.getFileMode(0)); + ent.setObjectId(tw.getObjectId(0)); + // for index & working tree compare + ent.setLastModified(0); + } + }); + } catch (IOException e) { + // TODO fix text + MessageDialog.openError(getSite().getShell(), + UIText.CommitAction_MergeHeadErrorTitle, + UIText.CommitAction_ErrorReadingMergeMsg); + } + break; + default: + // unstaged + } + } + + private void resetPathsToExpand() { + pathsToExpandInStaged = new HashSet<IPath>(); + pathsToExpandInUnstaged = new HashSet<IPath>(); + } + + private static void addExpandedPathsBelowFolder(StagingFolderEntry folder, + TreeViewer treeViewer, Set<IPath> addToSet) { + Object[] expandedElements = treeViewer.getExpandedElements(); + for (Object expandedElement : expandedElements) { + if (expandedElement instanceof StagingFolderEntry) { + StagingFolderEntry expandedFolder = (StagingFolderEntry) expandedElement; + if (folder.getPath().isPrefixOf( + expandedFolder.getPath())) + addPathAndParentPaths(expandedFolder.getPath(), addToSet); + } + } + } + + private static void addPathAndParentPaths(IPath initialPath, Set<IPath> addToSet) { + for (IPath p = initialPath; p.segmentCount() >= 1; p = p + .removeLastSegments(1)) + addToSet.add(p); + } + private boolean isValidRepo(final Repository repository) { return repository != null && !repository.isBare() @@ -1534,8 +1893,8 @@ public class StagingView extends ViewPart implements IShowInSource { saveCommitMessageComponentState(); currentRepository = null; StagingViewUpdate update = new StagingViewUpdate(null, null, null); - unstagedTableViewer.setInput(update); - stagedTableViewer.setInput(update); + unstagedViewer.setInput(update); + stagedViewer.setInput(update); enableCommitWidgets(false); updateSectionText(); form.setText(UIText.StagingView_NoSelectionTitle); @@ -1567,9 +1926,21 @@ public class StagingView extends ViewPart implements IShowInSource { boolean indexDiffAvailable = indexDiff != null; + if (repositoryChanged) + // Reset paths, they're from the old repository + resetPathsToExpand(); + final StagingViewUpdate update = new StagingViewUpdate(currentRepository, indexDiff, null); - unstagedTableViewer.setInput(update); - stagedTableViewer.setInput(update); + Object[] unstagedExpanded = unstagedViewer + .getExpandedElements(); + Object[] stagedExpanded = stagedViewer + .getExpandedElements(); + unstagedViewer.setInput(update); + stagedViewer.setInput(update); + expandPreviousExpandedAndPaths(unstagedExpanded, unstagedViewer, + pathsToExpandInUnstaged); + expandPreviousExpandedAndPaths(stagedExpanded, stagedViewer, + pathsToExpandInStaged); enableCommitWidgets(indexDiffAvailable); boolean commitEnabled = indexDiffAvailable && repository.getRepositoryState().canCommit(); @@ -1596,6 +1967,25 @@ public class StagingView extends ViewPart implements IShowInSource { return cacheEntry.getIndexDiff(); } + private void expandPreviousExpandedAndPaths(Object[] previous, + TreeViewer viewer, Set<IPath> additionalPaths) { + Set<IPath> paths = new HashSet<IPath>(additionalPaths); + // Instead of just expanding the previous elements directly, also expand + // all parent paths. This makes it work in case of "re-folding" of + // compact tree. + for (Object element : previous) + if (element instanceof StagingFolderEntry) + addPathAndParentPaths(((StagingFolderEntry) element).getPath(), paths); + List<Object> expand = new ArrayList<Object>(); + StagingViewContentProvider stagedContentProvider = getContentProvider(viewer); + for (StagingFolderEntry folder : stagedContentProvider + .getStagingFolderEntries()) { + if (paths.contains(folder.getPath())) + expand.add(folder); + } + viewer.setExpandedElements(expand.toArray()); + } + private void clearCommitMessageToggles() { amendPreviousCommitAction.setChecked(false); addChangeIdAction.setChecked(false); @@ -1750,7 +2140,7 @@ public class StagingView extends ViewPart implements IShowInSource { } private Collection<String> getStagedFileNames() { - StagingViewContentProvider stagedContentProvider = getContentProvider(stagedTableViewer); + StagingViewContentProvider stagedContentProvider = getContentProvider(stagedViewer); StagingEntry[] entries = stagedContentProvider.getStagingEntries(); List<String> files = new ArrayList<String>(); for (StagingEntry entry : entries) @@ -1795,7 +2185,7 @@ public class StagingView extends ViewPart implements IShowInSource { } private boolean isCommitWithoutFilesAllowed() { - if (stagedTableViewer.getTable().getItemCount() > 0) + if (stagedViewer.getTree().getItemCount() > 0) return true; if (amendPreviousCommitAction.isChecked()) @@ -1806,7 +2196,7 @@ public class StagingView extends ViewPart implements IShowInSource { @Override public void setFocus() { - unstagedTableViewer.getControl().setFocus(); + unstagedViewer.getControl().setFocus(); } @Override diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/StagingViewContentProvider.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/StagingViewContentProvider.java index 25c62b269b..dcb7ee99eb 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/StagingViewContentProvider.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/StagingViewContentProvider.java @@ -19,46 +19,242 @@ import static org.eclipse.egit.ui.internal.staging.StagingEntry.State.REMOVED; import static org.eclipse.egit.ui.internal.staging.StagingEntry.State.UNTRACKED; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; import java.util.Set; import java.util.TreeSet; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; import org.eclipse.egit.core.internal.indexdiff.IndexDiffData; import org.eclipse.egit.ui.Activator; import org.eclipse.egit.ui.internal.UIText; +import org.eclipse.egit.ui.internal.staging.StagingView.Presentation; import org.eclipse.egit.ui.internal.staging.StagingView.StagingViewUpdate; -import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.submodule.SubmoduleWalk; +import org.eclipse.ui.model.WorkbenchContentProvider; /** - * ContentProvider for staged and unstaged table nodes + * ContentProvider for staged and unstaged tree nodes */ -public class StagingViewContentProvider implements - IStructuredContentProvider { +public class StagingViewContentProvider extends WorkbenchContentProvider { + /** All files for the section (staged or unstaged). */ private StagingEntry[] content = new StagingEntry[0]; - private boolean isWorkspace; - StagingViewContentProvider(boolean workspace) { - this.isWorkspace = workspace; + /** Folders for the "Tree" presentation. */ + private StagingFolderEntry[] treeFolders; + + /** Folders for the "Compact Tree" presentation. */ + private StagingFolderEntry[] compactTreeFolders; + + private StagingView stagingView; + private boolean unstagedSection; + + private Repository repository; + + StagingViewContentProvider(StagingView stagingView, boolean unstagedSection) { + this.stagingView = stagingView; + this.unstagedSection = unstagedSection; + } + + public Object getParent(Object element) { + if (element instanceof StagingFolderEntry) + return ((StagingFolderEntry) element).getParent(); + if (element instanceof StagingEntry) + return ((StagingEntry) element).getParent(); + return null; + } + + public boolean hasChildren(Object element) { + return !(element instanceof StagingEntry); } public Object[] getElements(Object inputElement) { - return content; + return getChildren(inputElement); } - StagingEntry[] getStagingEntries() { - return content; + public Object[] getChildren(Object parentElement) { + if (repository == null) + return new Object[0]; + if (parentElement instanceof StagingEntry) + return new Object[0]; + if (parentElement instanceof StagingFolderEntry) { + return getFolderChildren((StagingFolderEntry) parentElement); + } else { + if (stagingView.getPresentation() == Presentation.LIST) + return content; + else { + StagingFolderEntry[] allFolders = getStagingFolderEntries(); + List<Object> roots = new ArrayList<Object>(); + for (StagingFolderEntry folder : allFolders) + if (folder.getParentPath().segmentCount() == 0) + roots.add(folder); + for (StagingEntry entry : content) + if (!entry.getPath().contains("/")) //$NON-NLS-1$ + roots.add(entry); + return roots.toArray(new Object[roots.size()]); + } + } + } + + private Object[] getFolderChildren(StagingFolderEntry parent) { + IPath parentPath = parent.getPath(); + List<Object> children = new ArrayList<Object>(); + for (StagingFolderEntry folder : getStagingFolderEntries()) { + if (folder.getParentPath().equals(parentPath)) { + folder.setParent(parent); + children.add(folder); + } + } + for (StagingEntry file : content) { + if (file.getParentPath().equals(parentPath)) { + file.setParent(parent); + children.add(file); + } + } + return children.toArray(new Object[children.size()]); + } + + StagingFolderEntry[] getStagingFolderEntries() { + Presentation presentation = stagingView.getPresentation(); + switch (presentation) { + case COMPACT_TREE: + return getCompactTreeFolders(); + case TREE: + return getTreeFolders(); + default: + return new StagingFolderEntry[0]; + } + } + + private StagingFolderEntry[] getCompactTreeFolders() { + if (compactTreeFolders == null) + compactTreeFolders = calculateTreeFolders(true); + return compactTreeFolders; + } + + private StagingFolderEntry[] getTreeFolders() { + if (treeFolders == null) + treeFolders = calculateTreeFolders(false); + return treeFolders; + } + + private StagingFolderEntry[] calculateTreeFolders(boolean compact) { + if (content == null || content.length == 0) + return new StagingFolderEntry[0]; + + Set<IPath> folderPaths = new HashSet<IPath>(); + Map<IPath, String> childSegments = new HashMap<IPath, String>(); + + for (StagingEntry file : content) { + IPath folderPath = file.getParentPath(); + if (folderPath.segmentCount() == 0) + // No folders need to be created + continue; + folderPaths.add(folderPath); + for (IPath p = folderPath; p.segmentCount() != 1; p = p + .removeLastSegments(1)) { + IPath parent = p.removeLastSegments(1); + if (!compact) { + folderPaths.add(parent); + } else { + String childSegment = p.lastSegment(); + String knownChildSegment = childSegments.get(parent); + if (knownChildSegment == null) { + childSegments.put(parent, childSegment); + } else if (!childSegment.equals(knownChildSegment)) { + // The parent has more than 1 direct child folder -> we + // need to make a node for it. + folderPaths.add(parent); + } + } + } + } + + IPath workingDirectory = new Path(repository.getWorkTree() + .getAbsolutePath()); + + List<StagingFolderEntry> folderEntries = new ArrayList<StagingFolderEntry>(); + for (IPath folderPath : folderPaths) { + IPath parent = folderPath.removeLastSegments(1); + // Find first existing parent node, but stop at root + while (parent.segmentCount() != 0 && !folderPaths.contains(parent)) + parent = parent.removeLastSegments(1); + if (parent.segmentCount() == 0) { + // Parent is root + StagingFolderEntry folderEntry = new StagingFolderEntry( + workingDirectory, folderPath, folderPath); + folderEntries.add(folderEntry); + } else { + // Parent is existing node + IPath nodePath = folderPath.makeRelativeTo(parent); + StagingFolderEntry folderEntry = new StagingFolderEntry( + workingDirectory, folderPath, nodePath); + folderEntries.add(folderEntry); + } + } + + Collections.sort(folderEntries, FolderComparator.INSTANCE); + return folderEntries.toArray(new StagingFolderEntry[folderEntries + .size()]); + } + + int getShownCount() { + String filterString = getFilterString(); + if (filterString.length() == 0) { + return getCount(); + } else { + int shownCount = 0; + for (StagingEntry entry : content) { + if (isInFilter(entry)) + shownCount++; + } + return shownCount; + } + } + + List<StagingEntry> getStagingEntriesFiltered(StagingFolderEntry folder) { + List<StagingEntry> stagingEntries = new ArrayList<StagingEntry>(); + for (StagingEntry stagingEntry : content) { + if (folder.getLocation().isPrefixOf(stagingEntry.getLocation())) { + if (isInFilter(stagingEntry)) + stagingEntries.add(stagingEntry); + } + } + return stagingEntries; + } + + boolean isInFilter(StagingEntry stagingEntry) { + String filterString = getFilterString(); + return filterString.length() == 0 + || stagingEntry.getPath().toUpperCase() + .contains(filterString.toUpperCase()); + } + + private String getFilterString() { + return stagingView.getFilterString(); + } + + boolean hasVisibleChildren(StagingFolderEntry folder) { + if (getFilterString().length() == 0) + return true; + else + return !getStagingEntriesFiltered(folder).isEmpty(); } - int getStagingEntryCount() { - return content.length; + StagingEntry[] getStagingEntries() { + return content; } - public void inputChanged(Viewer viewer, Object oldInput, - Object newInput) { + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { if (!(newInput instanceof StagingViewUpdate)) return; @@ -66,9 +262,18 @@ public class StagingViewContentProvider implements if (update.repository == null || update.indexDiff == null) { content = new StagingEntry[0]; + treeFolders = new StagingFolderEntry[0]; + compactTreeFolders = new StagingFolderEntry[0]; return; } + if (update.repository != repository) { + treeFolders = null; + compactTreeFolders = null; + } + + repository = update.repository; + Set<StagingEntry> nodes = new TreeSet<StagingEntry>( new Comparator<StagingEntry>() { public int compare(StagingEntry o1, StagingEntry o2) { @@ -86,8 +291,7 @@ public class StagingViewContentProvider implements } final IndexDiffData indexDiff = update.indexDiff; - final Repository repository = update.repository; - if (isWorkspace) { + if (unstagedSection) { for (String file : indexDiff.getMissing()) if (indexDiff.getChanged().contains(file)) nodes.add(new StagingEntry(repository, MISSING_AND_CHANGED, @@ -123,9 +327,31 @@ public class StagingViewContentProvider implements } content = nodes.toArray(new StagingEntry[nodes.size()]); + + treeFolders = null; + compactTreeFolders = null; } public void dispose() { // nothing to dispose } -} + + /** + * @return StagingEntry count + */ + public int getCount() { + if (content == null) + return 0; + else + return content.length; + } + + private static class FolderComparator implements + Comparator<StagingFolderEntry> { + public static FolderComparator INSTANCE = new FolderComparator(); + public int compare(StagingFolderEntry o1, StagingFolderEntry o2) { + return o1.getPath().toString().compareTo(o2.getPath().toString()); + } + } + +}
\ No newline at end of file diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/StagingViewLabelProvider.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/StagingViewLabelProvider.java index 92f944b9e4..ede63ee654 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/StagingViewLabelProvider.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/StagingViewLabelProvider.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2011, Bernard Leach <leachbj@bouncycastle.org> + * Copyright (C) 2011, 2013 Bernard Leach <leachbj@bouncycastle.org> and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 @@ -14,25 +14,27 @@ import org.eclipse.egit.ui.Activator; import org.eclipse.egit.ui.internal.UIIcons; import org.eclipse.egit.ui.internal.decorators.DecorationResult; import org.eclipse.egit.ui.internal.decorators.GitLightweightDecorator.DecorationHelper; +import org.eclipse.egit.ui.internal.staging.StagingView.Presentation; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.resource.LocalResourceManager; import org.eclipse.jface.resource.ResourceManager; -import org.eclipse.jface.viewers.BaseLabelProvider; import org.eclipse.jface.viewers.DecorationOverlayIcon; -import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider; import org.eclipse.jface.viewers.IDecoration; -import org.eclipse.jface.viewers.ITableLabelProvider; +import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.StyledString; import org.eclipse.swt.graphics.Image; import org.eclipse.ui.ISharedImages; import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.model.WorkbenchLabelProvider; /** * Label provider for {@link StagingEntry} objects */ -public class StagingViewLabelProvider extends BaseLabelProvider implements - ITableLabelProvider, IStyledLabelProvider { +public class StagingViewLabelProvider extends LabelProvider { + private StagingView stagingView; + + private WorkbenchLabelProvider workbenchLabelProvider = new WorkbenchLabelProvider(); private Image DEFAULT = PlatformUI.getWorkbench().getSharedImages() .getImage(ISharedImages.IMG_OBJ_FILE); @@ -49,6 +51,14 @@ public class StagingViewLabelProvider extends BaseLabelProvider implements private boolean fileNameMode = false; /** + * @param stagingView + */ + public StagingViewLabelProvider(StagingView stagingView) { + super(); + this.stagingView = stagingView; + } + + /** * Set file name mode to be enabled or disabled. This mode displays the * names of the file first followed by the path to the folder that the file * is in. @@ -61,18 +71,6 @@ public class StagingViewLabelProvider extends BaseLabelProvider implements return this; } - public Image getColumnImage(Object element, int columnIndex) { - if (columnIndex == 0) - return getImage(element); - return null; - } - - public String getColumnText(Object element, int columnIndex) { - if (columnIndex == 0) - return getStyledText(element).toString(); - return ""; //$NON-NLS-1$ - } - @Override public void dispose() { SUBMODULE.dispose(); @@ -100,45 +98,66 @@ public class StagingViewLabelProvider extends BaseLabelProvider implements return (Image) this.resourceManager.get(decorated); } - public StyledString getStyledText(Object element) { - final StagingEntry c = (StagingEntry) element; - final DecorationResult decoration = new DecorationResult(); + public Image getImage(Object element) { + + if (element instanceof StagingFolderEntry) { + StagingFolderEntry c = (StagingFolderEntry) element; + if (c.getContainer() == null) { + return PlatformUI.getWorkbench().getSharedImages() + .getImage(ISharedImages.IMG_OBJ_FOLDER); + } + return workbenchLabelProvider + .getImage(((StagingFolderEntry) element).getContainer()); + } + + StagingEntry c = (StagingEntry) element; + DecorationResult decoration = new DecorationResult(); decorationHelper.decorate(decoration, c); + return getDecoratedImage(getEditorImage(c), decoration.getOverlay()); + } + + @Override + public String getText(Object element) { + if (element instanceof StagingFolderEntry) { + StagingFolderEntry stagingFolderEntry = (StagingFolderEntry) element; + return stagingFolderEntry.getNodePath().toString(); + } + + StagingEntry stagingEntry = (StagingEntry) element; + final DecorationResult decoration = new DecorationResult(); + decorationHelper.decorate(decoration, stagingEntry); final StyledString styled = new StyledString(); final String prefix = decoration.getPrefix(); final String suffix = decoration.getSuffix(); if (prefix != null) styled.append(prefix, StyledString.DECORATIONS_STYLER); - if (fileNameMode) { - IPath parsed = Path.fromOSString(c.getPath()); - if (parsed.segmentCount() > 1) { - styled.append(parsed.lastSegment()); - if (suffix != null) - styled.append(suffix, StyledString.DECORATIONS_STYLER); - styled.append(' '); - styled.append('-', StyledString.QUALIFIER_STYLER); - styled.append(' '); - styled.append(parsed.removeLastSegments(1).toString(), - StyledString.QUALIFIER_STYLER); + if (stagingView.getPresentation() == Presentation.LIST) { + if (fileNameMode) { + IPath parsed = Path.fromOSString(stagingEntry.getPath()); + if (parsed.segmentCount() > 1) { + styled.append(parsed.lastSegment()); + if (suffix != null) + styled.append(suffix, StyledString.DECORATIONS_STYLER); + styled.append(' '); + styled.append('-', StyledString.QUALIFIER_STYLER); + styled.append(' '); + styled.append(parsed.removeLastSegments(1).toString(), + StyledString.QUALIFIER_STYLER); + } else { + styled.append(stagingEntry.getPath()); + if (suffix != null) + styled.append(suffix, StyledString.DECORATIONS_STYLER); + } } else { - styled.append(c.getPath()); + styled.append(stagingEntry.getPath()); if (suffix != null) styled.append(suffix, StyledString.DECORATIONS_STYLER); } } else { - styled.append(c.getPath()); - if (suffix != null) - styled.append(suffix, StyledString.DECORATIONS_STYLER); + styled.append(stagingEntry.getName()); } - - return styled; + return styled.toString(); } - public Image getImage(Object element) { - final StagingEntry c = (StagingEntry) element; - final DecorationResult decoration = new DecorationResult(); - decorationHelper.decorate(decoration, c); - return getDecoratedImage(getEditorImage(c), decoration.getOverlay()); - } } diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/uitext.properties b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/uitext.properties index 4fd0849b54..06ec7aeca4 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/uitext.properties +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/uitext.properties @@ -1630,6 +1630,10 @@ StagingView_UnstageItemMenuLabel=Remove from Git Index StagingView_StageItemMenuLabel=Add to Git Index StagingView_IgnoreItemMenuLabel=Ignore StagingView_DeleteItemMenuLabel=Delete +StagingView_Presentation=Presentation +StagingView_List=List +StagingView_Tree=Tree +StagingView_CompactTree=Compact Tree StagingView_Find=Filter files StagingViewContentProvider_SubmoduleError=Unhandled exception while analyzing submodules StashApplyCommand_applyFailed=Applying stashed commit ''{0}'' failed due to ''{1}'' |