blob: a0932cd315463f7862ba8e565baf601e2cf59d01 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2008 Tasktop Technologies 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
* Jingwen Ou - comment grouping
*******************************************************************************/
package org.eclipse.mylyn.internal.tasks.ui.editors;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuCreator;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.mylyn.internal.provisional.commons.ui.CommonImages;
import org.eclipse.mylyn.internal.tasks.core.TaskComment;
import org.eclipse.mylyn.internal.tasks.ui.actions.CommentActionGroup;
import org.eclipse.mylyn.internal.tasks.ui.editors.CommentGroupStrategy.CommentGroup;
import org.eclipse.mylyn.internal.tasks.ui.util.SelectionProviderAdapter;
import org.eclipse.mylyn.tasks.core.IRepositoryPerson;
import org.eclipse.mylyn.tasks.core.ITaskComment;
import org.eclipse.mylyn.tasks.core.data.TaskAttribute;
import org.eclipse.mylyn.tasks.core.data.TaskDataModel;
import org.eclipse.mylyn.tasks.ui.TasksUiImages;
import org.eclipse.mylyn.tasks.ui.editors.AbstractAttributeEditor;
import org.eclipse.mylyn.tasks.ui.editors.AbstractTaskEditorPart;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.actions.ActionContext;
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.FormToolkit;
import org.eclipse.ui.forms.widgets.ImageHyperlink;
import org.eclipse.ui.forms.widgets.Section;
/**
* @author Robert Elves
* @author Steffen Pingel
* @author Jingwen Ou
*/
public class TaskEditorCommentPart extends AbstractTaskEditorPart {
private static final String ID_POPUP_MENU = "org.eclipse.mylyn.tasks.ui.editor.menu.comments"; //$NON-NLS-1$
private class CommentGroupViewer {
private final CommentGroup commentGroup;
private ArrayList<CommentViewer> commentViewers;
private Section groupSection;
private boolean renderedInSubSection;
public CommentGroupViewer(CommentGroup commentGroup) {
this.commentGroup = commentGroup;
}
private Composite createCommentViewers(Composite parent, FormToolkit toolkit) {
Composite composite = toolkit.createComposite(parent);
GridLayout contentLayout = new GridLayout();
contentLayout.marginHeight = 0;
contentLayout.marginWidth = 0;
composite.setLayout(contentLayout);
List<CommentViewer> viewers = getCommentViewers();
for (CommentViewer commentViewer : viewers) {
Control control = commentViewer.createControl(composite, toolkit);
GridDataFactory.fillDefaults().grab(true, false).applyTo(control);
}
return composite;
}
private Control createControl(final Composite parent, final FormToolkit toolkit) {
if (renderedInSubSection) {
return createSection(parent, toolkit);
} else {
return createCommentViewers(parent, toolkit);
}
}
private Section createSection(final Composite parent, final FormToolkit toolkit) {
int style = ExpandableComposite.TWISTIE | ExpandableComposite.SHORT_TITLE_BAR;
// if (/*commentGroup.hasIncoming() || */expandAllInProgress) {
// style |= ExpandableComposite.EXPANDED;
// }
groupSection = toolkit.createSection(parent, style);
groupSection.clientVerticalSpacing = 0;
if (commentGroup.hasIncoming()) {
groupSection.setBackground(getTaskEditorPage().getAttributeEditorToolkit().getColorIncoming());
}
groupSection.setForeground(toolkit.getColors().getColor(IFormColors.TITLE));
groupSection.setText(commentGroup.getGroupName() + Messages.TaskEditorCommentPart_0
+ commentGroup.getCommentAttributes().size() + Messages.TaskEditorCommentPart_1);
if (groupSection.isExpanded()) {
Composite composite = createCommentViewers(groupSection, toolkit);
groupSection.setClient(composite);
} else {
groupSection.addExpansionListener(new ExpansionAdapter() {
@Override
public void expansionStateChanged(ExpansionEvent e) {
if (commentGroup.hasIncoming()) {
if (e.getState()) {
groupSection.setBackground(null);
} else {
// only decorate background with incoming color when collapsed, otherwise
// there is too much decoration in the editor
groupSection.setBackground(getTaskEditorPage().getAttributeEditorToolkit()
.getColorIncoming());
}
}
if (groupSection.getClient() == null) {
try {
getTaskEditorPage().setReflow(false);
Composite composite = createCommentViewers(groupSection, toolkit);
groupSection.setClient(composite);
} finally {
getTaskEditorPage().setReflow(true);
}
getTaskEditorPage().reflow();
}
}
});
}
return groupSection;
}
public List<CommentViewer> getCommentViewers() {
if (commentViewers != null) {
return commentViewers;
}
commentViewers = new ArrayList<CommentViewer>(commentGroup.getCommentAttributes().size());
for (final TaskAttribute commentAttribute : commentGroup.getCommentAttributes()) {
CommentViewer commentViewer = new CommentViewer(commentAttribute);
commentViewers.add(commentViewer);
}
return commentViewers;
}
public boolean isExpanded() {
if (groupSection != null) {
return groupSection.isExpanded();
}
if (commentViewers != null) {
for (CommentViewer commentViewer : commentViewers) {
if (commentViewer.isExpanded()) {
return true;
}
}
}
return false;
}
/**
* Returns true if this group and all comments in it are expanded.
*/
public boolean isFullyExpanded() {
if (groupSection != null && !groupSection.isExpanded()) {
return false;
}
if (commentViewers != null) {
for (CommentViewer commentViewer : commentViewers) {
if (!commentViewer.isExpanded()) {
return false;
}
}
return true;
}
return false;
}
public boolean isRenderedInSubSection() {
return renderedInSubSection;
}
/**
* Expands this group and all comments in it.
*/
public void setExpanded(boolean expanded) {
if (groupSection != null && groupSection.isExpanded() != expanded) {
EditorUtil.toggleExpandableComposite(expanded, groupSection);
}
if (commentViewers != null) {
for (CommentViewer commentViewer : commentViewers) {
commentViewer.setExpanded(expanded);
}
}
}
public void setRenderedInSubSection(boolean renderedInSubSection) {
this.renderedInSubSection = renderedInSubSection;
}
// private void createToolBar(final FormToolkit toolkit) {
// if (section == null) {
// return;
// }
//
// ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT);
//
// Action collapseAllAction = new Action("") {
// @Override
// public void run() {
// toggleSection(section, false);
// }
// };
// collapseAllAction.setImageDescriptor(CommonImages.COLLAPSE_ALL_SMALL);
// collapseAllAction.setToolTipText("Collapse All Current Comments");
// toolBarManager.add(collapseAllAction);
//
// Action expandAllAction = new Action("") {
// @Override
// public void run() {
// toggleSection(section, true);
// }
// };
// expandAllAction.setImageDescriptor(CommonImages.EXPAND_ALL_SMALL);
// expandAllAction.setToolTipText("Expand All Current Comments");
// toolBarManager.add(expandAllAction);
//
// Composite toolbarComposite = toolkit.createComposite(section);
// toolbarComposite.setBackground(null);
// RowLayout rowLayout = new RowLayout();
// rowLayout.marginTop = 0;
// rowLayout.marginBottom = 0;
// rowLayout.marginLeft = 0;
// rowLayout.marginRight = 0;
// toolbarComposite.setLayout(rowLayout);
//
// toolBarManager.createControl(toolbarComposite);
// section.setTextClient(toolbarComposite);
// }
}
private class CommentViewer {
private Composite buttonComposite;
private final TaskAttribute commentAttribute;
private ExpandableComposite commentComposite;
private final TaskComment taskComment;
private AbstractAttributeEditor editor;
public CommentViewer(TaskAttribute commentAttribute) {
this.commentAttribute = commentAttribute;
this.taskComment = new TaskComment(getModel().getTaskRepository(), getModel().getTask(), commentAttribute);
}
public Control createControl(Composite composite, final FormToolkit toolkit) {
boolean hasIncomingChanges = getModel().hasIncomingChanges(commentAttribute);
getTaskData().getAttributeMapper().updateTaskComment(taskComment, commentAttribute);
int style = ExpandableComposite.TREE_NODE | ExpandableComposite.LEFT_TEXT_CLIENT_ALIGNMENT
| ExpandableComposite.COMPACT;
if (hasIncomingChanges || expandAllInProgress) {
style |= ExpandableComposite.EXPANDED;
}
commentComposite = toolkit.createExpandableComposite(composite, style);
commentComposite.clientVerticalSpacing = 0;
commentComposite.setLayout(new GridLayout());
commentComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
commentComposite.setTitleBarForeground(toolkit.getColors().getColor(IFormColors.TITLE));
buttonComposite = createTitle(commentComposite, toolkit);
final Composite commentTextComposite = toolkit.createComposite(commentComposite);
commentComposite.setClient(commentTextComposite);
commentTextComposite.setLayout(new FillWidthLayout(EditorUtil.getLayoutAdvisor(getTaskEditorPage()), 15, 0,
0, 3));
commentComposite.addExpansionListener(new ExpansionAdapter() {
@Override
public void expansionStateChanged(ExpansionEvent event) {
expandComment(toolkit, commentTextComposite, event.getState());
}
});
if (hasIncomingChanges) {
commentComposite.setBackground(getTaskEditorPage().getAttributeEditorToolkit().getColorIncoming());
}
if (commentComposite.isExpanded()) {
expandComment(toolkit, commentTextComposite, true);
}
// for outline
EditorUtil.setMarker(commentComposite, commentAttribute.getId());
return commentComposite;
}
private Composite createTitle(final ExpandableComposite commentComposite, final FormToolkit toolkit) {
// always visible
Composite titleComposite = toolkit.createComposite(commentComposite);
commentComposite.setTextClient(titleComposite);
RowLayout rowLayout = new RowLayout();
rowLayout.pack = true;
rowLayout.marginLeft = 0;
rowLayout.marginBottom = 0;
rowLayout.marginTop = 0;
EditorUtil.center(rowLayout);
titleComposite.setLayout(rowLayout);
titleComposite.setBackground(null);
ImageHyperlink expandCommentHyperlink = createTitleHyperLink(toolkit, titleComposite, taskComment);
expandCommentHyperlink.setFont(commentComposite.getFont());
expandCommentHyperlink.addHyperlinkListener(new HyperlinkAdapter() {
@Override
public void linkActivated(HyperlinkEvent e) {
EditorUtil.toggleExpandableComposite(!commentComposite.isExpanded(), commentComposite);
}
});
// only visible when section is expanded
final Composite buttonComposite = toolkit.createComposite(titleComposite);
RowLayout buttonCompLayout = new RowLayout();
buttonCompLayout.marginBottom = 0;
buttonCompLayout.marginTop = 0;
buttonComposite.setLayout(buttonCompLayout);
buttonComposite.setBackground(null);
buttonComposite.setVisible(commentComposite.isExpanded());
ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT);
ReplyToCommentAction replyAction = new ReplyToCommentAction(this, taskComment);
replyAction.setImageDescriptor(TasksUiImages.COMMENT_REPLY_SMALL);
toolBarManager.add(replyAction);
toolBarManager.createControl(buttonComposite);
return buttonComposite;
}
private ImageHyperlink createTitleHyperLink(final FormToolkit toolkit, final Composite toolbarComp,
final ITaskComment taskComment) {
ImageHyperlink formHyperlink = toolkit.createImageHyperlink(toolbarComp, SWT.NONE);
formHyperlink.setBackground(null);
formHyperlink.setForeground(toolkit.getColors().getColor(IFormColors.TITLE));
IRepositoryPerson author = taskComment.getAuthor();
if (author != null
&& author.getPersonId().equalsIgnoreCase(getTaskEditorPage().getTaskRepository().getUserName())) {
formHyperlink.setImage(CommonImages.getImage(CommonImages.PERSON_ME_NARROW));
} else {
formHyperlink.setImage(CommonImages.getImage(CommonImages.PERSON_NARROW));
}
StringBuilder sb = new StringBuilder();
if (taskComment.getNumber() >= 0) {
sb.append(taskComment.getNumber());
sb.append(": "); //$NON-NLS-1$
}
if (author != null) {
if (author.getName() != null) {
sb.append(author.getName());
formHyperlink.setToolTipText(author.getPersonId());
} else {
sb.append(author.getPersonId());
}
}
if (taskComment.getCreationDate() != null) {
sb.append(", "); //$NON-NLS-1$
sb.append(EditorUtil.formatDateTime(taskComment.getCreationDate()));
}
formHyperlink.setText(sb.toString());
formHyperlink.setEnabled(true);
formHyperlink.setUnderlined(false);
return formHyperlink;
}
private void expandComment(FormToolkit toolkit, Composite composite, boolean expanded) {
buttonComposite.setVisible(expanded);
if (expanded && composite.getData(KEY_EDITOR) == null) {
// create viewer
TaskAttribute textAttribute = getTaskData().getAttributeMapper().getAssoctiatedAttribute(
taskComment.getTaskAttribute());
editor = createAttributeEditor(textAttribute);
if (editor != null) {
editor.setDecorationEnabled(false);
editor.createControl(composite, toolkit);
editor.getControl().addMouseListener(new MouseAdapter() {
@Override
public void mouseDown(MouseEvent e) {
getTaskEditorPage().selectionChanged(taskComment);
}
});
composite.setData(KEY_EDITOR, editor);
getTaskEditorPage().getAttributeEditorToolkit().adapt(editor);
getTaskEditorPage().reflow();
}
} else if (!expanded && composite.getData(KEY_EDITOR) != null) {
// dispose viewer
AbstractAttributeEditor editor = (AbstractAttributeEditor) composite.getData(KEY_EDITOR);
editor.getControl().setMenu(null);
editor.getControl().dispose();
composite.setData(KEY_EDITOR, null);
getTaskEditorPage().reflow();
}
getTaskEditorPage().selectionChanged(taskComment);
}
public boolean isExpanded() {
return commentComposite != null && commentComposite.isExpanded();
}
public void setExpanded(boolean expanded) {
if (commentComposite != null && commentComposite.isExpanded() != expanded) {
EditorUtil.toggleExpandableComposite(expanded, commentComposite);
}
}
/**
* Returns the comment viewer.
*
* @return null, if the viewer has not been constructed
*/
public AbstractAttributeEditor getEditor() {
return editor;
}
}
private class ReplyToCommentAction extends AbstractReplyToCommentAction implements IMenuCreator {
private final ITaskComment taskComment;
private final CommentViewer commentViewer;
public ReplyToCommentAction(CommentViewer commentViewer, ITaskComment taskComment) {
super(TaskEditorCommentPart.this.getTaskEditorPage(), taskComment);
this.commentViewer = commentViewer;
this.taskComment = taskComment;
setMenuCreator(this);
}
@Override
protected String getReplyText() {
return taskComment.getText();
}
public Menu getMenu(Control parent) {
currentViewer = commentViewer;
selectionProvider.setSelection(new StructuredSelection(taskComment));
return commentMenu;
}
public void dispose() {
}
public Menu getMenu(Menu parent) {
selectionProvider.setSelection(new StructuredSelection(taskComment));
return commentMenu;
}
}
/** Expandable composites are indented by 6 pixels by default. */
private static final int INDENT = -6;
private static final String KEY_EDITOR = "viewer"; //$NON-NLS-1$
private List<TaskAttribute> commentAttributes;
private CommentGroupStrategy commentGroupStrategy;
private List<CommentGroupViewer> commentGroupViewers;
private boolean expandAllInProgress;
private boolean hasIncoming;
protected Section section;
private SelectionProviderAdapter selectionProvider;
// XXX: stores a reference to the viewer for which the commentMenu was displayed last
private CommentViewer currentViewer;
private Menu commentMenu;
private CommentActionGroup actionGroup;
public TaskEditorCommentPart() {
this.commentGroupStrategy = new CommentGroupStrategy() {
@Override
protected boolean hasIncomingChanges(ITaskComment taskComment) {
return getModel().hasIncomingChanges(taskComment.getTaskAttribute());
}
};
setPartName(Messages.TaskEditorCommentPart_Comments);
}
private void collapseAllComments() {
try {
getTaskEditorPage().setReflow(false);
boolean collapsed = false;
List<CommentGroupViewer> viewers = getCommentGroupViewers();
for (int i = 0; i < viewers.size(); i++) {
if (viewers.get(i).isExpanded()) {
viewers.get(i).setExpanded(false);
collapsed = viewers.get(i).isRenderedInSubSection();
break;
}
}
if (!collapsed && section != null) {
EditorUtil.toggleExpandableComposite(false, section);
}
} finally {
getTaskEditorPage().setReflow(true);
}
getTaskEditorPage().reflow();
}
private TaskComment convertToTaskComment(TaskDataModel taskDataModel, TaskAttribute commentAttribute) {
TaskComment taskComment = new TaskComment(taskDataModel.getTaskRepository(), taskDataModel.getTask(),
commentAttribute);
taskDataModel.getTaskData().getAttributeMapper().updateTaskComment(taskComment, commentAttribute);
return taskComment;
}
@Override
public void createControl(Composite parent, final FormToolkit toolkit) {
initialize();
selectionProvider = new SelectionProviderAdapter();
actionGroup = new CommentActionGroup();
MenuManager menuManager = new MenuManager();
menuManager.setRemoveAllWhenShown(true);
menuManager.addMenuListener(new IMenuListener() {
public void menuAboutToShow(IMenuManager manager) {
// get comment and add reply action as first item in the menu
ISelection selection = selectionProvider.getSelection();
if (selection instanceof IStructuredSelection && !selection.isEmpty()) {
Object element = ((IStructuredSelection) selection).getFirstElement();
if (element instanceof ITaskComment) {
final ITaskComment comment = (ITaskComment) element;
AbstractReplyToCommentAction replyAction = new AbstractReplyToCommentAction(
getTaskEditorPage(), comment) {
@Override
protected String getReplyText() {
return comment.getText();
}
};
manager.add(replyAction);
}
}
actionGroup.fillContextMenu(manager);
actionGroup.setContext(new ActionContext(selectionProvider.getSelection()));
if (currentViewer != null && currentViewer.getEditor() instanceof RichTextAttributeEditor) {
RichTextAttributeEditor editor = (RichTextAttributeEditor) currentViewer.getEditor();
if (editor.getViewSourceAction().isEnabled()) {
manager.add(new Separator("planning")); //$NON-NLS-1$
manager.add(editor.getViewSourceAction());
}
}
}
});
getTaskEditorPage().getEditorSite().registerContextMenu(ID_POPUP_MENU, menuManager, selectionProvider, false);
commentMenu = menuManager.createContextMenu(parent);
section = createSection(parent, toolkit, hasIncoming);
section.setText(section.getText() + " (" + commentAttributes.size() + ")"); //$NON-NLS-1$ //$NON-NLS-2$
if (commentAttributes.isEmpty()) {
section.setEnabled(false);
} else {
if (hasIncoming) {
expandSection(toolkit, section);
} else {
section.addExpansionListener(new ExpansionAdapter() {
@Override
public void expansionStateChanged(ExpansionEvent event) {
if (section.getClient() == null) {
try {
getTaskEditorPage().setReflow(false);
expandSection(toolkit, section);
} finally {
getTaskEditorPage().setReflow(true);
}
getTaskEditorPage().reflow();
}
}
});
}
}
setSection(toolkit, section);
}
@Override
public void dispose() {
super.dispose();
if (actionGroup != null) {
actionGroup.dispose();
}
}
private void expandAllComments() {
try {
expandAllInProgress = true;
getTaskEditorPage().setReflow(false);
if (section != null) {
// the expandAllInProgress flag will ensure that comments in top-level groups have been
// expanded, no need to expand groups explicitly
boolean expandGroups = section.getClient() != null;
EditorUtil.toggleExpandableComposite(true, section);
if (expandGroups) {
List<CommentGroupViewer> viewers = getCommentGroupViewers();
for (int i = viewers.size() - 1; i >= 0; i--) {
if (!viewers.get(i).isFullyExpanded()) {
viewers.get(i).setExpanded(true);
break;
}
}
}
}
} finally {
expandAllInProgress = false;
getTaskEditorPage().setReflow(true);
}
getTaskEditorPage().reflow();
}
private void expandSection(final FormToolkit toolkit, final Section section) {
Composite composite = toolkit.createComposite(section);
section.setClient(composite);
GridLayout layout = new GridLayout();
layout.marginHeight = 0;
composite.setLayout(layout);
List<CommentGroupViewer> viewers = getCommentGroupViewers();
for (CommentGroupViewer viewer : viewers) {
Control control = viewer.createControl(composite, toolkit);
if (viewer.isRenderedInSubSection()) {
// align twistie of sub-section with section
GridDataFactory.fillDefaults().grab(true, false).indent(2 * INDENT, 0).applyTo(control);
} else {
GridDataFactory.fillDefaults().grab(true, false).indent(INDENT, 0).applyTo(control);
}
}
}
@Override
protected void fillToolBar(ToolBarManager barManager) {
if (commentAttributes.isEmpty()) {
return;
}
Action collapseAllAction = new Action("") { //$NON-NLS-1$
@Override
public void run() {
collapseAllComments();
}
};
collapseAllAction.setImageDescriptor(CommonImages.COLLAPSE_ALL_SMALL);
collapseAllAction.setToolTipText(Messages.TaskEditorCommentPart_Collapse_Comments);
barManager.add(collapseAllAction);
Action expandAllAction = new Action("") { //$NON-NLS-1$
@Override
public void run() {
expandAllComments();
}
};
expandAllAction.setImageDescriptor(CommonImages.EXPAND_ALL_SMALL);
expandAllAction.setToolTipText(Messages.TaskEditorCommentPart_Expand_Comments);
barManager.add(expandAllAction);
}
public CommentGroupStrategy getCommentGroupStrategy() {
return commentGroupStrategy;
}
public void setCommentGroupStrategy(CommentGroupStrategy commentGroupStrategy) {
this.commentGroupStrategy = commentGroupStrategy;
}
private List<CommentGroupViewer> getCommentGroupViewers() {
if (commentGroupViewers != null) {
return commentGroupViewers;
}
// group comments
List<ITaskComment> comments = new ArrayList<ITaskComment>();
for (TaskAttribute commentAttribute : this.commentAttributes) {
comments.add(convertToTaskComment(getModel(), commentAttribute));
}
String currentPersonId = getModel().getTaskRepository().getUserName();
List<CommentGroup> commentGroups = getCommentGroupStrategy().groupComments(comments, currentPersonId);
commentGroupViewers = new ArrayList<CommentGroupViewer>(commentGroups.size());
if (commentGroups.size() > 0) {
for (int i = 0; i < commentGroups.size(); i++) {
CommentGroupViewer viewer = new CommentGroupViewer(commentGroups.get(i));
boolean isLastGroup = i == commentGroups.size() - 1;
viewer.setRenderedInSubSection(!isLastGroup);
commentGroupViewers.add(viewer);
}
}
return commentGroupViewers;
}
private void initialize() {
commentAttributes = getTaskData().getAttributeMapper().getAttributesByType(getTaskData(),
TaskAttribute.TYPE_COMMENT);
if (commentAttributes.size() > 0) {
for (TaskAttribute commentAttribute : commentAttributes) {
if (getModel().hasIncomingChanges(commentAttribute)) {
hasIncoming = true;
break;
}
}
}
}
}