| /******************************************************************************* |
| * Copyright (c) 2004, 2015 Tasktop Technologies and others. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * https://www.eclipse.org/legal/epl-2.0 |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * 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; |
| } |
| } |