/******************************************************************************* * Copyright (c) 2004, 2015 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.commons.ui.CommonImages; import org.eclipse.mylyn.commons.ui.FillWidthLayout; import org.eclipse.mylyn.commons.ui.SelectionProviderAdapter; import org.eclipse.mylyn.commons.ui.compatibility.CommonColors; import org.eclipse.mylyn.commons.workbench.forms.CommonFormUtil; import org.eclipse.mylyn.commons.workbench.forms.ScalingHyperlink; 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.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.custom.StyledText; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.layout.RowLayout; import org.eclipse.swt.widgets.Canvas; 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; import com.google.common.base.Strings; /** * @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$ public 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) { List<CommentViewer> viewers = getCommentViewers(); Composite composite = toolkit.createComposite(parent); GridLayout contentLayout = new GridLayout(); contentLayout.marginHeight = 0; contentLayout.marginWidth = 0; composite.setLayout(contentLayout); for (CommentViewer commentViewer : viewers) { Control control = commentViewer.createControl(composite, toolkit); GridDataFactory.fillDefaults().grab(true, false).applyTo(control); } return composite; } private Control createControl(Composite parent, FormToolkit toolkit) { if (renderedInSubSection) { return createSection(parent, toolkit); } else { if (TaskEditorCommentPart.this.commentAttributes.size() >= CommentGroupStrategy.MAX_CURRENT) { // show a separator before current comments Canvas separator = new Canvas(parent, SWT.NONE) { @Override public Point computeSize(int wHint, int hHint, boolean changed) { return new Point((wHint == SWT.DEFAULT) ? 1 : wHint, 1); } }; separator.addPaintListener(new PaintListener() { public void paintControl(PaintEvent e) { e.gc.setForeground(separator.getForeground()); e.gc.drawLine(0, 0, separator.getSize().x, 0); } }); separator.setForeground(toolkit.getColors().getColor(IFormColors.TB_BORDER)); GridDataFactory.fillDefaults().grab(true, false).indent(2 * INDENT, 0).applyTo(separator); } return createCommentViewers(parent, toolkit); } } private Section createSection(Composite parent, FormToolkit toolkit) { int style = ExpandableComposite.TWISTIE | ExpandableComposite.SHORT_TITLE_BAR | ExpandableComposite.LEFT_TEXT_CLIENT_ALIGNMENT; 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); } reflow(); } } }); } return groupSection; } public List<CommentViewer> getCommentViewers() { if (commentViewers != null) { return commentViewers; } commentViewers = new ArrayList<CommentViewer>(commentGroup.getCommentAttributes().size()); for (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; } public void setExpanded(boolean expanded) { if (groupSection != null && groupSection.isExpanded() != expanded) { CommonFormUtil.setExpanded(groupSection, expanded); } } /** * Expands this group and all comments in it. */ public void setFullyExpanded(boolean expanded) { if (groupSection != null && groupSection.isExpanded() != expanded) { CommonFormUtil.setExpanded(groupSection, expanded); } if (commentViewers != null) { for (CommentViewer commentViewer : commentViewers) { commentViewer.setExpanded(expanded); } } } public void setRenderedInSubSection(boolean renderedInSubSection) { this.renderedInSubSection = renderedInSubSection; } public void createSectionHyperlink(String message, HyperlinkAdapter listener) { if (groupSection != null) { ScalingHyperlink resultLink = new ScalingHyperlink(groupSection, SWT.READ_ONLY); resultLink.setForeground(CommonColors.HYPERLINK_WIDGET); resultLink.setUnderlined(true); resultLink.setText(message); groupSection.setTextClient(resultLink); resultLink.getParent().layout(true, true); resultLink.addHyperlinkListener(listener); } } public void clearSectionHyperlink() { if (groupSection != null) { groupSection.setTextClient(null); } } } public class CommentViewer { private Composite buttonComposite; private final TaskAttribute commentAttribute; private ExpandableComposite commentComposite; private final TaskComment taskComment; private Composite commentViewer; private Composite userImageComposite; private AbstractAttributeEditor commentTextEditor; private boolean suppressSelectionChanged; public CommentViewer(TaskAttribute commentAttribute) { this.commentAttribute = commentAttribute; this.taskComment = new TaskComment(getModel().getTaskRepository(), getModel().getTask(), commentAttribute); } public Control createControl(Composite composite, 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 && !suppressExpandViewers)) { 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); Composite commentViewerComposite = toolkit.createComposite(commentComposite); commentComposite.setClient(commentViewerComposite); commentViewerComposite .setLayout(new FillWidthLayout(EditorUtil.getLayoutAdvisor(getTaskEditorPage()), 15, 0, 0, 3)); commentComposite.addExpansionListener(new ExpansionAdapter() { @Override public void expansionStateChanged(ExpansionEvent event) { expandComment(toolkit, commentViewerComposite, event.getState()); } }); if (hasIncomingChanges) { commentComposite.setBackground(getTaskEditorPage().getAttributeEditorToolkit().getColorIncoming()); } if (commentComposite.isExpanded()) { expandComment(toolkit, commentViewerComposite, true); } // for outline EditorUtil.setMarker(commentComposite, commentAttribute.getId()); return commentComposite; } private Composite createTitle(ExpandableComposite commentComposite, 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; rowLayout.center = true; titleComposite.setLayout(rowLayout); titleComposite.setBackground(null); ImageHyperlink expandCommentHyperlink = createTitleHyperLink(toolkit, titleComposite, taskComment); expandCommentHyperlink.addHyperlinkListener(new HyperlinkAdapter() { @Override public void linkActivated(HyperlinkEvent e) { CommonFormUtil.setExpanded(commentComposite, !commentComposite.isExpanded()); } }); ToolBarManager toolBarManagerTitle = new ToolBarManager(SWT.FLAT); addActionsToToolbarTitle(toolBarManagerTitle, taskComment, this); toolBarManagerTitle.createControl(titleComposite); // only visible when section is expanded 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 toolBarManagerButton = new ToolBarManager(SWT.FLAT); addActionsToToolbarButton(toolBarManagerButton, taskComment, this); toolBarManagerButton.createControl(buttonComposite); return buttonComposite; } private ImageHyperlink createTitleHyperLink(FormToolkit toolkit, Composite toolbarComp, 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.matchesUsername(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$ } String toolTipText = ""; //$NON-NLS-1$; if (author != null) { if (author.getName() != null) { sb.append(author.getName()); toolTipText = author.getPersonId(); } else { sb.append(author.getPersonId()); } } if (taskComment.getCreationDate() != null) { sb.append(", "); //$NON-NLS-1$ sb.append(EditorUtil.formatDateTime(taskComment.getCreationDate())); } formHyperlink.setFont(commentComposite.getFont()); formHyperlink.setToolTipText(toolTipText); 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) { commentViewer = toolkit.createComposite(composite); commentViewer.setLayout(new GridLayout(2, false)); commentViewer.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING)); //Create user image viewer boolean showAvatar = Boolean.parseBoolean(getModel().getTaskRepository() .getProperty(TaskEditorExtensions.REPOSITORY_PROPERTY_AVATAR_SUPPORT)); if (showAvatar) { String commentAuthor = getTaskData().getAttributeMapper().mapToRepositoryKey(commentAttribute, TaskAttribute.COMMENT_AUTHOR); TaskAttribute userImageAttribute = commentAttribute.getAttribute(commentAuthor); if (userImageAttribute != null) { userImageComposite = toolkit.createComposite(commentViewer); userImageComposite.setLayout(new GridLayout(1, false)); GridDataFactory.swtDefaults().align(SWT.LEFT, SWT.BEGINNING).applyTo(userImageComposite); toolkit.paintBordersFor(userImageComposite); UserAttributeEditor userImage = new UserAttributeEditor(getModel(), userImageAttribute, 30); userImage.createControl(userImageComposite, toolkit); userImage.refresh(); } } //Create comment text viewer TaskAttribute textAttribute = getTaskData().getAttributeMapper() .getAssoctiatedAttribute(taskComment.getTaskAttribute()); commentTextEditor = createAttributeEditor(textAttribute); if (commentTextEditor != null) { commentTextEditor.setDecorationEnabled(false); commentTextEditor.createControl(commentViewer, toolkit); GridData commentGridData = new GridData(GridData.FILL_HORIZONTAL); commentGridData.verticalAlignment = GridData.BEGINNING; commentTextEditor.getControl().setLayoutData(commentGridData); commentTextEditor.getControl().addMouseListener(new MouseAdapter() { @Override public void mouseDown(MouseEvent e) { getTaskEditorPage().selectionChanged(taskComment); } }); composite.setData(KEY_EDITOR, commentTextEditor); getTaskEditorPage().getAttributeEditorToolkit().adapt(commentTextEditor); reflow(); } } else if (!expanded && composite.getData(KEY_EDITOR) != null) { // dispose viewer commentTextEditor.getControl().setMenu(null); commentTextEditor.getControl().dispose(); if (userImageComposite != null) { userImageComposite.setMenu(null); userImageComposite.dispose(); } if (commentViewer != null) { commentViewer.setMenu(null); commentViewer.dispose(); } composite.setData(KEY_EDITOR, null); reflow(); } if (!suppressSelectionChanged) { getTaskEditorPage().selectionChanged(taskComment); } } public boolean isExpanded() { return commentComposite != null && commentComposite.isExpanded(); } public void setExpanded(boolean expanded) { if (commentComposite != null && commentComposite.isExpanded() != expanded) { CommonFormUtil.setExpanded(commentComposite, expanded); } } /** * Returns the comment viewer. * * @return null, if the viewer has not been constructed */ public AbstractAttributeEditor getEditor() { return commentTextEditor; } public TaskAttribute getTaskAttribute() { return commentAttribute; } public TaskComment getTaskComment() { return taskComment; } public Control getControl() { return commentComposite; } public void suppressSelectionChanged(boolean value) { this.suppressSelectionChanged = value; } public String getReplyToText() { String replyText = taskComment.getText(); if (hasTextControl()) { Control textControl = commentTextEditor.getControl(); String selectedText = getSelectedText(textControl); if (!Strings.isNullOrEmpty(selectedText)) { replyText = selectedText; } } return replyText; } private String getSelectedText(Control control) { if (control instanceof StyledText) { return ((StyledText) control).getSelectionText(); } else if (control instanceof Composite) { for (Control child : ((Composite) control).getChildren()) { String selectedText = getSelectedText(child); if (!Strings.isNullOrEmpty(selectedText)) { return selectedText; } } } return null; } private boolean hasTextControl() { return commentTextEditor != null && commentTextEditor.getControl() != null && !commentTextEditor.getControl().isDisposed(); } } private class ReplyToCommentAction extends AbstractReplyToCommentAction { private final CommentViewer commentViewer; public ReplyToCommentAction(CommentViewer commentViewer) { super(TaskEditorCommentPart.this.getTaskEditorPage(), commentViewer.getTaskComment()); this.commentViewer = commentViewer; } @Override protected String getReplyText() { return commentViewer.getReplyToText(); } protected CommentViewer getCommentViewer() { return commentViewer; } public void dispose() { } } private class ReplyToCommentActionWithMenu extends ReplyToCommentAction implements IMenuCreator { public ReplyToCommentActionWithMenu(CommentViewer commentViewer) { super(commentViewer); setMenuCreator(this); } public Menu getMenu(Control parent) { currentViewer = getCommentViewer(); selectionProvider.setSelection(new StructuredSelection(currentViewer.getTaskComment())); return commentMenu; } public Menu getMenu(Menu parent) { selectionProvider.setSelection(new StructuredSelection(getCommentViewer().getTaskComment())); 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; /** * We can't use the reflow flag in AbstractTaskEditorPage because it gets set at various points where we might not * want to reflow. */ private boolean reflow = true; 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; private boolean suppressExpandViewers; public TaskEditorCommentPart() { this.commentGroupStrategy = new CommentGroupStrategy() { @Override protected boolean hasIncomingChanges(ITaskComment taskComment) { return getModel().hasIncomingChanges(taskComment.getTaskAttribute()); } }; setPartName(Messages.TaskEditorCommentPart_Comments); } protected void addActionsToToolbarButton(ToolBarManager toolBarManager, TaskComment taskComment, CommentViewer commentViewer) { ReplyToCommentAction replyAction = new ReplyToCommentActionWithMenu(commentViewer); replyAction.setImageDescriptor(TasksUiImages.COMMENT_REPLY_SMALL); toolBarManager.add(replyAction); } protected void addActionsToToolbarTitle(ToolBarManager toolBarManager, TaskComment taskComment, CommentViewer commentViewer) { } private void collapseAllComments() { try { getTaskEditorPage().setReflow(false); @SuppressWarnings("unused") boolean collapsed = false; List<CommentGroupViewer> viewers = getCommentGroupViewers(); for (int i = 0; i < viewers.size(); i++) { if (viewers.get(i).isExpanded()) { viewers.get(i).setFullyExpanded(false); collapsed = viewers.get(i).isRenderedInSubSection(); // bug 280152: collapse all groups //break; } } } finally { getTaskEditorPage().setReflow(true); } 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, 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()) { ReplyToCommentAction replyAction = new ReplyToCommentAction(currentViewer); manager.add(replyAction); } actionGroup.setContext(new ActionContext(selectionProvider.getSelection())); actionGroup.fillContextMenu(manager); 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 { expandAllInProgress = true; getTaskEditorPage().setReflow(false); expandSection(toolkit, section); } finally { expandAllInProgress = false; getTaskEditorPage().setReflow(true); } reflow(); } } }); } } setSection(toolkit, section); } @Override public void dispose() { super.dispose(); if (actionGroup != null) { actionGroup.dispose(); } } public void expandAllComments(boolean expandViewers) { try { expandAllInProgress = true; suppressExpandViewers = !expandViewers; 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 CommonFormUtil.setExpanded(section, true); if (expandViewers) { List<CommentGroupViewer> groupViewers = getCommentGroupViewers(); for (int i = groupViewers.size() - 1; i >= 0; i--) { if (!groupViewers.get(i).isFullyExpanded()) { groupViewers.get(i).setFullyExpanded(true); } } } } } finally { expandAllInProgress = false; suppressExpandViewers = false; getTaskEditorPage().setReflow(true); } reflow(); } private void expandSection(FormToolkit toolkit, Section section) { Composite composite = toolkit.createComposite(section); section.setClient(composite); composite.setLayout(EditorUtil.createSectionClientLayout()); 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) { 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(true); } }; expandAllAction.setImageDescriptor(CommonImages.EXPAND_ALL_SMALL); expandAllAction.setToolTipText(Messages.TaskEditorCommentPart_Expand_Comments); barManager.add(expandAllAction); if (commentAttributes.isEmpty()) { collapseAllAction.setEnabled(false); expandAllAction.setEnabled(false); } } public CommentGroupStrategy getCommentGroupStrategy() { return commentGroupStrategy; } public void setCommentGroupStrategy(CommentGroupStrategy commentGroupStrategy) { this.commentGroupStrategy = commentGroupStrategy; } public 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; } } } } @Override public boolean setFormInput(Object input) { if (input instanceof String) { String text = (String) input; if (commentAttributes != null) { for (TaskAttribute commentAttribute : commentAttributes) { if (text.equals(commentAttribute.getId())) { selectReveal(commentAttribute); } } } } return super.setFormInput(input); } public CommentViewer selectReveal(TaskAttribute commentAttribute) { if (commentAttribute == null) { return null; } expandAllComments(false); List<CommentGroupViewer> groupViewers = getCommentGroupViewers(); for (CommentGroupViewer groupViewer : groupViewers) { for (CommentViewer viewer : groupViewer.getCommentViewers()) { if (viewer.getTaskAttribute().equals(commentAttribute)) { // expand section groupViewer.setExpanded(true); // EditorUtil is consistent with behavior of outline EditorUtil.reveal(getTaskEditorPage().getManagedForm().getForm(), commentAttribute.getId()); return viewer; } } } return null; } public boolean isCommentSectionExpanded() { return section != null && section.isExpanded(); } public void reflow() { if (reflow) { getTaskEditorPage().reflow(); } } public void setReflow(boolean reflow) { this.reflow = reflow; } }