| /******************************************************************************* |
| * Copyright (c) 2004, 2008 Tasktop Technologies and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Tasktop Technologies - initial API and implementation |
| * David Green - fixes for bug 237503 |
| * Frank Becker - fixes for bug 252300 |
| *******************************************************************************/ |
| |
| 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.IToolBarManager; |
| 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.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.internal.provisional.commons.ui.CommonFormUtil; |
| import org.eclipse.mylyn.internal.provisional.commons.ui.CommonImages; |
| import org.eclipse.mylyn.internal.provisional.commons.ui.CommonTextSupport; |
| import org.eclipse.mylyn.internal.provisional.commons.ui.CommonUiUtil; |
| import org.eclipse.mylyn.internal.provisional.commons.ui.GradientCanvas; |
| import org.eclipse.mylyn.internal.tasks.core.AbstractTaskContainer; |
| import org.eclipse.mylyn.internal.tasks.core.ITaskListRunnable; |
| 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.SynchronizeEditorAction; |
| import org.eclipse.mylyn.internal.tasks.ui.editors.EditorUtil; |
| 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.TaskEditorDescriptionPart; |
| 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.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.RepositoryStatus; |
| import org.eclipse.mylyn.tasks.core.TaskRepository; |
| import org.eclipse.mylyn.tasks.core.ITask.SynchronizationState; |
| 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.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.custom.CCombo; |
| import org.eclipse.swt.custom.CTabFolder; |
| import org.eclipse.swt.custom.StyledText; |
| 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.events.FocusAdapter; |
| import org.eclipse.swt.events.FocusEvent; |
| import org.eclipse.swt.events.FocusListener; |
| 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.Combo; |
| 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.Link; |
| import org.eclipse.swt.widgets.Listener; |
| import org.eclipse.swt.widgets.Menu; |
| import org.eclipse.swt.widgets.ScrollBar; |
| import org.eclipse.swt.widgets.Spinner; |
| import org.eclipse.swt.widgets.TabFolder; |
| import org.eclipse.swt.widgets.Table; |
| import org.eclipse.swt.widgets.Text; |
| import org.eclipse.swt.widgets.Tree; |
| import org.eclipse.ui.IEditorInput; |
| import org.eclipse.ui.IEditorSite; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.dialogs.FilteredTree; |
| 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.Hyperlink; |
| import org.eclipse.ui.forms.widgets.ScrolledForm; |
| import org.eclipse.ui.forms.widgets.Section; |
| import org.eclipse.ui.handlers.IHandlerService; |
| 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; |
| |
| public SubmitTaskJobListener(boolean attachContext) { |
| this.attachContext = attachContext; |
| } |
| |
| @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.synchronizeRepository(getTaskRepository(), false); |
| if (job.getTask().equals(getTask())) { |
| refreshFormContent(); |
| } else { |
| ITask oldTask = getTask(); |
| ITask newTask = job.getTask(); |
| addTask(newTask); |
| |
| TaskMigrator migrator = new TaskMigrator(oldTask); |
| migrator.setDelete(true); |
| migrator.setEditor(getTaskEditor()); |
| migrator.execute(newTask); |
| } |
| } |
| handleTaskSubmitted(new SubmitJobEvent(job)); |
| } finally { |
| showEditorBusy(false); |
| } |
| } |
| }); |
| } |
| |
| @Override |
| public void taskSubmitted(SubmitJobEvent event, IProgressMonitor monitor) throws CoreException { |
| if (!getModel().getTaskData().isNew() && attachContext) { |
| AttachmentUtil.postContext(connector, getModel().getTaskRepository(), task, "", null, monitor); //$NON-NLS-1$ |
| } |
| } |
| |
| @Override |
| public void taskSynchronized(SubmitJobEvent event, IProgressMonitor monitor) { |
| } |
| |
| } |
| |
| // private class TaskListChangeListener extends TaskListChangeAdapter { |
| // @Override |
| // public void containersChanged(Set<TaskContainerDelta> containers) { |
| // if (refreshDisabled) { |
| // return; |
| // } |
| // ITask taskToRefresh = null; |
| // for (TaskContainerDelta taskContainerDelta : containers) { |
| // if (task.equals(taskContainerDelta.getElement())) { |
| // if (taskContainerDelta.getKind().equals(TaskContainerDelta.Kind.CONTENT) |
| // && !taskContainerDelta.isTransient()) { |
| // taskToRefresh = (ITask) taskContainerDelta.getElement(); |
| // break; |
| // } |
| // } |
| // } |
| // if (taskToRefresh != null) { |
| // PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { |
| // public void run() { |
| // if (!isDirty() && task.getSynchronizationState() == SynchronizationState.SYNCHRONIZED) { |
| // // automatically refresh if the user has not made any changes and there is no chance of missing incomings |
| // refreshFormContent(); |
| // } else { |
| // getTaskEditor().setMessage("Task has incoming changes", IMessageProvider.WARNING, |
| // new HyperlinkAdapter() { |
| // @Override |
| // public void linkActivated(HyperlinkEvent e) { |
| // refreshFormContent(); |
| // } |
| // }); |
| // setSubmitEnabled(false); |
| // } |
| // } |
| // }); |
| // } |
| // } |
| // } |
| |
| 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) { |
| 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 |
| refreshFormContent(); |
| } else { |
| getTaskEditor().setMessage(Messages.AbstractTaskEditorPage_Task_has_incoming_changes, |
| IMessageProvider.WARNING, new HyperlinkAdapter() { |
| @Override |
| public void linkActivated(HyperlinkEvent e) { |
| refreshFormContent(); |
| } |
| }); |
| setSubmitEnabled(false); |
| } |
| } |
| }); |
| } |
| |
| public void editsDiscarded(TaskDataManagerEvent event) { |
| if (event.getTask().equals(AbstractTaskEditorPage.this.getTask())) { |
| refresh(event.getTask()); |
| } |
| } |
| }; |
| |
| 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$ |
| |
| 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 static final String ID_POPUP_MENU = "org.eclipse.mylyn.tasks.ui.editor.menu.page"; |
| |
| private AttributeEditorFactory attributeEditorFactory; |
| |
| private AttributeEditorToolkit attributeEditorToolkit; |
| |
| private AbstractRepositoryConnector connector; |
| |
| private final String connectorKind; |
| |
| private StructuredSelection defaultSelection; |
| |
| private Composite editorComposite; |
| |
| private ScrolledForm form; |
| |
| private boolean formBusy; |
| |
| private Control lastFocusControl; |
| |
| 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 ITaskListChangeListener taskListChangeListener; |
| |
| 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; |
| |
| /** |
| * @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; |
| } |
| |
| 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(); |
| } |
| |
| private void addFocusListener(Composite composite, FocusListener listener) { |
| Control[] children = composite.getChildren(); |
| for (Control control : children) { |
| if ((control instanceof Text) || (control instanceof Button) || (control instanceof Combo) |
| || (control instanceof CCombo) || (control instanceof Tree) || (control instanceof Table) |
| || (control instanceof Spinner) || (control instanceof Link) |
| || (control instanceof org.eclipse.swt.widgets.List) || (control instanceof TabFolder) |
| || (control instanceof CTabFolder) || (control instanceof Hyperlink) |
| || (control instanceof FilteredTree) || (control instanceof StyledText)) { |
| control.addFocusListener(listener); |
| } |
| if (control instanceof Composite) { |
| addFocusListener((Composite) control, listener); |
| } |
| } |
| } |
| |
| 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) { |
| 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) { |
| 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(); |
| editorComposite.setLayout(editorLayout); |
| editorComposite.setLayoutData(new GridData(GridData.FILL_BOTH)); |
| |
| //form.setData("focusScrolling", Boolean.FALSE); |
| |
| // menuManager = new MenuManager(); |
| // menuManager.setRemoveAllWhenShown(true); |
| // getEditorSite().registerContextMenu(ID_POPUP_MENU, menuManager, this, true); |
| // editorComposite.setMenu(menuManager.createContextMenu(editorComposite)); |
| editorComposite.setMenu(getTaskEditor().getMenu()); |
| |
| AbstractRepositoryConnectorUi connectorUi = TasksUiPlugin.getConnectorUi(getConnectorKind()); |
| if (connectorUi == null) { |
| getTaskEditor().setMessage(Messages.AbstractTaskEditorPage_Synchronize_to_update_editor_contents, |
| IMessageProvider.INFORMATION, new HyperlinkAdapter() { |
| @Override |
| public void linkActivated(HyperlinkEvent e) { |
| refreshFormContent(); |
| } |
| }); |
| } |
| |
| 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(); |
| |
| FocusListener listener = new FocusAdapter() { |
| @Override |
| public void focusGained(FocusEvent e) { |
| lastFocusControl = (Control) e.widget; |
| } |
| }; |
| addFocusListener(editorComposite, listener); |
| AbstractTaskEditorPart summaryPart = getPart(ID_PART_SUMMARY); |
| if (summaryPart != null) { |
| lastFocusControl = summaryPart.getControl(); |
| } |
| } |
| |
| 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) { |
| descriptors.add(new TaskEditorPartDescriptor(ID_PART_ATTACHMENTS) { |
| @Override |
| public AbstractTaskEditorPart createPart() { |
| return new TaskEditorAttachmentPart(); |
| } |
| }.setPath(PATH_ATTACHMENTS)); |
| } |
| 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)); |
| if (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_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)); |
| return descriptors; |
| } |
| |
| protected void createParts() { |
| List<TaskEditorPartDescriptor> descriptors = new LinkedList<TaskEditorPartDescriptor>(createPartDescriptors()); |
| // single column |
| createParts(PATH_HEADER, editorComposite, descriptors); |
| createParts(PATH_ATTRIBUTES, editorComposite, descriptors); |
| createParts(PATH_ATTACHMENTS, editorComposite, descriptors); |
| createParts(PATH_COMMENTS, editorComposite, descriptors); |
| createParts(PATH_PLANNING, 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, 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); |
| } |
| }); |
| it.remove(); |
| } |
| } |
| } |
| |
| @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) { |
| CommonTextSupport.doAction(actionId, EditorUtil.getFocusControl(this)); |
| } |
| |
| @Override |
| public void doSave(IProgressMonitor monitor) { |
| if (!isDirty()) { |
| return; |
| } |
| |
| getManagedForm().commit(true); |
| |
| 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) { |
| final 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$ |
| } |
| } |
| |
| updateHeaderMessage(); |
| getManagedForm().dirtyStateChanged(); |
| getTaskEditor().updateHeaderToolBar(); |
| } |
| |
| @Override |
| public void doSaveAs() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public void doSubmit() { |
| if (!submitEnabled) { |
| return; |
| } |
| |
| showEditorBusy(true); |
| |
| doSave(new NullProgressMonitor()); |
| |
| SubmitJob submitJob = TasksUiInternal.getJobFactory().createSubmitTaskJob(connector, |
| getModel().getTaskRepository(), task, getModel().getTaskData(), getModel().getChangedOldAttributes()); |
| submitJob.addSubmitJobListener(new SubmitTaskJobListener(getAttachContext())); |
| submitJob.schedule(); |
| } |
| |
| /** |
| * 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.add(synchronizeEditorAction); |
| } else { |
| if (taskData.isNew()) { |
| DeleteTaskEditorAction deleteAction = new DeleteTaskEditorAction(getTask()); |
| toolBarManager.add(deleteAction); |
| } else if (taskRepository != null) { |
| ClearOutgoingAction clearOutgoingAction = new ClearOutgoingAction( |
| Collections.singletonList((IRepositoryElement) task)); |
| (clearOutgoingAction).setTaskEditorPage(this); |
| if (clearOutgoingAction.isEnabled()) { |
| toolBarManager.add(clearOutgoingAction); |
| } |
| |
| if (task.getSynchronizationState() != SynchronizationState.OUTGOING_NEW) { |
| synchronizeEditorAction = new SynchronizeEditorAction(); |
| synchronizeEditorAction.selectionChanged(new StructuredSelection(getTaskEditor())); |
| toolBarManager.add(synchronizeEditorAction); |
| } |
| |
| NewSubTaskAction newSubTaskAction = new NewSubTaskAction(); |
| newSubTaskAction.selectionChanged(newSubTaskAction, new StructuredSelection(task)); |
| if (newSubTaskAction.isEnabled()) { |
| toolBarManager.add(newSubTaskAction); |
| } |
| |
| AbstractRepositoryConnectorUi connectorUi = TasksUiPlugin.getConnectorUi(taskData.getConnectorKind()); |
| if (connectorUi != null) { |
| final String historyUrl = connectorUi.getTaskHistoryUrl(taskRepository, task); |
| if (historyUrl != null) { |
| Action historyAction = new Action() { |
| @Override |
| public void run() { |
| TasksUiUtil.openUrl(historyUrl); |
| } |
| }; |
| |
| historyAction.setImageDescriptor(TasksUiImages.TASK_REPOSITORY_HISTORY); |
| historyAction.setToolTipText(Messages.AbstractTaskEditorPage_History); |
| toolBarManager.add(historyAction); |
| } |
| } |
| } |
| } |
| } |
| |
| protected void fireSelectionChanged(ISelection selection) { |
| // create an event |
| final SelectionChangedEvent event = new SelectionChangedEvent(this, selection); |
| |
| // fire the event |
| Object[] listeners = selectionChangedListeners.getListeners(); |
| for (int i = 0; i < listeners.length; ++i) { |
| final ISelectionChangedListener l = (ISelectionChangedListener) listeners[i]; |
| SafeRunner.run(new SafeRunnable() { |
| public void run() { |
| l.selectionChanged(event); |
| } |
| }); |
| } |
| } |
| |
| @SuppressWarnings("unchecked") |
| @Override |
| public Object getAdapter(Class adapter) { |
| if (adapter == IContentOutlinePage.class) { |
| updateOutlinePage(); |
| return outlinePage; |
| } |
| // TODO 3.2 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(); |
| if (select instanceof TaskEditorOutlineNode) { |
| TaskEditorOutlineNode node = (TaskEditorOutlineNode) select; |
| TaskAttribute attribute = node.getData(); |
| if (attribute != null) { |
| if (TaskAttribute.TYPE_COMMENT.equals(attribute.getMetaData().getType())) { |
| AbstractTaskEditorPart actionPart = getPart(ID_PART_COMMENTS); |
| if (actionPart != null && actionPart.getControl() instanceof ExpandableComposite) { |
| CommonFormUtil.setExpanded((ExpandableComposite) actionPart.getControl(), true); |
| if (actionPart.getControl() instanceof Section) { |
| Control client = ((Section) actionPart.getControl()).getClient(); |
| if (client instanceof Composite) { |
| for (Control control : ((Composite) client).getChildren()) { |
| // toggle subsections |
| if (control instanceof Section) { |
| CommonFormUtil.setExpanded((Section) control, true); |
| } |
| } |
| } |
| } |
| } |
| } |
| EditorUtil.reveal(form, attribute.getId()); |
| } else { |
| EditorUtil.reveal(form, node.getLabel()); |
| } |
| getEditor().setActivePage(getId()); |
| } |
| } |
| } |
| }); |
| } |
| if (getModel() != null) { |
| TaskEditorOutlineNode node = TaskEditorOutlineNode.parse(getModel().getTaskData()); |
| 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); |
| 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) { |
| if (event.getJob().getStatus() != null) { |
| handleSubmitError(event.getJob()); |
| } |
| } |
| |
| private void handleSubmitError(SubmitJob job) { |
| if (form != null && !form.isDisposed()) { |
| final IStatus status = job.getStatus(); |
| 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(); |
| } |
| } else if (status.getCode() == RepositoryStatus.ERROR_REPOSITORY_LOGIN) { |
| if (TasksUiUtil.openEditRepositoryWizard(getTaskRepository()) == Window.OK) { |
| doSubmit(); |
| } |
| } else { |
| String message; |
| if (status.getCode() == RepositoryStatus.ERROR_IO) { |
| message = ERROR_NOCONNECTIVITY; |
| } else if (status.getMessage().length() > 0) { |
| message = Messages.AbstractTaskEditorPage_Submit_failed_ + status.getMessage(); |
| } else { |
| message = Messages.AbstractTaskEditorPage_Submit_failed; |
| } |
| getTaskEditor().setMessage(message, IMessageProvider.ERROR, new HyperlinkAdapter() { |
| @Override |
| public void linkActivated(HyperlinkEvent e) { |
| TasksUiInternal.displayStatus(Messages.AbstractTaskEditorPage_Submit_failed, status); |
| } |
| }); |
| } |
| } |
| } |
| |
| @Override |
| public void init(IEditorSite site, IEditorInput input) { |
| super.init(site, input); |
| |
| site.setSelectionProvider(this); |
| |
| 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); |
| |
| 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) { |
| getManagedForm().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) { |
| getManagedForm().addPart(part); |
| part.initialize(this); |
| 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); |
| } |
| } |
| } |
| |
| @Override |
| public boolean isDirty() { |
| return (getModel() != null && getModel().isDirty()) || (getManagedForm() != null && getManagedForm().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. |
| */ |
| public void refreshFormContent() { |
| if (getManagedForm().getForm().isDisposed()) { |
| // editor possibly closed as part of submit |
| return; |
| } |
| |
| try { |
| showEditorBusy(true); |
| |
| 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(); |
| } |
| lastFocusControl = null; |
| lastSelection = null; |
| for (IFormPart part : getManagedForm().getParts()) { |
| part.dispose(); |
| getManagedForm().removePart(part); |
| } |
| |
| // restore menu |
| editorComposite.setMenu(menu); |
| |
| createFormContentInternal(); |
| |
| getTaskEditor().setMessage(null, 0); |
| 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); |
| final TextTransfer textTransfer = TextTransfer.getInstance(); |
| final FileTransfer fileTransfer = FileTransfer.getInstance(); |
| Transfer[] types = new Transfer[] { 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); |
| } |
| } |
| |
| @Override |
| public void setFocus() { |
| if (lastFocusControl != null && !lastFocusControl.isDisposed()) { |
| lastFocusControl.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.setEnabled(enabled); |
| } |
| submitEnabled = enabled; |
| } |
| |
| private void setTaskData(TaskData taskData) { |
| this.taskData = taskData; |
| } |
| |
| @Override |
| public void showBusy(boolean busy) { |
| if (!getManagedForm().getForm().isDisposed() && busy != formBusy) { |
| // parentEditor.showBusy(busy); |
| // if (synchronizeEditorAction != null) { |
| // synchronizeEditorAction.setEnabled(!busy); |
| // } |
| // |
| // if (openBrowserAction != null) { |
| // openBrowserAction.setEnabled(!busy); |
| // } |
| // |
| // if (historyAction != null) { |
| // historyAction.setEnabled(!busy); |
| // } |
| // |
| // if (actionPart != null) { |
| // actionPart.setSubmitEnabled(!busy); |
| // } |
| // |
| // if (newSubTaskAction != null) { |
| // newSubTaskAction.setEnabled(!busy); |
| // } |
| // |
| // if (clearOutgoingAction != null) { |
| // clearOutgoingAction.setEnabled(!busy); |
| // } |
| |
| CommonUiUtil.setEnabled(editorComposite, !busy); |
| |
| formBusy = busy; |
| } |
| } |
| |
| 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 HyperlinkAdapter() { |
| @Override |
| public void linkActivated(HyperlinkEvent e) { |
| if (TasksUiPlugin.getTaskList().getTask(task.getRepositoryUrl(), task.getTaskId()) == null) { |
| TasksUiPlugin.getTaskList().addTask(task, |
| TasksUiPlugin.getTaskList().getDefaultCategory()); |
| getTaskEditor().setMessage(null, IMessageProvider.NONE, null); |
| // updateHeaderMessage(); |
| } |
| } |
| }); |
| } |
| } |
| |
| @Override |
| public Control getPartControl() { |
| return partControl != null ? partControl : super.getPartControl(); |
| } |
| |
| /** |
| * @since 3.2 |
| */ |
| public boolean needsFooter() { |
| return needsFooter; |
| } |
| |
| /** |
| * @since 3.2 |
| */ |
| public void setNeedsFooter(boolean needsFooter) { |
| this.needsFooter = needsFooter; |
| } |
| |
| private void createFooterContent(Composite parent) { |
| parent.setLayout(new GridLayout()); |
| |
| submitButton = toolkit.createButton(parent, Messages.TaskEditorActionPart_Submit, SWT.NONE); |
| GridData submitButtonData = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); |
| submitButtonData.widthHint = 100; |
| submitButton.setBackground(null); |
| submitButton.setImage(CommonImages.getImage(TasksUiImages.REPOSITORY_SUBMIT)); |
| submitButton.setLayoutData(submitButtonData); |
| submitButton.addListener(SWT.Selection, new Listener() { |
| public void handleEvent(Event e) { |
| doSubmit(); |
| } |
| }); |
| } |
| |
| } |