Redo "Use ViewerComparator instead of deprecated ViewerSorter"
[egit/eclipse.git] / org.eclipse.egit.ui / src / org / eclipse / egit / ui / internal / fetch / FetchResultTable.java
blob2f34f2e2c3291c3890073aaf1fcfc5ef18aff97b
1 /*******************************************************************************
2 * Copyright (c) 2009, 2013 Shawn O. Pearce and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *******************************************************************************/
8 package org.eclipse.egit.ui.internal.fetch;
10 import java.io.IOException;
11 import java.text.MessageFormat;
12 import java.util.ArrayList;
13 import java.util.HashMap;
14 import java.util.List;
15 import java.util.Map;
17 import org.eclipse.egit.ui.Activator;
18 import org.eclipse.egit.ui.UIUtils;
19 import org.eclipse.egit.ui.internal.DecorationOverlayDescriptor;
20 import org.eclipse.egit.ui.internal.UIIcons;
21 import org.eclipse.egit.ui.internal.UIText;
22 import org.eclipse.egit.ui.internal.WorkbenchStyledLabelProvider;
23 import org.eclipse.egit.ui.internal.commit.CommitEditor;
24 import org.eclipse.egit.ui.internal.commit.RepositoryCommit;
25 import org.eclipse.egit.ui.internal.history.FileDiff;
26 import org.eclipse.jface.layout.GridDataFactory;
27 import org.eclipse.jface.layout.GridLayoutFactory;
28 import org.eclipse.jface.resource.ImageDescriptor;
29 import org.eclipse.jface.util.OpenStrategy;
30 import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
31 import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider;
32 import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider;
33 import org.eclipse.jface.viewers.IDecoration;
34 import org.eclipse.jface.viewers.ISelection;
35 import org.eclipse.jface.viewers.IStructuredSelection;
36 import org.eclipse.jface.viewers.StyledString;
37 import org.eclipse.jface.viewers.TreeViewer;
38 import org.eclipse.jface.viewers.Viewer;
39 import org.eclipse.jface.viewers.ViewerComparator;
40 import org.eclipse.jgit.lib.Constants;
41 import org.eclipse.jgit.lib.ObjectId;
42 import org.eclipse.jgit.lib.ObjectReader;
43 import org.eclipse.jgit.lib.Repository;
44 import org.eclipse.jgit.notes.NoteMap;
45 import org.eclipse.jgit.revwalk.RevCommit;
46 import org.eclipse.jgit.revwalk.RevWalk;
47 import org.eclipse.jgit.transport.FetchResult;
48 import org.eclipse.jgit.transport.TrackingRefUpdate;
49 import org.eclipse.swt.SWT;
50 import org.eclipse.swt.events.DisposeEvent;
51 import org.eclipse.swt.events.DisposeListener;
52 import org.eclipse.swt.widgets.Composite;
53 import org.eclipse.swt.widgets.Control;
54 import org.eclipse.swt.widgets.ToolBar;
55 import org.eclipse.swt.widgets.Tree;
56 import org.eclipse.ui.ISharedImages;
57 import org.eclipse.ui.OpenAndLinkWithEditorHelper;
58 import org.eclipse.ui.PlatformUI;
59 import org.eclipse.ui.model.WorkbenchAdapter;
60 import org.eclipse.ui.model.WorkbenchContentProvider;
62 /**
63 * Component displaying table with results of fetch operation.
65 class FetchResultTable {
67 private class FetchResultAdapter extends WorkbenchAdapter {
69 private final TrackingRefUpdate update;
71 private Object[] children;
73 public FetchResultAdapter(TrackingRefUpdate update) {
74 this.update = update;
77 @Override
78 public String getLabel(Object object) {
79 return getStyledText(object).getString();
82 @Override
83 public ImageDescriptor getImageDescriptor(Object object) {
84 switch (update.getResult()) {
85 case IO_FAILURE:
86 case LOCK_FAILURE:
87 case REJECTED_CURRENT_BRANCH:
88 case REJECTED:
89 return PlatformUI.getWorkbench().getSharedImages()
90 .getImageDescriptor(ISharedImages.IMG_OBJS_ERROR_TSK);
91 case FORCED:
92 if (isPruned()) {
93 ImageDescriptor icon = UIIcons.BRANCH;
94 if (update.getLocalName().startsWith(Constants.R_TAGS))
95 icon = UIIcons.TAG;
96 if (update.getLocalName().startsWith(Constants.R_NOTES))
97 icon = UIIcons.NOTE;
98 return new DecorationOverlayDescriptor(icon,
99 UIIcons.OVR_STAGED_REMOVE, IDecoration.TOP_RIGHT);
101 // else
102 //$FALL-THROUGH$
103 case RENAMED:
104 case FAST_FORWARD:
105 if (update.getRemoteName().startsWith(Constants.R_HEADS))
106 return UIIcons.BRANCH;
107 if (update.getLocalName().startsWith(Constants.R_TAGS))
108 return UIIcons.TAG;
109 if (update.getLocalName().startsWith(Constants.R_NOTES))
110 return UIIcons.NOTE;
111 break;
112 case NEW:
113 if (update.getRemoteName().startsWith(Constants.R_HEADS))
114 return UIIcons.CREATE_BRANCH;
115 if (update.getLocalName().startsWith(Constants.R_TAGS))
116 return UIIcons.CREATE_TAG;
117 if (update.getLocalName().startsWith(Constants.R_NOTES))
118 return UIIcons.NOTE;
119 break;
120 default:
121 return super.getImageDescriptor(object);
123 return super.getImageDescriptor(object);
126 private void addCommits(StyledString styled, String separator) {
127 styled.append('[', StyledString.DECORATIONS_STYLER);
128 styled.append(safeAbbreviate(update.getNewObjectId()),
129 StyledString.DECORATIONS_STYLER);
130 styled.append(separator, StyledString.DECORATIONS_STYLER);
131 styled.append(safeAbbreviate(update.getOldObjectId()),
132 StyledString.DECORATIONS_STYLER);
133 styled.append(']', StyledString.DECORATIONS_STYLER);
135 styled.append(MessageFormat.format(
136 UIText.FetchResultTable_counterCommits,
137 Integer.valueOf(getChildren(this).length)),
138 StyledString.COUNTER_STYLER);
141 @Override
142 public Object[] getChildren(Object object) {
143 if (children != null)
144 return children;
146 switch (update.getResult()) {
147 case FORCED:
148 if (isPruned())
149 return NO_CHILDREN;
150 // else
151 //$FALL-THROUGH$
152 case FAST_FORWARD:
153 try (RevWalk walk = new RevWalk(reader)) {
154 walk.setRetainBody(true);
155 walk.markStart(walk.parseCommit(update.getNewObjectId()));
156 walk.markUninteresting(walk.parseCommit(update
157 .getOldObjectId()));
158 List<RepositoryCommit> commits = new ArrayList<>();
159 for (RevCommit commit : walk)
160 commits.add(new RepositoryCommit(repo, commit));
161 children = commits.toArray();
162 break;
163 } catch (IOException e) {
164 Activator.logError(
165 "Error parsing commits from fetch result", e); //$NON-NLS-1$
167 //$FALL-THROUGH$
168 default:
169 children = super.getChildren(object);
171 return children;
175 * Shorten ref name
177 * @param ref
178 * @return shortened ref name
180 protected String shortenRef(final String ref) {
181 return NoteMap.shortenRefName(Repository.shortenRefName(ref));
184 @Override
185 public StyledString getStyledText(Object object) {
186 StyledString styled = new StyledString();
187 final String remote = update.getRemoteName();
188 final String local = update.getLocalName();
189 styled.append(shortenRef(remote));
190 styled.append(" : ", StyledString.QUALIFIER_STYLER); //$NON-NLS-1$
191 styled.append(shortenRef(local), StyledString.QUALIFIER_STYLER);
192 styled.append(' ');
193 switch (update.getResult()) {
194 case LOCK_FAILURE:
195 styled.append(UIText.FetchResultTable_statusLockFailure,
196 StyledString.DECORATIONS_STYLER);
197 break;
198 case IO_FAILURE:
199 styled.append(UIText.FetchResultTable_statusIOError,
200 StyledString.DECORATIONS_STYLER);
201 break;
202 case NEW:
203 if (remote.startsWith(Constants.R_HEADS))
204 styled.append(UIText.FetchResultTable_statusNewBranch,
205 StyledString.DECORATIONS_STYLER);
206 else if (local.startsWith(Constants.R_TAGS))
207 styled.append(UIText.FetchResultTable_statusNewTag,
208 StyledString.DECORATIONS_STYLER);
209 else
210 styled.append(UIText.FetchResultTable_statusNew,
211 StyledString.DECORATIONS_STYLER);
212 break;
213 case FORCED:
214 if (isPruned())
215 styled.append(UIText.FetchResultTable_statusPruned,
216 StyledString.DECORATIONS_STYLER);
217 else
218 addCommits(styled, "..."); //$NON-NLS-1$
219 break;
220 case FAST_FORWARD:
221 addCommits(styled, ".."); //$NON-NLS-1$
222 break;
223 case REJECTED:
224 styled.append(UIText.FetchResultTable_statusRejected,
225 StyledString.DECORATIONS_STYLER);
226 break;
227 case NO_CHANGE:
228 styled.append(UIText.FetchResultTable_statusUpToDate,
229 StyledString.DECORATIONS_STYLER);
230 break;
231 default:
232 break;
234 return styled;
237 private boolean isPruned() {
238 return update.getNewObjectId().equals(ObjectId.zeroId());
242 private final Composite treePanel;
244 private final TreeViewer treeViewer;
246 private Repository repo;
248 private ObjectReader reader;
250 private Map<ObjectId, String> abbrevations;
252 FetchResultTable(final Composite parent) {
253 treePanel = new Composite(parent, SWT.NONE);
254 GridLayoutFactory.swtDefaults().numColumns(2).applyTo(treePanel);
255 treeViewer = new TreeViewer(treePanel);
256 treeViewer.setAutoExpandLevel(2);
258 addToolbar(treePanel);
260 final IStyledLabelProvider styleProvider = new WorkbenchStyledLabelProvider() {
262 @Override
263 public StyledString getStyledText(Object element) {
264 // TODO Replace with use of IWorkbenchAdapter3 when is no longer
265 // supported
266 if (element instanceof FetchResultAdapter)
267 return ((FetchResultAdapter) element)
268 .getStyledText(element);
269 if (element instanceof RepositoryCommit)
270 return ((RepositoryCommit) element).getStyledText(element);
272 return super.getStyledText(element);
275 treeViewer.setLabelProvider(new DelegatingStyledCellLabelProvider(
276 styleProvider) {
278 @Override
279 public String getToolTipText(final Object element) {
280 if (element instanceof FetchResultAdapter) {
281 switch (((FetchResultAdapter) element).update.getResult()) {
282 case FAST_FORWARD:
283 return UIText.FetchResultTable_statusDetailFastForward;
284 case FORCED:
285 case REJECTED:
286 return UIText.FetchResultTable_statusDetailNonFastForward;
287 case IO_FAILURE:
288 return UIText.FetchResultTable_statusDetailIOError;
289 case LOCK_FAILURE:
290 return UIText.FetchResultTable_statusDetailCouldntLock;
291 default:
292 return super.getToolTipText(element);
295 return super.getToolTipText(element);
299 treeViewer.setComparator(new ViewerComparator() {
301 @Override
302 public int compare(Viewer viewer, Object e1, Object e2) {
303 if (e1 instanceof FetchResultAdapter
304 && e2 instanceof FetchResultAdapter) {
305 FetchResultAdapter f1 = (FetchResultAdapter) e1;
306 FetchResultAdapter f2 = (FetchResultAdapter) e2;
307 if (f1.getChildren(f1).length > 0
308 && f2.getChildren(f2).length == 0)
309 return 1;
310 if (f1.getChildren(f1).length == 0
311 && f2.getChildren(f2).length > 0)
312 return -1;
314 return f1.getLabel(f1).compareToIgnoreCase(f2.getLabel(f2));
317 // Leave commits order alone
318 if (e1 instanceof RepositoryCommit
319 && e2 instanceof RepositoryCommit)
320 return 0;
322 if (e1 instanceof FileDiff && e2 instanceof FileDiff) {
323 FileDiff f1 = (FileDiff) e1;
324 FileDiff f2 = (FileDiff) e2;
325 return f1.getPath().compareTo(f2.getPath());
328 return super.compare(viewer, e1, e2);
332 ColumnViewerToolTipSupport.enableFor(treeViewer);
333 final Tree tree = treeViewer.getTree();
334 GridDataFactory.fillDefaults().grab(true, true).applyTo(tree);
336 treePanel.addDisposeListener(new DisposeListener() {
337 @Override
338 public void widgetDisposed(DisposeEvent e) {
339 if (reader != null)
340 reader.close();
344 treeViewer.setContentProvider(new WorkbenchContentProvider() {
346 @Override
347 public Object[] getElements(Object inputElement) {
348 if (inputElement == null)
349 return new FetchResultAdapter[0];
351 final FetchResult result = (FetchResult) inputElement;
352 TrackingRefUpdate[] updates = result.getTrackingRefUpdates()
353 .toArray(new TrackingRefUpdate[0]);
354 FetchResultAdapter[] elements = new FetchResultAdapter[updates.length];
355 for (int i = 0; i < elements.length; i++)
356 elements[i] = new FetchResultAdapter(updates[i]);
357 return elements;
360 @Override
361 public Object[] getChildren(Object element) {
362 if (element instanceof RepositoryCommit) {
363 return ((RepositoryCommit) element).getDiffs();
365 return super.getChildren(element);
368 @Override
369 public boolean hasChildren(Object element) {
370 if (element instanceof RepositoryCommit) {
371 // always return true for commits to avoid commit diff
372 // calculation in UI thread, see bug 458839
373 return true;
375 return super.hasChildren(element);
379 new OpenAndLinkWithEditorHelper(treeViewer) {
380 @Override
381 protected void linkToEditor(ISelection selection) {
382 // Not supported
385 @Override
386 protected void open(ISelection selection, boolean activate) {
387 handleOpen(selection, OpenStrategy.activateOnOpen());
389 @Override
390 protected void activate(ISelection selection) {
391 handleOpen(selection, true);
393 private void handleOpen(ISelection selection, boolean activateOnOpen) {
394 if (selection instanceof IStructuredSelection)
395 for (Object element : ((IStructuredSelection) selection)
396 .toArray())
397 if (element instanceof RepositoryCommit)
398 CommitEditor.openQuiet((RepositoryCommit) element, activateOnOpen);
403 private void addToolbar(Composite parent) {
404 ToolBar toolbar = new ToolBar(parent, SWT.VERTICAL);
405 GridDataFactory.fillDefaults().grab(false, true).applyTo(toolbar);
406 UIUtils.addExpansionItems(toolbar, treeViewer);
409 void setData(final Repository db, final FetchResult fetchResult) {
410 treeViewer.setInput(null);
411 repo = db;
412 reader = db.newObjectReader();
413 abbrevations = new HashMap<>();
414 treeViewer.setInput(fetchResult);
417 private String safeAbbreviate(ObjectId id) {
418 String abbrev = abbrevations.get(id);
419 if (abbrev == null) {
420 try {
421 abbrev = reader.abbreviate(id).name();
422 } catch (IOException cannotAbbreviate) {
423 abbrev = id.name();
425 abbrevations.put(id, abbrev);
427 return abbrev;
430 Control getControl() {
431 return treePanel;