blob: bea459cc8da926a0fb060cbfd72151c08756f850 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013, 2015 Tasktop Technologies, Ericsson 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
*
* Contributors:
* Tasktop Technologies - initial API and implementation
* Sam Davis - improvements for bug 383592
*******************************************************************************/
package org.eclipse.mylyn.reviews.ui.spi.editor;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.layout.LayoutConstants;
import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider;
import org.eclipse.jface.viewers.IOpenListener;
import org.eclipse.jface.viewers.ITreeSelection;
import org.eclipse.jface.viewers.ITreeViewerListener;
import org.eclipse.jface.viewers.OpenEvent;
import org.eclipse.jface.viewers.TreeExpansionEvent;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.window.ToolTip;
import org.eclipse.mylyn.commons.ui.compatibility.CommonColors;
import org.eclipse.mylyn.commons.workbench.forms.ScalingHyperlink;
import org.eclipse.mylyn.internal.reviews.ui.providers.ReviewsLabelProvider;
import org.eclipse.mylyn.internal.tasks.ui.editors.EditorUtil;
import org.eclipse.mylyn.reviews.core.model.IComment;
import org.eclipse.mylyn.reviews.core.model.ICommit;
import org.eclipse.mylyn.reviews.core.model.IFileItem;
import org.eclipse.mylyn.reviews.core.model.ILocation;
import org.eclipse.mylyn.reviews.core.model.IRepository;
import org.eclipse.mylyn.reviews.core.model.IReview;
import org.eclipse.mylyn.reviews.core.model.IReviewItemSet;
import org.eclipse.mylyn.reviews.core.spi.remote.emf.RemoteEmfConsumer;
import org.eclipse.mylyn.reviews.core.spi.remote.emf.RemoteEmfObserver;
import org.eclipse.mylyn.reviews.internal.core.ReviewFileCommentsMapper;
import org.eclipse.mylyn.reviews.internal.core.TaskBuildStatusMapper;
import org.eclipse.mylyn.tasks.core.data.TaskAttribute;
import org.eclipse.mylyn.tasks.ui.editors.AbstractAttributeEditor;
import org.eclipse.mylyn.tasks.ui.editors.AbstractTaskEditorPage;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
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.TreeItem;
import org.eclipse.ui.forms.FormColors;
import org.eclipse.ui.forms.IFormColors;
import org.eclipse.ui.forms.events.ExpansionAdapter;
import org.eclipse.ui.forms.events.ExpansionEvent;
import org.eclipse.ui.forms.events.HyperlinkAdapter;
import org.eclipse.ui.forms.events.HyperlinkEvent;
import org.eclipse.ui.forms.widgets.ExpandableComposite;
import org.eclipse.ui.forms.widgets.Section;
/**
* @author Steffen Pingel
* @author Miles Parker
* @author Sam Davis
*/
@SuppressWarnings("restriction")
public class ReviewSetContentSection {
private static final int MAXIMUM_ITEMS_SHOWN = 30;
private final ReviewSetSection parentSection;
private final IReviewItemSet set;
private final Section section;
private TreeViewer viewer;
private final RemoteEmfObserver<IReviewItemSet, List<IFileItem>, String, Long> itemListObserver = new RemoteEmfObserver<IReviewItemSet, List<IFileItem>, String, Long>() {
@Override
public void updated(boolean modified) {
createItemSetTable();
updateItemSetTable();
updateMessage();
createButtons();
}
@Override
public void updating() {
updateMessage();
}
};
private final RemoteEmfConsumer<IReviewItemSet, List<IFileItem>, String, ?, ?, Long> itemSetConsumer;
private Composite treeContainer;
private Composite actionContainer;
private final RemoteEmfObserver<IRepository, IReview, String, Date> reviewObserver;
private final AbstractTaskEditorPage page;
public ReviewSetContentSection(ReviewSetSection parentSection, final IReviewItemSet set,
AbstractTaskEditorPage page) {
this.parentSection = parentSection;
this.set = set;
this.page = page;
int style = ExpandableComposite.TWISTIE | ExpandableComposite.CLIENT_INDENT
| ExpandableComposite.LEFT_TEXT_CLIENT_ALIGNMENT;
section = parentSection.getToolkit().createSection(parentSection.getComposite(), style);
GridDataFactory.fillDefaults().grab(true, false).applyTo(section);
section.setText(set.getName());
section.setTitleBarForeground(parentSection.getToolkit().getColors().getColor(IFormColors.TITLE));
parentSection.addTextClient(parentSection.getToolkit(), section, "", false); //$NON-NLS-1$
itemSetConsumer = getParentSection().getReviewEditorPage()
.getFactoryProvider()
.getReviewItemSetContentFactory()
.getConsumerForLocalKey(set, set.getId());
itemListObserver.setConsumer(itemSetConsumer);
final RemoteEmfConsumer<IRepository, IReview, String, ?, ?, Date> reviewConsumer = getParentSection()
.getReviewEditorPage().getFactoryProvider().getReviewFactory().getConsumerForModel(
set.getReview().getRepository(), set.getReview());
reviewObserver = new RemoteEmfObserver<IRepository, IReview, String, Date>() {
@Override
public void updated(boolean modified) {
if (reviewConsumer.getRemoteObject() != null && section.isExpanded() && modified) {
itemSetConsumer.retrieve(false);
updateMessage();
createButtons();
}
}
};
reviewConsumer.addObserver(reviewObserver);
section.addExpansionListener(new ExpansionAdapter() {
@Override
public void expansionStateChanged(ExpansionEvent e) {
if (e.getState()) {
if (set.getItems().isEmpty()) {
itemSetConsumer.retrieve(false);
}
updateMessage();
createButtons();
}
}
});
createMainSection();
createItemSetTable();
updateItemSetTable();
updateMessage();
}
public void updateMessage() {
if (section.isDisposed()) {
return;
}
String message;
if (itemListObserver.getConsumer().getStatus().isOK()) {
String time = DateFormat.getDateTimeInstance().format(set.getCreationDate());
int numComments = set.getAllComments().size();
if (numComments > 0) {
message = NLS.bind(Messages.ReviewSetContentSection_X_comma_Y_Comments, time, numComments);
} else {
message = time;
}
if (itemListObserver != null && itemListObserver.getConsumer().isRetrieving()) {
message += " " + Messages.Reviews_RetrievingContents; //$NON-NLS-1$
}
} else {
message = NLS.bind(Messages.Reviews_UpdateFailure_X,
itemListObserver.getConsumer().getStatus().getMessage());
}
AbstractReviewSection.appendMessage(getSection(), message);
}
void createMainSection() {
Composite composite = parentSection.getToolkit().createComposite(section);
GridLayoutFactory.fillDefaults().numColumns(2).spacing(60, LayoutConstants.getSpacing().y).applyTo(composite);
Composite leftColumn = parentSection.getToolkit().createComposite(composite);
GridLayoutFactory.fillDefaults().numColumns(2).applyTo(leftColumn);
Composite rightColumn = parentSection.getToolkit().createComposite(composite);
GridLayoutFactory.fillDefaults().numColumns(2).applyTo(rightColumn);
section.setClient(composite);
FormColors colors = parentSection.getToolkit().getColors();
createAuthorLabel(leftColumn, colors);
createCommitterLabel(leftColumn, colors);
createCommitLink(rightColumn, colors);
createRefLabel(rightColumn, colors);
createParentsLinks(composite, colors);
TaskAttribute buildAttribute = parentSection.getTaskData().getRoot().getAttribute("PATCH_SET-" + set.getId()); //$NON-NLS-1$
if (buildAttribute != null) {
Composite buildComposite = parentSection.getToolkit().createComposite(composite);
GridLayoutFactory.fillDefaults().numColumns(2).applyTo(buildComposite);
GridDataFactory.fillDefaults().span(2, 1).applyTo(buildComposite);
AbstractAttributeEditor editor = parentSection.getTaskEditorPage()
.getAttributeEditorFactory()
.createEditor(TaskBuildStatusMapper.BUILD_RESULT_TYPE, buildAttribute);
editor.createLabelControl(buildComposite, parentSection.getToolkit());
editor.createControl(buildComposite, parentSection.getToolkit());
getTaskEditorPage().getAttributeEditorToolkit().adapt(editor);
}
treeContainer = new Composite(composite, SWT.NONE);
treeContainer.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_DARK_GRAY));
GridDataFactory.fillDefaults().span(4, 1).grab(true, true).applyTo(treeContainer);
GridLayoutFactory.fillDefaults().numColumns(2).applyTo(treeContainer);
actionContainer = new Composite(composite, SWT.NONE);
GridDataFactory.fillDefaults().span(4, 1).grab(true, true).applyTo(actionContainer);
GridLayoutFactory.fillDefaults().numColumns(4).applyTo(actionContainer);
createButtons();
reflow();
}
private AbstractTaskEditorPage getTaskEditorPage() {
return page;
}
private void createAuthorLabel(Composite composite, FormColors colors) {
createTitleLabel(composite, colors, Messages.ReviewSetContentSection_Author, SWT.NONE);
Text authorText = new Text(composite, SWT.READ_ONLY);
if (set.getAddedBy() != null) {
authorText.setText(set.getAddedBy().getDisplayName());
} else {
authorText.setText(Messages.ReviewSetContentSection_Unspecified);
}
}
private void createCommitterLabel(Composite composite, FormColors colors) {
Label committerLabel = new Label(composite, SWT.NONE);
committerLabel.setForeground(colors.getColor(IFormColors.TITLE));
committerLabel.setText(Messages.ReviewSetContentSection_Committer);
Text committerText = new Text(composite, SWT.READ_ONLY);
if (set.getCommittedBy() != null) {
committerText.setText(set.getCommittedBy().getDisplayName());
} else {
committerText.setText(Messages.ReviewSetContentSection_Unspecified);
}
}
private void createCommitLink(Composite composite, FormColors colors) {
Label commitLabel = new Label(composite, SWT.NONE);
commitLabel.setForeground(colors.getColor(IFormColors.TITLE));
commitLabel.setText(Messages.ReviewSetContentSection_Commit);
ScalingHyperlink commitLink = new ScalingHyperlink(composite, SWT.READ_ONLY);
commitLink.setText(set.getRevision());
commitLink.setForeground(CommonColors.HYPERLINK_WIDGET);
commitLink.registerMouseTrackListener();
commitLink.addHyperlinkListener(new HyperlinkAdapter() {
@Override
public void linkActivated(HyperlinkEvent event) {
getParentSection().getUiFactoryProvider()
.getOpenCommitFactory(ReviewSetContentSection.this.getParentSection(), set)
.execute();
}
});
}
private void createRefLabel(Composite composite, FormColors colors) {
Label refLabel = new Label(composite, SWT.NONE);
refLabel.setForeground(colors.getColor(IFormColors.TITLE));
refLabel.setText(Messages.ReviewSetContentSection_Ref);
Text refText = new Text(composite, SWT.READ_ONLY);
refText.setText(set.getReference());
}
private void createParentsLinks(Composite composite, FormColors colors) {
if (set.getParentCommits().isEmpty()) {
return;// for Gerrit versions earlier than 2.8 we don't support getting the parents
}
Composite parentsComposite = new Composite(composite, SWT.NONE);
GridLayoutFactory.fillDefaults().numColumns(3).applyTo(parentsComposite);
GridDataFactory.fillDefaults().span(2, 1).applyTo(parentsComposite);
Label parentsLabel = new Label(parentsComposite, SWT.NONE);
parentsLabel.setForeground(colors.getColor(IFormColors.TITLE));
parentsLabel.setText(Messages.ReviewSetContentSection_Parents);
final List<String> parentCommitIds = new ArrayList<String>();
for (ICommit commit : set.getParentCommits()) {
parentCommitIds.add(commit.getId());
}
ScalingHyperlink parentOne = new ScalingHyperlink(parentsComposite, SWT.READ_ONLY);
GridDataFactory.fillDefaults().indent(13, 0).applyTo(parentOne);
if (parentCommitIds.size() > 0) {
addParentCommitHyperlink(parentOne, parentCommitIds.get(0));
}
if (parentCommitIds.size() == 2 && parentCommitIds.get(1) != null) {
ScalingHyperlink parentTwo = new ScalingHyperlink(parentsComposite, SWT.READ_ONLY);
addParentCommitHyperlink(parentTwo, parentCommitIds.get(1));
}
}
private void addParentCommitHyperlink(ScalingHyperlink commit, final String commitId) {
commit.setText(commitId);
commit.setForeground(CommonColors.HYPERLINK_WIDGET);
commit.registerMouseTrackListener();
commit.addHyperlinkListener(new HyperlinkAdapter() {
@Override
public void linkActivated(HyperlinkEvent event) {
getParentSection().getUiFactoryProvider()
.getOpenParentCommitFactory(ReviewSetContentSection.this.getParentSection(), set, commitId)
.execute();
}
});
}
private Label createTitleLabel(Composite composite, FormColors colors, String text, int style) {
Label titleLabel = new Label(composite, style);
titleLabel.setForeground(colors.getColor(IFormColors.TITLE));
titleLabel.setText(text);
return titleLabel;
}
public void createItemSetTable() {
if (viewer == null && !set.getItems().isEmpty()) {
boolean fixedViewerSize = set.getItems().size() > MAXIMUM_ITEMS_SHOWN;
int heightHint = fixedViewerSize ? 300 : SWT.DEFAULT;
int style = SWT.SINGLE | SWT.BORDER | SWT.VIRTUAL;
if (fixedViewerSize) {
style |= SWT.V_SCROLL;
} else {
style |= SWT.NO_SCROLL;
}
viewer = new TreeViewer(treeContainer, style);
GridDataFactory.fillDefaults()
.span(2, 1)
.grab(true, true)
.hint(500, heightHint)
.applyTo(viewer.getControl());
final ReviewSetContentProvider contentProvider = new ReviewSetContentProvider();
viewer.setContentProvider(contentProvider);
ColumnViewerToolTipSupport.enableFor(viewer, ToolTip.NO_RECREATE);
final DelegatingStyledCellLabelProvider styledLabelProvider = new DelegatingStyledCellLabelProvider(
new ReviewsLabelProvider.Tree(true)) {
@Override
public String getToolTipText(Object element) {
//For some reason tooltips are not delegated..
return ReviewsLabelProvider.ITEMS_COLUMN.getToolTipText(element);
};
};
viewer.setLabelProvider(styledLabelProvider);
viewer.addOpenListener(new IOpenListener() {
public void open(OpenEvent event) {
ITreeSelection selection = (ITreeSelection) event.getSelection();
TreePath[] paths = selection.getPaths();
for (TreePath path : paths) {
IFileItem file = null;
IComment comment = null;
for (int i = 0; i < path.getSegmentCount(); i++) {
Object o = path.getSegment(i);
if (o instanceof IFileItem) {
file = (IFileItem) o;
} else if (o instanceof ILocation) {
Object[] children = contentProvider.getChildren(o);
if (children.length > 0 && children[0] instanceof IComment) {
comment = (IComment) children[0];
}
} else if (o instanceof IComment) {
comment = (IComment) o;
}
}
if (file != null && comment != null) {
getParentSection().getUiFactoryProvider()
.getOpenFileToCommentFactory(ReviewSetContentSection.this.getParentSection(), set,
file, comment)
.execute();
} else if (file != null) {
getParentSection().getUiFactoryProvider()
.getOpenFileFactory(ReviewSetContentSection.this.getParentSection(), set, file)
.execute();
}
}
}
});
EditorUtil.addScrollListener(viewer.getTree());
viewer.setInput(set);
viewer.addTreeListener(new ITreeViewerListener() {
@Override
public void treeExpanded(TreeExpansionEvent event) {
reflowAsync();
}
@Override
public void treeCollapsed(TreeExpansionEvent event) {
reflowAsync();
}
});
}
}
private void updateItemSetTable() {
if (set.getItems().size() > 0 && hasViewer()) {
viewer.setInput(set);
TaskAttribute fileComments = ReviewSetContentSection.this.page.getModel()
.getTaskData()
.getRoot()
.getAttribute(ReviewFileCommentsMapper.FILE_ITEM_COMMENTS);
final Set<IComment> toHighlight = new HashSet<IComment>();
if (fileComments != null) {
List<IFileItem> files = itemSetConsumer.getModelObject();
for (IFileItem file : files) {
for (IComment comment : file.getAllComments()) {
if (fileComments.getAttribute(comment.getId()) == null) {
toHighlight.add(comment);
}
}
}
}
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
if (hasViewer()) {
viewer.refresh();
viewer.expandAll();
decorateItems(viewer.getTree().getItems(), toHighlight);
reflow();
}
}
});
}
}
private void decorateItems(TreeItem[] items, Set<IComment> toHighlight) {
for (TreeItem item : items) {
if (toHighlight.contains(item.getData())) {
item.setBackground(getTaskEditorPage().getAttributeEditorToolkit().getColorIncoming());
}
decorateItems(item.getItems(), toHighlight);
}
}
public void createButtons() {
if (!actionContainer.isDisposed()) {
for (Control oldActionControl : actionContainer.getChildren()) {
oldActionControl.dispose();
}
getParentSection().getUiFactoryProvider().createControls(getParentSection(), actionContainer,
getParentSection().getToolkit(), set);
actionContainer.layout();
reflow();
}
}
public Section getSection() {
return section;
}
public ReviewSetSection getParentSection() {
return parentSection;
}
private void reflowAsync() {
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
reflow();
}
});
}
private void reflow() {
getParentSection().getTaskEditorPage().reflow();
}
private boolean hasViewer() {
return viewer != null && !viewer.getControl().isDisposed();
}
public void dispose() {
itemListObserver.dispose();
reviewObserver.dispose();
section.dispose();
}
}