blob: 3af8598ee4a64da78a5b9ec629ce1d348263ab62 [file] [log] [blame]
/*******************************************************************************
* 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
* David Green - fixes for bug 237503
* Frank Becker - fixes for bug 252300
* Kevin Sawicki - fixes for bug 306029
*******************************************************************************/
package org.eclipse.mylyn.tasks.ui.editors;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuCreator;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.util.LocalSelectionTransfer;
import org.eclipse.jface.util.SafeRunnable;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.window.Window;
import org.eclipse.mylyn.commons.core.StatusHandler;
import org.eclipse.mylyn.commons.ui.CommonImages;
import org.eclipse.mylyn.commons.ui.CommonUiUtil;
import org.eclipse.mylyn.commons.ui.GradientCanvas;
import org.eclipse.mylyn.commons.workbench.editors.CommonTextSupport;
import org.eclipse.mylyn.commons.workbench.forms.CommonFormUtil;
import org.eclipse.mylyn.internal.tasks.core.AbstractTaskContainer;
import org.eclipse.mylyn.internal.tasks.core.ITaskListChangeListener;
import org.eclipse.mylyn.internal.tasks.core.ITaskListRunnable;
import org.eclipse.mylyn.internal.tasks.core.TaskContainerDelta;
import org.eclipse.mylyn.internal.tasks.core.data.ITaskDataManagerListener;
import org.eclipse.mylyn.internal.tasks.core.data.TaskDataManagerEvent;
import org.eclipse.mylyn.internal.tasks.ui.TasksUiPlugin;
import org.eclipse.mylyn.internal.tasks.ui.actions.ClearOutgoingAction;
import org.eclipse.mylyn.internal.tasks.ui.actions.DeleteTaskEditorAction;
import org.eclipse.mylyn.internal.tasks.ui.actions.NewSubTaskAction;
import org.eclipse.mylyn.internal.tasks.ui.actions.OpenWithBrowserAction;
import org.eclipse.mylyn.internal.tasks.ui.actions.SynchronizeEditorAction;
import org.eclipse.mylyn.internal.tasks.ui.editors.AbstractTaskEditorSection;
import org.eclipse.mylyn.internal.tasks.ui.editors.EditorUtil;
import org.eclipse.mylyn.internal.tasks.ui.editors.FocusTracker;
import org.eclipse.mylyn.internal.tasks.ui.editors.Messages;
import org.eclipse.mylyn.internal.tasks.ui.editors.TaskAttachmentDropListener;
import org.eclipse.mylyn.internal.tasks.ui.editors.TaskEditorActionContributor;
import org.eclipse.mylyn.internal.tasks.ui.editors.TaskEditorActionPart;
import org.eclipse.mylyn.internal.tasks.ui.editors.TaskEditorAttachmentPart;
import org.eclipse.mylyn.internal.tasks.ui.editors.TaskEditorAttributePart;
import org.eclipse.mylyn.internal.tasks.ui.editors.TaskEditorCommentPart;
import org.eclipse.mylyn.internal.tasks.ui.editors.TaskEditorContributionExtensionReader;
import org.eclipse.mylyn.internal.tasks.ui.editors.TaskEditorDescriptionPart;
import org.eclipse.mylyn.internal.tasks.ui.editors.TaskEditorFindSupport;
import org.eclipse.mylyn.internal.tasks.ui.editors.TaskEditorNewCommentPart;
import org.eclipse.mylyn.internal.tasks.ui.editors.TaskEditorOutlineNode;
import org.eclipse.mylyn.internal.tasks.ui.editors.TaskEditorOutlinePage;
import org.eclipse.mylyn.internal.tasks.ui.editors.TaskEditorPeoplePart;
import org.eclipse.mylyn.internal.tasks.ui.editors.TaskEditorPlanningPart;
import org.eclipse.mylyn.internal.tasks.ui.editors.TaskEditorRichTextPart;
import org.eclipse.mylyn.internal.tasks.ui.editors.TaskEditorSummaryPart;
import org.eclipse.mylyn.internal.tasks.ui.editors.TaskMigrator;
import org.eclipse.mylyn.internal.tasks.ui.editors.ToolBarButtonContribution;
import org.eclipse.mylyn.internal.tasks.ui.util.AttachmentUtil;
import org.eclipse.mylyn.internal.tasks.ui.util.TasksUiInternal;
import org.eclipse.mylyn.tasks.core.AbstractRepositoryConnector;
import org.eclipse.mylyn.tasks.core.IRepositoryElement;
import org.eclipse.mylyn.tasks.core.ITask;
import org.eclipse.mylyn.tasks.core.ITask.SynchronizationState;
import org.eclipse.mylyn.tasks.core.RepositoryStatus;
import org.eclipse.mylyn.tasks.core.TaskRepository;
import org.eclipse.mylyn.tasks.core.data.ITaskDataWorkingCopy;
import org.eclipse.mylyn.tasks.core.data.TaskAttribute;
import org.eclipse.mylyn.tasks.core.data.TaskData;
import org.eclipse.mylyn.tasks.core.data.TaskDataModel;
import org.eclipse.mylyn.tasks.core.data.TaskDataModelEvent;
import org.eclipse.mylyn.tasks.core.data.TaskDataModelListener;
import org.eclipse.mylyn.tasks.core.data.TaskRelation;
import org.eclipse.mylyn.tasks.core.sync.SubmitJob;
import org.eclipse.mylyn.tasks.core.sync.SubmitJobEvent;
import org.eclipse.mylyn.tasks.core.sync.SubmitJobListener;
import org.eclipse.mylyn.tasks.ui.AbstractRepositoryConnectorUi;
import org.eclipse.mylyn.tasks.ui.TasksUi;
import org.eclipse.mylyn.tasks.ui.TasksUiImages;
import org.eclipse.mylyn.tasks.ui.TasksUiUtil;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DropTarget;
import org.eclipse.swt.dnd.FileTransfer;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.ScrollBar;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.forms.FormColors;
import org.eclipse.ui.forms.IFormColors;
import org.eclipse.ui.forms.IFormPart;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.editor.FormPage;
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.ScrolledForm;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.services.IDisposable;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
/**
* Extend to provide a task editor page.
*
* @author Mik Kersten
* @author Rob Elves
* @author Steffen Pingel
* @since 3.0
*/
public abstract class AbstractTaskEditorPage extends TaskFormPage
implements ISelectionProvider, ISelectionChangedListener {
/**
* Causes the form page to reflow on resize.
*/
private final class ParentResizeHandler implements Listener {
private int generation;
public void handleEvent(Event event) {
++generation;
Display.getCurrent().timerExec(300, new Runnable() {
int scheduledGeneration = generation;
public void run() {
if (getManagedForm().getForm().isDisposed()) {
return;
}
// only reflow if this is the latest generation to prevent
// unnecessary reflows while the form is being resized
if (scheduledGeneration == generation) {
getManagedForm().reflow(true);
}
}
});
}
}
private class SubmitTaskJobListener extends SubmitJobListener {
private final boolean attachContext;
private final boolean expandLastComment;
public SubmitTaskJobListener(boolean attachContext, boolean expandLastComment) {
this.attachContext = attachContext;
this.expandLastComment = expandLastComment;
}
@Override
public void done(SubmitJobEvent event) {
final SubmitJob job = event.getJob();
PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
private void addTask(ITask newTask) {
AbstractTaskContainer parent = null;
AbstractTaskEditorPart actionPart = getPart(ID_PART_ACTIONS);
if (actionPart instanceof TaskEditorActionPart) {
parent = ((TaskEditorActionPart) actionPart).getCategory();
}
TasksUiInternal.getTaskList().addTask(newTask, parent);
}
public void run() {
try {
if (job.getStatus() == null) {
TasksUiInternal.synchronizeRepositoryInBackground(getTaskRepository());
if (job.getTask().equals(getTask())) {
refresh();
} else {
ITask oldTask = getTask();
ITask newTask = job.getTask();
addTask(newTask);
TaskMigrator migrator = new TaskMigrator(oldTask);
migrator.setDelete(true);
migrator.setEditor(getTaskEditor());
migrator.setMigrateDueDate(
!connector.hasRepositoryDueDate(getTaskRepository(), newTask, taskData));
migrator.execute(newTask);
}
if (expandLastComment) {
expandLastComment();
}
}
handleTaskSubmitted(new SubmitJobEvent(job));
} finally {
showEditorBusy(false);
}
}
});
}
@Override
public void taskSubmitted(SubmitJobEvent event, IProgressMonitor monitor) throws CoreException {
if (!getModel().getTaskData().isNew() && attachContext) {
TaskData taskData = getModel().getTaskData();
TaskAttribute taskAttribute = null;
if (taskData != null) {
taskAttribute = taskData.getRoot().createMappedAttribute(TaskAttribute.NEW_ATTACHMENT);
}
AttachmentUtil.postContext(connector, getModel().getTaskRepository(), task, "", taskAttribute, monitor); //$NON-NLS-1$
}
}
@Override
public void taskSynchronized(SubmitJobEvent event, IProgressMonitor monitor) {
}
}
private final ITaskDataManagerListener TASK_DATA_LISTENER = new ITaskDataManagerListener() {
public void taskDataUpdated(final TaskDataManagerEvent event) {
ITask task = event.getTask();
if (task.equals(AbstractTaskEditorPage.this.getTask()) && event.getTaskDataUpdated()) {
refresh(task);
}
}
private void refresh(final ITask task) {
PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
public void run() {
if (refreshDisabled || busy) {
return;
}
if (!isDirty() && task.getSynchronizationState() == SynchronizationState.SYNCHRONIZED) {
// automatically refresh if the user has not made any changes and there is no chance of missing incomings
AbstractTaskEditorPage.this.refresh();
} else {
getTaskEditor().setMessage(Messages.AbstractTaskEditorPage_Task_has_incoming_changes,
IMessageProvider.WARNING, new HyperlinkAdapter() {
@Override
public void linkActivated(HyperlinkEvent e) {
AbstractTaskEditorPage.this.refresh();
}
});
setSubmitEnabled(false);
}
}
});
}
public void editsDiscarded(TaskDataManagerEvent event) {
if (event.getTask().equals(AbstractTaskEditorPage.this.getTask())) {
refresh(event.getTask());
}
}
};
private class NotInTaskListListener extends HyperlinkAdapter implements ITaskListChangeListener, IDisposable {
public NotInTaskListListener() {
TasksUiPlugin.getTaskList().addChangeListener(this);
}
@Override
public void linkActivated(HyperlinkEvent e) {
TasksUiPlugin.getTaskList().addTaskIfAbsent(task);
getTaskEditor().setMessage(null, IMessageProvider.NONE);
}
public void containersChanged(Set<TaskContainerDelta> containers) {
// clears message if task is added to Task List.
for (TaskContainerDelta taskContainerDelta : containers) {
if (task.equals(taskContainerDelta.getElement())) {
if (taskContainerDelta.getKind().equals(TaskContainerDelta.Kind.ADDED)) {
PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
public void run() {
getTaskEditor().setMessage(null, IMessageProvider.NONE);
}
});
}
}
}
}
public void dispose() {
TasksUiPlugin.getTaskList().removeChangeListener(this);
}
};
private class MenuCreator implements IMenuCreator {
private MenuManager menuManager;
private Menu menu;
public MenuCreator() {
}
public void dispose() {
if (menu != null) {
menu.dispose();
menu = null;
}
if (menuManager != null) {
menuManager.dispose();
menuManager = null;
}
}
public Menu getMenu(Control parent) {
if (menuManager == null) {
menuManager = new MenuManager();
initialize(menuManager);
}
return menuManager.createContextMenu(parent);
}
public Menu getMenu(Menu parent) {
return null;
}
protected void initialize(MenuManager menuManager) {
}
}
private static final String ERROR_NOCONNECTIVITY = Messages.AbstractTaskEditorPage_Unable_to_submit_at_this_time;
public static final String ID_PART_ACTIONS = "org.eclipse.mylyn.tasks.ui.editors.parts.actions"; //$NON-NLS-1$
public static final String ID_PART_ATTACHMENTS = "org.eclipse.mylyn.tasks.ui.editors.parts.attachments"; //$NON-NLS-1$
public static final String ID_PART_ATTRIBUTES = "org.eclipse.mylyn.tasks.ui.editors.parts.attributes"; //$NON-NLS-1$
public static final String ID_PART_COMMENTS = "org.eclipse.mylyn.tasks.ui.editors.parts.comments"; //$NON-NLS-1$
public static final String ID_PART_DESCRIPTION = "org.eclipse.mylyn.tasks.ui.editors.parts.descriptions"; //$NON-NLS-1$
public static final String ID_PART_NEW_COMMENT = "org.eclipse.mylyn.tasks.ui.editors.parts.newComment"; //$NON-NLS-1$
public static final String ID_PART_PEOPLE = "org.eclipse.mylyn.tasks.ui.editors.parts.people"; //$NON-NLS-1$
public static final String ID_PART_PLANNING = "org.eclipse.mylyn.tasks.ui.editors.parts.planning"; //$NON-NLS-1$
public static final String ID_PART_SUMMARY = "org.eclipse.mylyn.tasks.ui.editors.parts.summary"; //$NON-NLS-1$
public static final String PATH_ACTIONS = "actions"; //$NON-NLS-1$
/**
* @since 3.7
*/
public static final String PATH_ASSOCIATIONS = "associations"; //$NON-NLS-1$
public static final String PATH_ATTACHMENTS = "attachments"; //$NON-NLS-1$
public static final String PATH_ATTRIBUTES = "attributes"; //$NON-NLS-1$
public static final String PATH_COMMENTS = "comments"; //$NON-NLS-1$
public static final String PATH_HEADER = "header"; //$NON-NLS-1$
public static final String PATH_PEOPLE = "people"; //$NON-NLS-1$
public static final String PATH_PLANNING = "planning"; //$NON-NLS-1$
private AttributeEditorFactory attributeEditorFactory;
private AttributeEditorToolkit attributeEditorToolkit;
private AbstractRepositoryConnector connector;
private final String connectorKind;
private StructuredSelection defaultSelection;
private Composite editorComposite;
private ScrolledForm form;
private boolean busy;
private ISelection lastSelection;
private TaskDataModel model;
private boolean needsAddToCategory;
private boolean reflow;
private volatile boolean refreshDisabled;
private final ListenerList selectionChangedListeners;
private SynchronizeEditorAction synchronizeEditorAction;
private ITask task;
private TaskData taskData;
private FormToolkit toolkit;
private TaskEditorOutlinePage outlinePage;
private TaskAttachmentDropListener defaultDropListener;
private CommonTextSupport textSupport;
private Composite partControl;
private GradientCanvas footerComposite;
private boolean needsFooter;
private Button submitButton;
private boolean submitEnabled;
private boolean needsSubmit;
private boolean needsSubmitButton;
private boolean needsPrivateSection;
private FocusTracker focusTracker;
private TaskEditorFindSupport findSupport;
/**
* @since 3.1
*/
public AbstractTaskEditorPage(TaskEditor editor, String id, String label, String connectorKind) {
super(editor, id, label);
Assert.isNotNull(connectorKind);
this.connectorKind = connectorKind;
this.reflow = true;
this.selectionChangedListeners = new ListenerList();
this.submitEnabled = true;
this.needsSubmit = true;
}
public AbstractTaskEditorPage(TaskEditor editor, String connectorKind) {
this(editor, "id", "label", connectorKind); //$NON-NLS-1$ //$NON-NLS-2$
}
/**
* @since 3.1
* @see FormPage#getEditor()
*/
@Override
public TaskEditor getEditor() {
return (TaskEditor) super.getEditor();
}
public void addSelectionChangedListener(ISelectionChangedListener listener) {
selectionChangedListeners.add(listener);
}
public void appendTextToNewComment(String text) {
AbstractTaskEditorPart newCommentPart = getPart(ID_PART_NEW_COMMENT);
if (newCommentPart instanceof TaskEditorRichTextPart) {
((TaskEditorRichTextPart) newCommentPart).appendText(text);
newCommentPart.setFocus();
}
}
public boolean canPerformAction(String actionId) {
if (findSupport != null && actionId.equals(ActionFactory.FIND.getId())) {
return true;
}
return CommonTextSupport.canPerformAction(actionId, EditorUtil.getFocusControl(this));
}
public void close() {
if (Display.getCurrent() != null) {
getSite().getPage().closeEditor(getTaskEditor(), false);
} else {
// TODO consider removing asyncExec()
Display activeDisplay = getSite().getShell().getDisplay();
activeDisplay.asyncExec(new Runnable() {
public void run() {
if (getSite() != null && getSite().getPage() != null && !getManagedForm().getForm().isDisposed()) {
if (getTaskEditor() != null) {
getSite().getPage().closeEditor(getTaskEditor(), false);
} else {
getSite().getPage().closeEditor(AbstractTaskEditorPage.this, false);
}
}
}
});
}
}
protected AttributeEditorFactory createAttributeEditorFactory() {
return new AttributeEditorFactory(getModel(), getTaskRepository(), getEditorSite());
}
AttributeEditorToolkit createAttributeEditorToolkit() {
return new AttributeEditorToolkit(textSupport);
}
@Override
public void createPartControl(Composite parent) {
parent.addListener(SWT.Resize, new ParentResizeHandler());
if (needsFooter()) {
partControl = getEditor().getToolkit().createComposite(parent);
GridLayout partControlLayout = new GridLayout(1, false);
partControlLayout.marginWidth = 0;
partControlLayout.marginHeight = 0;
partControlLayout.verticalSpacing = 0;
partControl.setLayout(partControlLayout);
super.createPartControl(partControl);
getManagedForm().getForm().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
footerComposite = new GradientCanvas(partControl, SWT.NONE);
footerComposite.setSeparatorVisible(true);
footerComposite.setSeparatorAlignment(SWT.TOP);
GridLayout headLayout = new GridLayout();
headLayout.marginHeight = 0;
headLayout.marginWidth = 0;
headLayout.horizontalSpacing = 0;
headLayout.verticalSpacing = 0;
headLayout.numColumns = 1;
footerComposite.setLayout(headLayout);
footerComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
FormColors colors = getEditor().getToolkit().getColors();
Color top = colors.getColor(IFormColors.H_GRADIENT_END);
Color bottom = colors.getColor(IFormColors.H_GRADIENT_START);
footerComposite.setBackgroundGradient(new Color[] { bottom, top }, new int[] { 100 }, true);
footerComposite.putColor(IFormColors.H_BOTTOM_KEYLINE1, colors.getColor(IFormColors.H_BOTTOM_KEYLINE1));
footerComposite.putColor(IFormColors.H_BOTTOM_KEYLINE2, colors.getColor(IFormColors.H_BOTTOM_KEYLINE2));
footerComposite.putColor(IFormColors.H_HOVER_LIGHT, colors.getColor(IFormColors.H_HOVER_LIGHT));
footerComposite.putColor(IFormColors.H_HOVER_FULL, colors.getColor(IFormColors.H_HOVER_FULL));
footerComposite.putColor(IFormColors.TB_TOGGLE, colors.getColor(IFormColors.TB_TOGGLE));
footerComposite.putColor(IFormColors.TB_TOGGLE_HOVER, colors.getColor(IFormColors.TB_TOGGLE_HOVER));
footerComposite.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, true, false));
createFooterContent(footerComposite);
} else {
super.createPartControl(parent);
}
}
@Override
protected void createFormContent(final IManagedForm managedForm) {
super.createFormContent(managedForm);
form = managedForm.getForm();
toolkit = managedForm.getToolkit();
registerDefaultDropListener(form);
CommonFormUtil.disableScrollingOnFocus(form);
try {
setReflow(false);
editorComposite = form.getBody();
// TODO consider using TableWrapLayout, it makes resizing much faster
GridLayout editorLayout = new GridLayout();
editorLayout.verticalSpacing = 0;
editorComposite.setLayout(editorLayout);
editorComposite.setMenu(getTaskEditor().getMenu());
AbstractRepositoryConnectorUi connectorUi = TasksUiPlugin.getConnectorUi(getConnectorKind());
if (connectorUi == null) {
PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
public void run() {
getTaskEditor().setMessage(
Messages.AbstractTaskEditorPage_Synchronize_to_update_editor_contents,
IMessageProvider.INFORMATION, new HyperlinkAdapter() {
@Override
public void linkActivated(HyperlinkEvent e) {
AbstractTaskEditorPage.this.refresh();
}
});
}
});
}
if (taskData != null) {
createFormContentInternal();
}
updateHeaderMessage();
} finally {
setReflow(true);
// if the editor is restored as part of workbench startup then we must reflow() asynchronously
// otherwise the editor layout is incorrect
boolean reflowRequired = calculateReflowRequired(form);
if (reflowRequired) {
Display.getCurrent().asyncExec(new Runnable() {
public void run() {
// this fixes a problem with layout that occurs when an editor
// is restored before the workbench is fully initialized
reflow();
}
});
}
}
}
private boolean calculateReflowRequired(ScrolledForm form) {
Composite stopComposite = getEditor().getEditorParent().getParent().getParent();
Composite composite = form.getParent();
while (composite != null) {
Rectangle clientArea = composite.getClientArea();
if (clientArea.width > 1) {
return false;
}
if (composite == stopComposite) {
return true;
}
composite = composite.getParent();
}
return true;
}
private void createFormContentInternal() {
// end life-cycle of previous editor controls
if (attributeEditorToolkit != null) {
attributeEditorToolkit.dispose();
}
// start life-cycle of previous editor controls
if (attributeEditorFactory == null) {
attributeEditorFactory = createAttributeEditorFactory();
Assert.isNotNull(attributeEditorFactory);
}
attributeEditorToolkit = createAttributeEditorToolkit();
Assert.isNotNull(attributeEditorToolkit);
attributeEditorToolkit.setMenu(editorComposite.getMenu());
attributeEditorFactory.setEditorToolkit(attributeEditorToolkit);
createParts();
focusTracker = new FocusTracker();
focusTracker.track(editorComposite);
}
protected TaskDataModel createModel(TaskEditorInput input) throws CoreException {
ITaskDataWorkingCopy taskDataState;
try {
taskDataState = TasksUi.getTaskDataManager().getWorkingCopy(task);
} catch (OperationCanceledException e) {
// XXX retry once to work around bug 235479
taskDataState = TasksUi.getTaskDataManager().getWorkingCopy(task);
}
TaskRepository taskRepository = TasksUi.getRepositoryManager().getRepository(taskDataState.getConnectorKind(),
taskDataState.getRepositoryUrl());
return new TaskDataModel(taskRepository, input.getTask(), taskDataState);
}
/**
* To suppress a section, just remove its descriptor from the list. To add your own section in a specific order on
* the page, use the path value for where you want it to appear (your descriptor will appear after previously added
* descriptors with the same path), and add it to the descriptors list in your override of this method.
*/
protected Set<TaskEditorPartDescriptor> createPartDescriptors() {
Set<TaskEditorPartDescriptor> descriptors = new LinkedHashSet<TaskEditorPartDescriptor>();
descriptors.add(new TaskEditorPartDescriptor(ID_PART_SUMMARY) {
@Override
public AbstractTaskEditorPart createPart() {
return new TaskEditorSummaryPart();
}
}.setPath(PATH_HEADER));
descriptors.add(new TaskEditorPartDescriptor(ID_PART_ATTRIBUTES) {
@Override
public AbstractTaskEditorPart createPart() {
return new TaskEditorAttributePart();
}
}.setPath(PATH_ATTRIBUTES));
if (!taskData.isNew() && connector.getTaskAttachmentHandler() != null
&& (AttachmentUtil.canDownloadAttachment(task) || AttachmentUtil.canUploadAttachment(task))) {
descriptors.add(new TaskEditorPartDescriptor(ID_PART_ATTACHMENTS) {
@Override
public AbstractTaskEditorPart createPart() {
return new TaskEditorAttachmentPart();
}
}.setPath(PATH_ATTACHMENTS));
}
if (needsPrivateSection() || taskData.isNew()) {
descriptors.add(new TaskEditorPartDescriptor(ID_PART_PLANNING) {
@Override
public AbstractTaskEditorPart createPart() {
return new TaskEditorPlanningPart();
}
}.setPath(PATH_PLANNING));
}
descriptors.add(new TaskEditorPartDescriptor(ID_PART_DESCRIPTION) {
@Override
public AbstractTaskEditorPart createPart() {
TaskEditorDescriptionPart part = new TaskEditorDescriptionPart();
if (getModel().getTaskData().isNew()) {
part.setExpandVertically(true);
part.setSectionStyle(ExpandableComposite.TITLE_BAR | ExpandableComposite.EXPANDED);
}
return part;
}
}.setPath(PATH_COMMENTS));
if (!taskData.isNew()) {
descriptors.add(new TaskEditorPartDescriptor(ID_PART_COMMENTS) {
@Override
public AbstractTaskEditorPart createPart() {
return new TaskEditorCommentPart();
}
}.setPath(PATH_COMMENTS));
}
descriptors.add(new TaskEditorPartDescriptor(ID_PART_NEW_COMMENT) {
@Override
public AbstractTaskEditorPart createPart() {
return new TaskEditorNewCommentPart();
}
}.setPath(PATH_COMMENTS));
descriptors.add(new TaskEditorPartDescriptor(ID_PART_ACTIONS) {
@Override
public AbstractTaskEditorPart createPart() {
return new TaskEditorActionPart();
}
}.setPath(PATH_ACTIONS));
descriptors.add(new TaskEditorPartDescriptor(ID_PART_PEOPLE) {
@Override
public AbstractTaskEditorPart createPart() {
return new TaskEditorPeoplePart();
}
}.setPath(PATH_PEOPLE));
descriptors.addAll(getContributionPartDescriptors());
return descriptors;
}
private Collection<TaskEditorPartDescriptor> getContributionPartDescriptors() {
return TaskEditorContributionExtensionReader.getRepositoryEditorContributions();
}
protected void createParts() {
List<TaskEditorPartDescriptor> descriptors = new LinkedList<TaskEditorPartDescriptor>(createPartDescriptors());
// single column
createParts(PATH_HEADER, editorComposite, descriptors);
createParts(PATH_ASSOCIATIONS, editorComposite, descriptors);
createParts(PATH_ATTRIBUTES, editorComposite, descriptors);
createParts(PATH_ATTACHMENTS, editorComposite, descriptors);
createParts(PATH_PLANNING, editorComposite, descriptors);
createParts(PATH_COMMENTS, editorComposite, descriptors);
// two column
Composite bottomComposite = toolkit.createComposite(editorComposite);
bottomComposite.setLayout(GridLayoutFactory.fillDefaults().numColumns(2).create());
GridDataFactory.fillDefaults().grab(true, false).applyTo(bottomComposite);
createParts(PATH_ACTIONS, bottomComposite, descriptors);
createParts(PATH_PEOPLE, bottomComposite, descriptors);
bottomComposite.pack(true);
}
private void createParts(String path, final Composite parent,
final Collection<TaskEditorPartDescriptor> descriptors) {
for (Iterator<TaskEditorPartDescriptor> it = descriptors.iterator(); it.hasNext();) {
final TaskEditorPartDescriptor descriptor = it.next();
if (path == null || path.equals(descriptor.getPath())) {
SafeRunner.run(new ISafeRunnable() {
public void handleException(Throwable e) {
StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN,
"Error creating task editor part: \"" + descriptor.getId() + "\"", e)); //$NON-NLS-1$ //$NON-NLS-2$
}
public void run() throws Exception {
AbstractTaskEditorPart part = descriptor.createPart();
part.setPartId(descriptor.getId());
initializePart(parent, part, descriptors);
}
});
it.remove();
}
}
}
private void createSubParts(final AbstractTaskEditorSection parentPart,
final Collection<TaskEditorPartDescriptor> descriptors) {
for (final TaskEditorPartDescriptor descriptor : descriptors) {
int i;
String path = descriptor.getPath();
if (path != null && (i = path.indexOf("/")) != -1) { //$NON-NLS-1$
String parentId = path.substring(0, i);
final String subPath = path.substring(i + 1);
if (parentId.equals(parentPart.getPartId())) {
SafeRunner.run(new ISafeRunnable() {
public void handleException(Throwable e) {
StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN,
"Error creating task editor part: \"" + descriptor.getId() + "\"", e)); //$NON-NLS-1$ //$NON-NLS-2$
}
public void run() throws Exception {
AbstractTaskEditorPart part = descriptor.createPart();
part.setPartId(descriptor.getId());
getManagedForm().addPart(part);
part.initialize(AbstractTaskEditorPage.this);
parentPart.addSubPart(subPath, part);
}
});
}
}
}
}
@Override
public void dispose() {
if (textSupport != null) {
textSupport.dispose();
}
if (attributeEditorToolkit != null) {
attributeEditorToolkit.dispose();
}
TasksUiPlugin.getTaskDataManager().removeListener(TASK_DATA_LISTENER);
super.dispose();
}
public void doAction(String actionId) {
if (findSupport != null && actionId.equals(ActionFactory.FIND.getId())) {
findSupport.toggleFind();
}
CommonTextSupport.doAction(actionId, EditorUtil.getFocusControl(this));
}
@Override
public void doSave(IProgressMonitor monitor) {
if (!isDirty()) {
return;
}
getManagedForm().commit(true);
if (model.isDirty()) {
try {
model.save(monitor);
} catch (final CoreException e) {
StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN, "Error saving task", e)); //$NON-NLS-1$
getTaskEditor().setMessage(Messages.AbstractTaskEditorPage_Could_not_save_task, IMessageProvider.ERROR,
new HyperlinkAdapter() {
@Override
public void linkActivated(HyperlinkEvent event) {
TasksUiInternal.displayStatus(Messages.AbstractTaskEditorPage_Save_failed,
e.getStatus());
}
});
}
}
// update the summary of unsubmitted repository tasks
if (getTask().getSynchronizationState() == SynchronizationState.OUTGOING_NEW) {
String summary = connector.getTaskMapping(model.getTaskData()).getSummary();
try {
TasksUiPlugin.getTaskList().run(new ITaskListRunnable() {
public void execute(IProgressMonitor monitor) throws CoreException {
task.setSummary(summary);
}
});
TasksUiPlugin.getTaskList().notifyElementChanged(task);
} catch (CoreException e) {
StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN,
"Failed to set summary for task \"" + task + "\"", e)); //$NON-NLS-1$ //$NON-NLS-2$
}
}
TasksUiPlugin.getTaskList().addTaskIfAbsent(task);
updateHeaderMessage();
getManagedForm().dirtyStateChanged();
getTaskEditor().updateHeaderToolBar();
}
@Override
public void doSaveAs() {
throw new UnsupportedOperationException();
}
public void doSubmit() {
if (!submitEnabled || !needsSubmit()) {
return;
}
try {
showEditorBusy(true);
doSave(new NullProgressMonitor());
TaskAttribute newCommentAttribute = getModel().getTaskData()
.getRoot()
.getMappedAttribute(TaskAttribute.COMMENT_NEW);
boolean expandLastComment = newCommentAttribute != null
&& getModel().getChangedAttributes().contains(newCommentAttribute);
SubmitJob submitJob = TasksUiInternal.getJobFactory().createSubmitTaskJob(connector,
getModel().getTaskRepository(), task, getModel().getTaskData(),
getModel().getChangedOldAttributes());
submitJob.addSubmitJobListener(new SubmitTaskJobListener(getAttachContext(), expandLastComment));
submitJob.schedule();
} catch (RuntimeException e) {
showEditorBusy(false);
throw e;
}
TasksUiPlugin.getTaskList().addTaskIfAbsent(task);
}
/**
* Override for customizing the tool bar.
*/
@Override
public void fillToolBar(IToolBarManager toolBarManager) {
final TaskRepository taskRepository = (model != null) ? getModel().getTaskRepository() : null;
if (taskData == null) {
synchronizeEditorAction = new SynchronizeEditorAction();
synchronizeEditorAction.selectionChanged(new StructuredSelection(getTaskEditor()));
toolBarManager.appendToGroup("repository", synchronizeEditorAction); //$NON-NLS-1$
} else {
if (taskData.isNew()) {
DeleteTaskEditorAction deleteAction = new DeleteTaskEditorAction(getTask());
deleteAction.setImageDescriptor(CommonImages.CLEAR);
toolBarManager.appendToGroup("new", deleteAction); //$NON-NLS-1$
} else if (taskRepository != null) {
ClearOutgoingAction clearOutgoingAction = new ClearOutgoingAction(
Collections.singletonList((IRepositoryElement) task));
(clearOutgoingAction).setTaskEditorPage(this);
if (clearOutgoingAction.isEnabled()) {
toolBarManager.appendToGroup("new", clearOutgoingAction); //$NON-NLS-1$
}
if (task.getSynchronizationState() != SynchronizationState.OUTGOING_NEW) {
synchronizeEditorAction = new SynchronizeEditorAction();
synchronizeEditorAction.selectionChanged(new StructuredSelection(getTaskEditor()));
toolBarManager.appendToGroup("repository", synchronizeEditorAction); //$NON-NLS-1$
}
NewSubTaskAction newSubTaskAction = new NewSubTaskAction();
newSubTaskAction.selectionChanged(newSubTaskAction, new StructuredSelection(task));
if (newSubTaskAction.isEnabled()) {
toolBarManager.appendToGroup("new", newSubTaskAction); //$NON-NLS-1$
}
AbstractRepositoryConnectorUi connectorUi = TasksUiPlugin.getConnectorUi(taskData.getConnectorKind());
if (connectorUi != null) {
final String historyUrl = connectorUi.getTaskHistoryUrl(taskRepository, task);
if (historyUrl != null) {
final Action historyAction = new Action() {
@Override
public void run() {
TasksUiUtil.openUrl(historyUrl);
}
};
historyAction.setText(Messages.AbstractTaskEditorPage_History);
historyAction.setImageDescriptor(TasksUiImages.TASK_REPOSITORY_HISTORY);
historyAction.setToolTipText(Messages.AbstractTaskEditorPage_History);
if (getEditor().openWithBrowserAction != null) {
getEditor().openWithBrowserAction.setMenuCreator(new MenuCreator() {
@Override
protected void initialize(MenuManager menuManager) {
OpenWithBrowserAction openWithBrowserAction = new OpenWithBrowserAction();
openWithBrowserAction.selectionChanged(new StructuredSelection(task));
menuManager.add(openWithBrowserAction);
menuManager.add(new Separator());
menuManager.add(historyAction);
};
});
} else {
toolBarManager.prependToGroup("open", historyAction); //$NON-NLS-1$
}
}
}
}
if (needsSubmitButton()) {
ToolBarButtonContribution submitButtonContribution = new ToolBarButtonContribution(
"org.eclipse.mylyn.tasks.toolbars.submit") { //$NON-NLS-1$
@Override
protected Control createButton(Composite composite) {
submitButton = new Button(composite, SWT.FLAT);
submitButton.setText(Messages.TaskEditorActionPart_Submit + " "); //$NON-NLS-1$
submitButton.setImage(CommonImages.getImage(TasksUiImages.REPOSITORY_SUBMIT));
submitButton.setBackground(null);
submitButton.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event e) {
doSubmit();
}
});
return submitButton;
}
};
submitButtonContribution.marginLeft = 10;
toolBarManager.add(submitButtonContribution);
}
if (findSupport != null) {
findSupport.addFindAction(toolBarManager);
}
}
}
protected void fireSelectionChanged(ISelection selection) {
// create an event
final SelectionChangedEvent event = new SelectionChangedEvent(this, selection);
// fire the event
Object[] listeners = selectionChangedListeners.getListeners();
for (Object listener : listeners) {
final ISelectionChangedListener l = (ISelectionChangedListener) listener;
SafeRunner.run(new SafeRunnable() {
public void run() {
l.selectionChanged(event);
}
});
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public Object getAdapter(Class adapter) {
if (adapter == IContentOutlinePage.class) {
updateOutlinePage();
return outlinePage;
}
// TODO 3.5 replace by getTextSupport() method
if (adapter == CommonTextSupport.class) {
return textSupport;
}
return super.getAdapter(adapter);
}
private void updateOutlinePage() {
if (outlinePage == null) {
outlinePage = new TaskEditorOutlinePage();
outlinePage.addSelectionChangedListener(new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
ISelection selection = event.getSelection();
if (selection instanceof StructuredSelection) {
Object select = ((StructuredSelection) selection).getFirstElement();
selectReveal(select);
getEditor().setActivePage(getId());
}
}
});
}
if (getModel() != null) {
TaskEditorOutlineNode node = TaskEditorOutlineNode.parse(getModel().getTaskData(), false);
outlinePage.setInput(getTaskRepository(), node);
} else {
outlinePage.setInput(null, null);
}
}
private boolean getAttachContext() {
AbstractTaskEditorPart actionPart = getPart(ID_PART_ACTIONS);
if (actionPart instanceof TaskEditorActionPart) {
return ((TaskEditorActionPart) actionPart).getAttachContext();
}
return false;
}
public AttributeEditorFactory getAttributeEditorFactory() {
return attributeEditorFactory;
}
public AttributeEditorToolkit getAttributeEditorToolkit() {
return attributeEditorToolkit;
}
public AbstractRepositoryConnector getConnector() {
return connector;
}
public String getConnectorKind() {
return connectorKind;
}
/**
* @return The composite for the whole editor.
*/
public Composite getEditorComposite() {
return editorComposite;
}
public TaskDataModel getModel() {
return model;
}
public AbstractTaskEditorPart getPart(String partId) {
Assert.isNotNull(partId);
if (getManagedForm() != null) {
for (IFormPart part : getManagedForm().getParts()) {
if (part instanceof AbstractTaskEditorPart) {
AbstractTaskEditorPart taskEditorPart = (AbstractTaskEditorPart) part;
if (partId.equals(taskEditorPart.getPartId())) {
return taskEditorPart;
}
}
}
}
return null;
}
public ISelection getSelection() {
return lastSelection;
}
public ITask getTask() {
return task;
}
public TaskEditor getTaskEditor() {
return getEditor();
}
public TaskRepository getTaskRepository() {
// FIXME model can be null
return getModel().getTaskRepository();
}
/**
* Invoked after task submission has completed. This method is invoked on the UI thread in all cases whether
* submission was successful, canceled or failed. The value returned by <code>event.getJob().getStatus()</code>
* indicates the result of the submit job. Sub-classes may override but are encouraged to invoke the super method.
*
* @since 3.2
* @see SubmitJob
*/
protected void handleTaskSubmitted(SubmitJobEvent event) {
IStatus status = event.getJob().getStatus();
if (status != null && status.getSeverity() != IStatus.CANCEL) {
handleSubmitError(event.getJob());
}
}
private void handleSubmitError(SubmitJob job) {
if (form != null && !form.isDisposed()) {
final IStatus status = job.getStatus();
String message = null;
if (status.getCode() == RepositoryStatus.REPOSITORY_COMMENT_REQUIRED) {
TasksUiInternal.displayStatus(Messages.AbstractTaskEditorPage_Comment_required, status);
AbstractTaskEditorPart newCommentPart = getPart(ID_PART_NEW_COMMENT);
if (newCommentPart != null) {
newCommentPart.setFocus();
}
return;
} else if (status.getCode() == RepositoryStatus.ERROR_REPOSITORY_LOGIN) {
if (TasksUiUtil.openEditRepositoryWizard(getTaskRepository()) == Window.OK) {
submitEnabled = true;
doSubmit();
return;
} else {
message = getMessageFromStatus(status);
}
} else if (status.getCode() == RepositoryStatus.ERROR_IO) {
message = ERROR_NOCONNECTIVITY;
} else {
message = getMessageFromStatus(status);
}
getTaskEditor().setMessage(message, IMessageProvider.ERROR, new HyperlinkAdapter() {
@Override
public void linkActivated(HyperlinkEvent e) {
TasksUiInternal.displayStatus(Messages.AbstractTaskEditorPage_Submit_failed, status);
}
});
}
}
private String getMessageFromStatus(final IStatus status) {
String message;
if (status.getMessage().length() > 0) {
if (status.getMessage().length() < 256) {
message = Messages.AbstractTaskEditorPage_Submit_failed_ + status.getMessage();
} else {
message = Messages.AbstractTaskEditorPage_Submit_failed_ + status.getMessage().substring(0, 256)
+ "..."; //$NON-NLS-1$
}
} else {
message = Messages.AbstractTaskEditorPage_Submit_failed;
}
return message.replaceAll("\n", " ").replaceAll("\r", " "); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
}
@Override
public void init(IEditorSite site, IEditorInput input) {
super.init(site, input);
TaskEditorInput taskEditorInput = (TaskEditorInput) input;
this.task = taskEditorInput.getTask();
this.defaultSelection = new StructuredSelection(task);
this.lastSelection = defaultSelection;
IHandlerService handlerService = (IHandlerService) getSite().getService(IHandlerService.class);
this.textSupport = new CommonTextSupport(handlerService);
this.textSupport.setSelectionChangedListener(this);
createFindSupport();
initModel(taskEditorInput);
TasksUiPlugin.getTaskDataManager().addListener(TASK_DATA_LISTENER);
}
private void initModel(TaskEditorInput input) {
Assert.isTrue(model == null);
try {
this.model = createModel(input);
this.connector = TasksUi.getRepositoryManager().getRepositoryConnector(getConnectorKind());
setTaskData(model.getTaskData());
model.addModelListener(new TaskDataModelListener() {
@Override
public void attributeChanged(TaskDataModelEvent event) {
IManagedForm form = getManagedForm();
if (form != null && !form.isDirty()) {
form.dirtyStateChanged();
}
}
});
setNeedsAddToCategory(model.getTaskData().isNew());
} catch (final CoreException e) {
StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN, "Error opening task", e)); //$NON-NLS-1$
getTaskEditor().setStatus(Messages.AbstractTaskEditorPage_Error_opening_task,
Messages.AbstractTaskEditorPage_Open_failed, e.getStatus());
}
}
private void initializePart(Composite parent, AbstractTaskEditorPart part,
Collection<TaskEditorPartDescriptor> descriptors) {
getManagedForm().addPart(part);
part.initialize(this);
if (part instanceof AbstractTaskEditorSection) {
createSubParts((AbstractTaskEditorSection) part, descriptors);
}
if (parent != null) {
part.createControl(parent, toolkit);
if (part.getControl() != null) {
if (ID_PART_ACTIONS.equals(part.getPartId())) {
// do not expand horizontally
GridDataFactory.fillDefaults()
.align(SWT.FILL, SWT.FILL)
.grab(false, false)
.applyTo(part.getControl());
} else {
if (part.getExpandVertically()) {
GridDataFactory.fillDefaults()
.align(SWT.FILL, SWT.FILL)
.grab(true, true)
.applyTo(part.getControl());
} else {
GridDataFactory.fillDefaults()
.align(SWT.FILL, SWT.TOP)
.grab(true, false)
.applyTo(part.getControl());
}
}
// for outline
if (ID_PART_COMMENTS.equals(part.getPartId())) {
EditorUtil.setMarker(part.getControl(), TaskEditorOutlineNode.LABEL_COMMENTS);
} else if (ID_PART_ATTACHMENTS.equals(part.getPartId())) {
EditorUtil.setMarker(part.getControl(), TaskEditorOutlineNode.LABEL_ATTACHMENTS);
}
}
}
}
/**
* Subclasses may override to disable the task editor find functionality.
*
* @since 3.11
*/
protected void createFindSupport() {
this.findSupport = new TaskEditorFindSupport(this);
}
@Override
public boolean isDirty() {
return isModelDirty() || isFormDirty();
}
private boolean isFormDirty() {
IManagedForm form = getManagedForm();
return form != null && form.isDirty();
}
private boolean isModelDirty() {
TaskDataModel model = getModel();
return model != null && model.isDirty();
}
@Override
public boolean isSaveAsAllowed() {
return false;
}
public boolean needsAddToCategory() {
return needsAddToCategory;
}
/**
* Force a re-layout of entire form.
*/
public void reflow() {
if (reflow) {
try {
form.setRedraw(false);
// help the layout managers: ensure that the form width always matches
// the parent client area width.
Rectangle parentClientArea = form.getParent().getClientArea();
Point formSize = form.getSize();
if (formSize.x != parentClientArea.width) {
ScrollBar verticalBar = form.getVerticalBar();
int verticalBarWidth = verticalBar != null ? verticalBar.getSize().x : 15;
form.setSize(parentClientArea.width - verticalBarWidth, formSize.y);
}
form.layout(true, false);
form.reflow(true);
} finally {
form.setRedraw(true);
}
}
}
/**
* Updates the editor contents in place.
*
* @deprecated Use {@link #refresh()} instead
*/
@Deprecated
public void refreshFormContent() {
refresh();
}
/**
* Updates the editor contents in place.
*/
@Override
public void refresh() {
if (getManagedForm() == null || getManagedForm().getForm().isDisposed()) {
// editor possibly closed or page has not been initialized
return;
}
try {
showEditorBusy(true);
boolean hasIncoming = false;
if (getTask() != null) {
hasIncoming = getTask().getSynchronizationState().isIncoming();
}
if (model != null) {
doSave(new NullProgressMonitor());
refreshInput();
} else {
initModel(getTaskEditor().getTaskEditorInput());
}
if (taskData != null) {
try {
setReflow(false);
// prevent menu from being disposed when disposing control on the form during refresh
Menu menu = editorComposite.getMenu();
CommonUiUtil.setMenu(editorComposite, null);
// clear old controls and parts
for (Control control : editorComposite.getChildren()) {
control.dispose();
}
if (focusTracker != null) {
focusTracker.reset();
}
lastSelection = null;
for (IFormPart part : getManagedForm().getParts()) {
part.dispose();
getManagedForm().removePart(part);
}
// restore menu
editorComposite.setMenu(menu);
createFormContentInternal();
getTaskEditor().setMessage(null, 0);
if (hasIncoming) {
getTaskEditor().setActivePage(getId());
}
setSubmitEnabled(true);
} finally {
setReflow(true);
}
}
updateOutlinePage();
updateHeaderMessage();
getManagedForm().dirtyStateChanged();
getTaskEditor().updateHeaderToolBar();
} finally {
showEditorBusy(false);
}
reflow();
}
private void refreshInput() {
try {
refreshDisabled = true;
model.refresh(null);
} catch (CoreException e) {
getTaskEditor().setMessage(Messages.AbstractTaskEditorPage_Failed_to_read_task_data_ + e.getMessage(),
IMessageProvider.ERROR);
taskData = null;
return;
} finally {
refreshDisabled = false;
}
setTaskData(model.getTaskData());
}
/**
* Registers a drop listener for <code>control</code>. The default implementation registers a listener for attaching
* files. Does nothing if the editor is showing a new task.
* <p>
* Clients may override.
* </p>
*
* @param control
* the control to register the listener for
*/
public void registerDefaultDropListener(final Control control) {
if (getModel() == null || getModel().getTaskData().isNew()) {
return;
}
DropTarget target = new DropTarget(control, DND.DROP_COPY | DND.DROP_DEFAULT);
LocalSelectionTransfer localSelectionTransfer = LocalSelectionTransfer.getTransfer();
final TextTransfer textTransfer = TextTransfer.getInstance();
final FileTransfer fileTransfer = FileTransfer.getInstance();
Transfer[] types = new Transfer[] { localSelectionTransfer, textTransfer, fileTransfer };
target.setTransfer(types);
if (defaultDropListener == null) {
defaultDropListener = new TaskAttachmentDropListener(this);
}
target.addDropListener(defaultDropListener);
}
public void removeSelectionChangedListener(ISelectionChangedListener listener) {
selectionChangedListeners.remove(listener);
}
public void selectionChanged(Object element) {
selectionChanged(new SelectionChangedEvent(this, new StructuredSelection(element)));
}
public void selectionChanged(SelectionChangedEvent event) {
ISelection selection = event.getSelection();
if (selection instanceof TextSelection) {
// only update global actions
((TaskEditorActionContributor) getEditorSite().getActionBarContributor())
.updateSelectableActions(event.getSelection());
return;
}
if (selection.isEmpty()) {
// something was unselected, reset to default selection
selection = defaultSelection;
// XXX a styled text widget has lost focus, re-enable all edit actions
((TaskEditorActionContributor) getEditorSite().getActionBarContributor()).forceActionsEnabled();
}
if (!selection.equals(lastSelection)) {
this.lastSelection = selection;
fireSelectionChanged(lastSelection);
getSite().getSelectionProvider().setSelection(selection);
}
}
@Override
public void setFocus() {
if (focusTracker != null && focusTracker.setFocus()) {
return;
} else {
IFormPart[] parts = getManagedForm().getParts();
if (parts.length > 0) {
parts[0].setFocus();
return;
}
}
super.setFocus();
}
public void setNeedsAddToCategory(boolean needsAddToCategory) {
this.needsAddToCategory = needsAddToCategory;
}
public void setReflow(boolean reflow) {
this.reflow = reflow;
form.setRedraw(reflow);
}
public void setSelection(ISelection selection) {
IFormPart[] parts = getManagedForm().getParts();
for (IFormPart formPart : parts) {
if (formPart instanceof AbstractTaskEditorPart) {
if (((AbstractTaskEditorPart) formPart).setSelection(selection)) {
lastSelection = selection;
return;
}
}
}
}
// TODO EDITOR this needs to be tracked somewhere else
private void setSubmitEnabled(boolean enabled) {
AbstractTaskEditorPart actionPart = getPart(ID_PART_ACTIONS);
if (actionPart instanceof TaskEditorActionPart) {
((TaskEditorActionPart) actionPart).setSubmitEnabled(enabled);
}
if (submitButton != null && !submitButton.isDisposed()) {
submitButton.setEnabled(enabled);
}
submitEnabled = enabled;
}
private void setTaskData(TaskData taskData) {
this.taskData = taskData;
}
@Override
public void showBusy(boolean busy) {
if (getManagedForm() != null && !getManagedForm().getForm().isDisposed() && this.busy != busy) {
setSubmitEnabled(!busy);
CommonUiUtil.setEnabled(editorComposite, !busy);
this.busy = busy;
}
}
// TODO m4.0 remove
public void showEditorBusy(boolean busy) {
getTaskEditor().showBusy(busy);
refreshDisabled = busy;
}
private void updateHeaderMessage() {
if (taskData == null) {
getTaskEditor().setMessage(Messages.AbstractTaskEditorPage_Synchronize_to_retrieve_task_data,
IMessageProvider.WARNING, new HyperlinkAdapter() {
@Override
public void linkActivated(HyperlinkEvent e) {
if (synchronizeEditorAction != null) {
synchronizeEditorAction.run();
}
}
});
}
if (getTaskEditor().getMessage() == null
&& TasksUiPlugin.getTaskList().getTask(task.getRepositoryUrl(), task.getTaskId()) == null) {
getTaskEditor().setMessage(Messages.AbstractTaskEditorPage_Add_task_to_tasklist,
IMessageProvider.INFORMATION, new NotInTaskListListener());
}
}
@Override
public Control getPartControl() {
return partControl != null ? partControl : super.getPartControl();
}
/**
* Returns true, if the page has an always visible footer.
*
* @see #setNeedsFooter(boolean)
*/
private boolean needsFooter() {
return needsFooter;
}
/**
* Specifies that the page should provide an always visible footer. This flag is not set by default.
*
* @see #createFooterContent(Composite)
* @see #needsFooter()
*/
@SuppressWarnings("unused")
private void setNeedsFooter(boolean needsFooter) {
this.needsFooter = needsFooter;
}
private void createFooterContent(Composite parent) {
parent.setLayout(new GridLayout());
}
/**
* Returns true, if the page supports a submit operation.
*
* @since 3.2
* @see #setNeedsSubmit(boolean)
*/
public boolean needsSubmit() {
return needsSubmit;
}
/**
* Specifies that the page supports the submit operation. This flag is set to true by default.
*
* @since 3.2
* @see #needsSubmit()
* @see #doSubmit()
*/
public void setNeedsSubmit(boolean needsSubmit) {
this.needsSubmit = needsSubmit;
}
/**
* Returns true, if the page provides a submit button.
*
* @since 3.2
* @see #setNeedsSubmitButton(boolean)
*/
public boolean needsSubmitButton() {
return needsSubmitButton;
}
/**
* Specifies that the page supports submitting. This flag is set to false by default.
*
* @since 3.2
* @see #needsSubmitButton()
*/
public void setNeedsSubmitButton(boolean needsSubmitButton) {
this.needsSubmitButton = needsSubmitButton;
}
/**
* Returns true, if the page provides a submit button.
*
* @since 3.2
* @see #setNeedsPrivateSection(boolean)
*/
public boolean needsPrivateSection() {
return needsPrivateSection;
}
/**
* Specifies that the page should provide the private section. This flag is not set by default.
*
* @since 3.2
* @see #needsPrivateSection()
*/
public void setNeedsPrivateSection(boolean needsPrivateSection) {
this.needsPrivateSection = needsPrivateSection;
}
@Override
public boolean selectReveal(Object object) {
if (object instanceof TaskEditorOutlineNode) {
TaskEditorOutlineNode node = (TaskEditorOutlineNode) object;
TaskAttribute attribute = node.getData();
if (attribute != null) {
super.selectReveal(attribute.getId());
} else {
TaskRelation taskRelation = node.getTaskRelation();
TaskRepository taskRepository = node.getTaskRepository();
if (taskRelation != null && taskRepository != null) {
String taskID = taskRelation.getTaskId();
TasksUiUtil.openTask(taskRepository, taskID);
} else {
EditorUtil.reveal(this.getManagedForm().getForm(), node.getLabel());
}
return true;
}
}
return super.selectReveal(object);
}
void expandLastComment() {
if (getManagedForm() == null || getManagedForm().getForm().isDisposed()) {
// editor possibly closed or page has not been initialized
return;
}
if (taskData == null) {
return;
}
List<TaskAttribute> commentAttributes = taskData.getAttributeMapper().getAttributesByType(taskData,
TaskAttribute.TYPE_COMMENT);
if (commentAttributes.size() > 0) {
selectReveal(commentAttributes.get(commentAttributes.size() - 1).getId());
}
}
}