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
;
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
;
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
) {
78 public String
getLabel(Object object
) {
79 return getStyledText(object
).getString();
83 public ImageDescriptor
getImageDescriptor(Object object
) {
84 switch (update
.getResult()) {
87 case REJECTED_CURRENT_BRANCH
:
89 return PlatformUI
.getWorkbench().getSharedImages()
90 .getImageDescriptor(ISharedImages
.IMG_OBJS_ERROR_TSK
);
93 ImageDescriptor icon
= UIIcons
.BRANCH
;
94 if (update
.getLocalName().startsWith(Constants
.R_TAGS
))
96 if (update
.getLocalName().startsWith(Constants
.R_NOTES
))
98 return new DecorationOverlayDescriptor(icon
,
99 UIIcons
.OVR_STAGED_REMOVE
, IDecoration
.TOP_RIGHT
);
105 if (update
.getRemoteName().startsWith(Constants
.R_HEADS
))
106 return UIIcons
.BRANCH
;
107 if (update
.getLocalName().startsWith(Constants
.R_TAGS
))
109 if (update
.getLocalName().startsWith(Constants
.R_NOTES
))
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
))
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
);
142 public Object
[] getChildren(Object object
) {
143 if (children
!= null)
146 switch (update
.getResult()) {
153 try (RevWalk walk
= new RevWalk(reader
)) {
154 walk
.setRetainBody(true);
155 walk
.markStart(walk
.parseCommit(update
.getNewObjectId()));
156 walk
.markUninteresting(walk
.parseCommit(update
158 List
<RepositoryCommit
> commits
= new ArrayList
<>();
159 for (RevCommit commit
: walk
)
160 commits
.add(new RepositoryCommit(repo
, commit
));
161 children
= commits
.toArray();
163 } catch (IOException e
) {
165 "Error parsing commits from fetch result", e
); //$NON-NLS-1$
169 children
= super.getChildren(object
);
178 * @return shortened ref name
180 protected String
shortenRef(final String ref
) {
181 return NoteMap
.shortenRefName(Repository
.shortenRefName(ref
));
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
);
193 switch (update
.getResult()) {
195 styled
.append(UIText
.FetchResultTable_statusLockFailure
,
196 StyledString
.DECORATIONS_STYLER
);
199 styled
.append(UIText
.FetchResultTable_statusIOError
,
200 StyledString
.DECORATIONS_STYLER
);
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
);
210 styled
.append(UIText
.FetchResultTable_statusNew
,
211 StyledString
.DECORATIONS_STYLER
);
215 styled
.append(UIText
.FetchResultTable_statusPruned
,
216 StyledString
.DECORATIONS_STYLER
);
218 addCommits(styled
, "..."); //$NON-NLS-1$
221 addCommits(styled
, ".."); //$NON-NLS-1$
224 styled
.append(UIText
.FetchResultTable_statusRejected
,
225 StyledString
.DECORATIONS_STYLER
);
228 styled
.append(UIText
.FetchResultTable_statusUpToDate
,
229 StyledString
.DECORATIONS_STYLER
);
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() {
263 public StyledString
getStyledText(Object element
) {
264 // TODO Replace with use of IWorkbenchAdapter3 when is no longer
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(
279 public String
getToolTipText(final Object element
) {
280 if (element
instanceof FetchResultAdapter
) {
281 switch (((FetchResultAdapter
) element
).update
.getResult()) {
283 return UIText
.FetchResultTable_statusDetailFastForward
;
286 return UIText
.FetchResultTable_statusDetailNonFastForward
;
288 return UIText
.FetchResultTable_statusDetailIOError
;
290 return UIText
.FetchResultTable_statusDetailCouldntLock
;
292 return super.getToolTipText(element
);
295 return super.getToolTipText(element
);
299 treeViewer
.setComparator(new ViewerComparator() {
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)
310 if (f1
.getChildren(f1
).length
== 0
311 && f2
.getChildren(f2
).length
> 0)
314 return f1
.getLabel(f1
).compareToIgnoreCase(f2
.getLabel(f2
));
317 // Leave commits order alone
318 if (e1
instanceof RepositoryCommit
319 && e2
instanceof RepositoryCommit
)
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() {
338 public void widgetDisposed(DisposeEvent e
) {
344 treeViewer
.setContentProvider(new WorkbenchContentProvider() {
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
]);
361 public Object
[] getChildren(Object element
) {
362 if (element
instanceof RepositoryCommit
) {
363 return ((RepositoryCommit
) element
).getDiffs();
365 return super.getChildren(element
);
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
375 return super.hasChildren(element
);
379 new OpenAndLinkWithEditorHelper(treeViewer
) {
381 protected void linkToEditor(ISelection selection
) {
386 protected void open(ISelection selection
, boolean activate
) {
387 handleOpen(selection
, OpenStrategy
.activateOnOpen());
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
)
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);
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) {
421 abbrev
= reader
.abbreviate(id
).name();
422 } catch (IOException cannotAbbreviate
) {
425 abbrevations
.put(id
, abbrev
);
430 Control
getControl() {