| /******************************************************************************* |
| * 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 |
| *******************************************************************************/ |
| |
| package org.eclipse.mylyn.internal.tasks.ui.deprecated; |
| |
| import java.io.File; |
| import java.lang.reflect.Method; |
| import java.text.SimpleDateFormat; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.Date; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.core.runtime.OperationCanceledException; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.SubProgressMonitor; |
| import org.eclipse.core.runtime.jobs.IJobChangeEvent; |
| import org.eclipse.core.runtime.jobs.IJobChangeListener; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.core.runtime.jobs.JobChangeAdapter; |
| import org.eclipse.jface.action.Action; |
| import org.eclipse.jface.action.ControlContribution; |
| import org.eclipse.jface.action.IMenuListener; |
| import org.eclipse.jface.action.IMenuManager; |
| import org.eclipse.jface.action.IToolBarManager; |
| import org.eclipse.jface.action.MenuManager; |
| import org.eclipse.jface.action.Separator; |
| import org.eclipse.jface.action.ToolBarManager; |
| import org.eclipse.jface.dialogs.IMessageProvider; |
| import org.eclipse.jface.dialogs.MessageDialog; |
| import org.eclipse.jface.fieldassist.ContentProposalAdapter; |
| import org.eclipse.jface.fieldassist.ControlDecoration; |
| import org.eclipse.jface.fieldassist.FieldDecoration; |
| import org.eclipse.jface.fieldassist.FieldDecorationRegistry; |
| import org.eclipse.jface.fieldassist.IContentProposalProvider; |
| import org.eclipse.jface.fieldassist.TextContentAdapter; |
| import org.eclipse.jface.layout.GridDataFactory; |
| import org.eclipse.jface.resource.JFaceResources; |
| import org.eclipse.jface.text.ITextListener; |
| import org.eclipse.jface.text.Region; |
| import org.eclipse.jface.text.TextEvent; |
| import org.eclipse.jface.text.TextViewer; |
| import org.eclipse.jface.text.hyperlink.IHyperlink; |
| import org.eclipse.jface.util.SafeRunnable; |
| import org.eclipse.jface.viewers.ColumnViewerToolTipSupport; |
| import org.eclipse.jface.viewers.DoubleClickEvent; |
| import org.eclipse.jface.viewers.IDoubleClickListener; |
| import org.eclipse.jface.viewers.ILabelProvider; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.ISelectionChangedListener; |
| import org.eclipse.jface.viewers.ISelectionProvider; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.jface.viewers.LabelProvider; |
| import org.eclipse.jface.viewers.SelectionChangedEvent; |
| import org.eclipse.jface.viewers.StructuredSelection; |
| import org.eclipse.jface.viewers.TableViewer; |
| import org.eclipse.jface.viewers.Viewer; |
| import org.eclipse.jface.viewers.ViewerSorter; |
| import org.eclipse.jface.window.ToolTip; |
| import org.eclipse.mylyn.commons.core.StatusHandler; |
| import org.eclipse.mylyn.internal.provisional.commons.ui.CommonImages; |
| import org.eclipse.mylyn.internal.provisional.commons.ui.CommonThemes; |
| import org.eclipse.mylyn.internal.tasks.core.AbstractTask; |
| import org.eclipse.mylyn.internal.tasks.core.AbstractTaskCategory; |
| import org.eclipse.mylyn.internal.tasks.core.CommentQuoter; |
| import org.eclipse.mylyn.internal.tasks.core.ITaskListChangeListener; |
| import org.eclipse.mylyn.internal.tasks.core.RepositoryQuery; |
| import org.eclipse.mylyn.internal.tasks.core.TaskContainerDelta; |
| import org.eclipse.mylyn.internal.tasks.core.deprecated.AbstractLegacyDuplicateDetector; |
| import org.eclipse.mylyn.internal.tasks.core.deprecated.AbstractLegacyRepositoryConnector; |
| import org.eclipse.mylyn.internal.tasks.core.deprecated.AbstractTaskDataHandler; |
| import org.eclipse.mylyn.internal.tasks.core.deprecated.RepositoryAttachment; |
| import org.eclipse.mylyn.internal.tasks.core.deprecated.RepositoryOperation; |
| import org.eclipse.mylyn.internal.tasks.core.deprecated.RepositoryTaskAttribute; |
| import org.eclipse.mylyn.internal.tasks.core.deprecated.RepositoryTaskData; |
| import org.eclipse.mylyn.internal.tasks.core.deprecated.TaskComment; |
| import org.eclipse.mylyn.internal.tasks.ui.PersonProposalLabelProvider; |
| import org.eclipse.mylyn.internal.tasks.ui.PersonProposalProvider; |
| import org.eclipse.mylyn.internal.tasks.ui.TaskHyperlink; |
| import org.eclipse.mylyn.internal.tasks.ui.TasksUiPlugin; |
| import org.eclipse.mylyn.internal.tasks.ui.actions.AbstractTaskEditorAction; |
| import org.eclipse.mylyn.internal.tasks.ui.actions.AttachAction; |
| import org.eclipse.mylyn.internal.tasks.ui.actions.AttachScreenshotAction; |
| import org.eclipse.mylyn.internal.tasks.ui.actions.ClearOutgoingAction; |
| 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.actions.ToggleTaskActivationAction; |
| import org.eclipse.mylyn.internal.tasks.ui.editors.AttachmentTableLabelProvider; |
| import org.eclipse.mylyn.internal.tasks.ui.editors.AttachmentsTableContentProvider; |
| import org.eclipse.mylyn.internal.tasks.ui.editors.ContentOutlineTools; |
| import org.eclipse.mylyn.internal.tasks.ui.editors.IRepositoryTaskAttributeListener; |
| import org.eclipse.mylyn.internal.tasks.ui.editors.IRepositoryTaskSelection; |
| import org.eclipse.mylyn.internal.tasks.ui.editors.RepositoryAttachmentEditorInput; |
| import org.eclipse.mylyn.internal.tasks.ui.editors.RepositoryTaskEditorDropListener; |
| import org.eclipse.mylyn.internal.tasks.ui.editors.RepositoryTaskOutlineNode; |
| import org.eclipse.mylyn.internal.tasks.ui.editors.RepositoryTaskOutlinePage; |
| import org.eclipse.mylyn.internal.tasks.ui.editors.RepositoryTaskSelection; |
| import org.eclipse.mylyn.internal.tasks.ui.editors.TaskListChangeAdapter; |
| import org.eclipse.mylyn.internal.tasks.ui.editors.TaskUrlHyperlink; |
| import org.eclipse.mylyn.internal.tasks.ui.search.SearchHitCollector; |
| import org.eclipse.mylyn.internal.tasks.ui.util.AttachmentUtil; |
| import org.eclipse.mylyn.internal.tasks.ui.util.TasksUiInternal; |
| import org.eclipse.mylyn.internal.tasks.ui.views.UpdateRepositoryConfigurationAction; |
| import org.eclipse.mylyn.tasks.core.AbstractDuplicateDetector; |
| 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.ITaskAttachment; |
| 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.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.mylyn.tasks.ui.editors.AbstractRenderingEngine; |
| import org.eclipse.mylyn.tasks.ui.editors.TaskEditor; |
| import org.eclipse.osgi.util.NLS; |
| import org.eclipse.search.ui.NewSearchUI; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.browser.Browser; |
| import org.eclipse.swt.browser.LocationAdapter; |
| import org.eclipse.swt.browser.LocationEvent; |
| import org.eclipse.swt.custom.BusyIndicator; |
| import org.eclipse.swt.custom.CCombo; |
| import org.eclipse.swt.custom.CTabFolder; |
| import org.eclipse.swt.custom.StackLayout; |
| import org.eclipse.swt.custom.StyledText; |
| import org.eclipse.swt.custom.VerifyKeyListener; |
| import org.eclipse.swt.dnd.Clipboard; |
| 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.ControlAdapter; |
| import org.eclipse.swt.events.ControlEvent; |
| import org.eclipse.swt.events.FocusAdapter; |
| import org.eclipse.swt.events.FocusEvent; |
| import org.eclipse.swt.events.FocusListener; |
| import org.eclipse.swt.events.KeyEvent; |
| import org.eclipse.swt.events.KeyListener; |
| import org.eclipse.swt.events.ModifyEvent; |
| import org.eclipse.swt.events.ModifyListener; |
| import org.eclipse.swt.events.SelectionAdapter; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.events.SelectionListener; |
| import org.eclipse.swt.events.VerifyEvent; |
| import org.eclipse.swt.graphics.Color; |
| import org.eclipse.swt.graphics.Cursor; |
| import org.eclipse.swt.graphics.Font; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.layout.GridData; |
| import org.eclipse.swt.layout.GridLayout; |
| import org.eclipse.swt.layout.RowLayout; |
| import org.eclipse.swt.widgets.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.FileDialog; |
| import org.eclipse.swt.widgets.Label; |
| import org.eclipse.swt.widgets.Link; |
| import org.eclipse.swt.widgets.Listener; |
| import org.eclipse.swt.widgets.Menu; |
| import org.eclipse.swt.widgets.Spinner; |
| import org.eclipse.swt.widgets.TabFolder; |
| import org.eclipse.swt.widgets.Table; |
| import org.eclipse.swt.widgets.TableColumn; |
| import org.eclipse.swt.widgets.Text; |
| import org.eclipse.swt.widgets.Tree; |
| import org.eclipse.ui.IEditorDescriptor; |
| import org.eclipse.ui.IEditorInput; |
| import org.eclipse.ui.IEditorSite; |
| import org.eclipse.ui.ISelectionListener; |
| import org.eclipse.ui.ISharedImages; |
| import org.eclipse.ui.IStorageEditorInput; |
| import org.eclipse.ui.IWorkbenchActionConstants; |
| import org.eclipse.ui.IWorkbenchPage; |
| import org.eclipse.ui.IWorkbenchPart; |
| import org.eclipse.ui.PartInitException; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.dialogs.FilteredTree; |
| import org.eclipse.ui.fieldassist.ContentAssistCommandAdapter; |
| import org.eclipse.ui.forms.IFormColors; |
| import org.eclipse.ui.forms.IManagedForm; |
| import org.eclipse.ui.forms.editor.FormEditor; |
| import org.eclipse.ui.forms.events.ExpansionAdapter; |
| import org.eclipse.ui.forms.events.ExpansionEvent; |
| import org.eclipse.ui.forms.events.HyperlinkAdapter; |
| import org.eclipse.ui.forms.events.HyperlinkEvent; |
| import org.eclipse.ui.forms.widgets.ExpandableComposite; |
| import org.eclipse.ui.forms.widgets.FormToolkit; |
| import org.eclipse.ui.forms.widgets.Hyperlink; |
| import org.eclipse.ui.forms.widgets.ImageHyperlink; |
| import org.eclipse.ui.forms.widgets.ScrolledForm; |
| import org.eclipse.ui.forms.widgets.Section; |
| import org.eclipse.ui.internal.ObjectActionContributorManager; |
| import org.eclipse.ui.internal.WorkbenchImages; |
| import org.eclipse.ui.keys.IBindingService; |
| import org.eclipse.ui.themes.IThemeManager; |
| import org.eclipse.ui.views.contentoutline.ContentOutline; |
| import org.eclipse.ui.views.contentoutline.IContentOutlinePage; |
| |
| /** |
| * @deprecated Do not use. This class is pending for removal: see bug 237552. |
| */ |
| @Deprecated |
| public abstract class AbstractRepositoryTaskEditor extends TaskFormPage { |
| |
| private static final String PREF_SORT_ORDER_PREFIX = "org.eclipse.mylyn.editor.comments.sortDirectionUp."; |
| |
| private static final String ERROR_NOCONNECTIVITY = "Unable to submit at this time. Check connectivity and retry."; |
| |
| private static final String LABEL_HISTORY = "History"; |
| |
| private static final String LABEL_REPLY = "Reply"; |
| |
| private static final String LABEL_JOB_SUBMIT = "Submitting to repository"; |
| |
| private static final String HEADER_DATE_FORMAT = "yyyy-MM-dd HH:mm"; |
| |
| private static final String ATTACHMENT_DEFAULT_NAME = "attachment"; |
| |
| private static final String CTYPE_ZIP = "zip"; |
| |
| private static final String CTYPE_OCTET_STREAM = "octet-stream"; |
| |
| private static final String CTYPE_TEXT = "text"; |
| |
| private static final String CTYPE_HTML = "html"; |
| |
| private static final String LABEL_BROWSER = "Browser"; |
| |
| private static final String LABEL_DEFAULT_EDITOR = "Default Editor"; |
| |
| private static final String LABEL_TEXT_EDITOR = "Text Editor"; |
| |
| protected static final String CONTEXT_MENU_ID = "#MylynRepositoryEditor"; |
| |
| private FormToolkit toolkit; |
| |
| private ScrolledForm form; |
| |
| protected TaskRepository repository; |
| |
| private static final int RADIO_OPTION_WIDTH = 120; |
| |
| private static final Font TITLE_FONT = JFaceResources.getBannerFont(); |
| |
| protected static final Font TEXT_FONT = JFaceResources.getDefaultFont(); |
| |
| private static final int DESCRIPTION_WIDTH = 79 * 7; // 500; |
| |
| private static final int DESCRIPTION_HEIGHT = 10 * 14; |
| |
| protected static final int SUMMARY_HEIGHT = 20; |
| |
| private static final String LABEL_BUTTON_SUBMIT = "Submit"; |
| |
| private static final String LABEL_COPY_URL_TO_CLIPBOARD = "Copy &URL"; |
| |
| private static final String LABEL_COPY_TO_CLIPBOARD = "Copy Contents"; |
| |
| private static final String LABEL_SAVE = "Save..."; |
| |
| private static final String LABEL_SEARCH_DUPS = "Search"; |
| |
| private static final String LABEL_SELECT_DETECTOR = "Duplicate Detection"; |
| |
| private RepositoryTaskEditorInput editorInput; |
| |
| private TaskEditor parentEditor = null; |
| |
| private RepositoryTaskOutlineNode taskOutlineModel = null; |
| |
| private boolean expandedStateAttributes = false; |
| |
| protected Button submitButton; |
| |
| private Table attachmentsTable; |
| |
| private boolean refreshEnabled = true; |
| |
| private TableViewer attachmentsTableViewer; |
| |
| private final String[] attachmentsColumns = { "Name", "Description", "Type", "Size", "Creator", "Created" }; |
| |
| private final int[] attachmentsColumnWidths = { 140, 160, 100, 70, 100, 100 }; |
| |
| private Composite editorComposite; |
| |
| protected TextViewer summaryTextViewer; |
| |
| private ImageHyperlink sortHyperlink; |
| |
| private boolean commentSortIsUp = true; |
| |
| private boolean commentSortEnable = false; |
| |
| private Composite addCommentsComposite; |
| |
| /** |
| * WARNING: This is present for backward compatibility only. You can get and set text on this widget but all ui |
| * related changes to this widget will have no affect as ui is now being presented with a StyledText widget. This |
| * simply proxies get/setText calls to the StyledText widget. |
| */ |
| protected Text summaryText; |
| |
| private TextViewer newCommentTextViewer; |
| |
| private org.eclipse.swt.widgets.List ccList; |
| |
| private Section commentsSection; |
| |
| private Color colorIncoming; |
| |
| private boolean hasAttributeChanges = false; |
| |
| private boolean showAttachments = true; |
| |
| private boolean attachContextEnabled = true; |
| |
| protected Button searchForDuplicates; |
| |
| protected CCombo duplicateDetectorChooser; |
| |
| protected Label duplicateDetectorLabel; |
| |
| private boolean ignoreLocationEvents = false; |
| |
| private boolean refreshing = false; |
| |
| private TaskComment selectedComment = null; |
| |
| /** |
| * @author Raphael Ackermann (bug 195514) |
| */ |
| private class TabVerifyKeyListener implements VerifyKeyListener { |
| |
| public void verifyKey(VerifyEvent event) { |
| // if there is a tab key, do not "execute" it and instead select the Status control |
| if (event.keyCode == SWT.TAB) { |
| event.doit = false; |
| if (headerInfoComposite != null) { |
| headerInfoComposite.setFocus(); |
| } |
| } |
| } |
| |
| } |
| |
| protected enum SECTION_NAME { |
| ATTRIBTUES_SECTION("Attributes"), ATTACHMENTS_SECTION("Attachments"), DESCRIPTION_SECTION("Description"), COMMENTS_SECTION( |
| "Comments"), NEWCOMMENT_SECTION("New Comment"), ACTIONS_SECTION("Actions"), PEOPLE_SECTION("People"), RELATEDBUGS_SECTION( |
| "Related Tasks"); |
| |
| private String prettyName; |
| |
| public String getPrettyName() { |
| return prettyName; |
| } |
| |
| SECTION_NAME(String prettyName) { |
| this.prettyName = prettyName; |
| } |
| } |
| |
| private final List<IRepositoryTaskAttributeListener> attributesListeners = new ArrayList<IRepositoryTaskAttributeListener>(); |
| |
| protected RepositoryTaskData taskData; |
| |
| protected final ISelectionProvider selectionProvider = new ISelectionProvider() { |
| public void addSelectionChangedListener(ISelectionChangedListener listener) { |
| selectionChangedListeners.add(listener); |
| } |
| |
| public ISelection getSelection() { |
| RepositoryTaskSelection selection = new RepositoryTaskSelection(taskData.getTaskId(), |
| taskData.getRepositoryUrl(), taskData.getConnectorKind(), "", selectedComment, |
| taskData.getSummary()); |
| selection.setIsDescription(true); |
| return selection; |
| } |
| |
| public void removeSelectionChangedListener(ISelectionChangedListener listener) { |
| selectionChangedListeners.remove(listener); |
| } |
| |
| public void setSelection(ISelection selection) { |
| // No implementation. |
| } |
| }; |
| |
| private final ITaskListChangeListener TASKLIST_CHANGE_LISTENER = new TaskListChangeAdapter() { |
| |
| @Override |
| public void containersChanged(Set<TaskContainerDelta> containers) { |
| ITask taskToRefresh = null; |
| for (TaskContainerDelta taskContainerDelta : containers) { |
| if (!localChange && repositoryTask != null && repositoryTask.equals(taskContainerDelta.getElement())) { |
| if (taskContainerDelta.getKind().equals(TaskContainerDelta.Kind.CONTENT) |
| && !taskContainerDelta.isTransient() && !refreshing) { |
| taskToRefresh = (ITask) taskContainerDelta.getElement(); |
| break; |
| } |
| } |
| } |
| if (taskToRefresh != null) { |
| PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { |
| public void run() { |
| if (repositoryTask.getSynchronizationState() == SynchronizationState.INCOMING |
| || repositoryTask.getSynchronizationState() == SynchronizationState.CONFLICT) { |
| parentEditor.setMessage("Task has incoming changes", IMessageProvider.WARNING, |
| new HyperlinkAdapter() { |
| @Override |
| public void linkActivated(HyperlinkEvent e) { |
| refreshEditor(); |
| } |
| }); |
| setSubmitEnabled(false); |
| } else { |
| refreshEditor(); |
| } |
| } |
| }); |
| } |
| } |
| }; |
| |
| private final List<ISelectionChangedListener> selectionChangedListeners = new ArrayList<ISelectionChangedListener>(); |
| |
| private IRepositoryTaskSelection lastSelected = null; |
| |
| /** |
| * Focuses on form widgets when an item in the outline is selected. |
| */ |
| private final ISelectionListener selectionListener = new ISelectionListener() { |
| public void selectionChanged(IWorkbenchPart part, ISelection selection) { |
| if ((part instanceof ContentOutline) && (selection instanceof StructuredSelection)) { |
| Object select = ((StructuredSelection) selection).getFirstElement(); |
| if (select instanceof RepositoryTaskOutlineNode) { |
| RepositoryTaskOutlineNode n = (RepositoryTaskOutlineNode) select; |
| |
| if (lastSelected != null |
| && ContentOutlineTools.getHandle(n).equals(ContentOutlineTools.getHandle(lastSelected))) { |
| // we don't need to set the selection if it is already |
| // set |
| return; |
| } |
| lastSelected = n; |
| |
| boolean highlight = true; |
| if (n.getKey().equals(RepositoryTaskOutlineNode.LABEL_COMMENTS)) { |
| highlight = false; |
| } |
| |
| Object data = n.getData(); |
| if (n.getKey().equals(RepositoryTaskOutlineNode.LABEL_NEW_COMMENT)) { |
| selectNewComment(); |
| } else if (n.getKey().equals(RepositoryTaskOutlineNode.LABEL_DESCRIPTION) |
| && descriptionTextViewer.isEditable()) { |
| focusDescription(); |
| } else if (data != null) { |
| select(data, highlight); |
| } |
| } |
| part.setFocus(); |
| } |
| } |
| }; |
| |
| private AbstractTask repositoryTask; |
| |
| private Set<RepositoryTaskAttribute> changedAttributes; |
| |
| private Menu menu; |
| |
| private SynchronizeEditorAction synchronizeEditorAction; |
| |
| private ToggleTaskActivationAction activateAction; |
| |
| private Action historyAction; |
| |
| private Action clearOutgoingAction; |
| |
| private Action openBrowserAction; |
| |
| private NewSubTaskAction newSubTaskAction; |
| |
| private Control lastFocusControl; |
| |
| private boolean localChange = false; |
| |
| /** |
| * Call upon change to attribute value |
| * |
| * @param attribute |
| * changed attribute |
| */ |
| protected boolean attributeChanged(RepositoryTaskAttribute attribute) { |
| if (attribute == null) { |
| return false; |
| } |
| changedAttributes.add(attribute); |
| markDirty(true); |
| validateInput(); |
| return true; |
| } |
| |
| @Override |
| public void init(IEditorSite site, IEditorInput input) { |
| if (!(input instanceof RepositoryTaskEditorInput)) { |
| return; |
| } |
| |
| initTaskEditor(site, (RepositoryTaskEditorInput) input); |
| |
| if (taskData != null) { |
| editorInput.setToolTipText(taskData.getLabel()); |
| taskOutlineModel = RepositoryTaskOutlineNode.parseBugReport(taskData); |
| } |
| hasAttributeChanges = hasVisibleAttributeChanges(); |
| TasksUiInternal.getTaskList().addChangeListener(TASKLIST_CHANGE_LISTENER); |
| } |
| |
| protected void initTaskEditor(IEditorSite site, RepositoryTaskEditorInput input) { |
| changedAttributes = new HashSet<RepositoryTaskAttribute>(); |
| editorInput = input; |
| repositoryTask = editorInput.getRepositoryTask(); |
| repository = editorInput.getRepository(); |
| taskData = editorInput.getTaskData(); |
| if (repositoryTask != null) { |
| TasksUiPlugin.getTaskDataManager().setTaskRead(repositoryTask, true); |
| } |
| connector = (AbstractLegacyRepositoryConnector) TasksUi.getRepositoryManager().getRepositoryConnector( |
| repository.getConnectorKind()); |
| commentSortIsUp = TasksUiPlugin.getDefault().getPreferenceStore().getBoolean( |
| PREF_SORT_ORDER_PREFIX + repository.getConnectorKind()); |
| setSite(site); |
| setInput(input); |
| |
| isDirty = false; |
| } |
| |
| public AbstractTask getRepositoryTask() { |
| return repositoryTask; |
| } |
| |
| // @Override |
| // public void markDirty(boolean dirty) { |
| // if (repositoryTask != null) { |
| // repositoryTask.setDirty(dirty); |
| // } |
| // super.markDirty(dirty); |
| // } |
| |
| // /** |
| // * Update task state |
| // */ |
| // protected void updateTask() { |
| // if (taskData == null) |
| // return; |
| // if (repositoryTask != null) { |
| // TasksUiPlugin.getSynchronizationManager().saveOutgoing(repositoryTask, changedAttributes); |
| // } |
| // if (parentEditor != null) { |
| // parentEditor.notifyTaskChanged(); |
| // } |
| // markDirty(false); |
| // } |
| |
| protected abstract void validateInput(); |
| |
| /** |
| * Creates a new <code>AbstractTaskEditor</code>. |
| */ |
| public AbstractRepositoryTaskEditor(FormEditor editor) { |
| // set the scroll increments so the editor scrolls normally with the |
| // scroll wheel |
| super(editor, "id", "label"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| protected boolean supportsCommentSort() { |
| return false; |
| } |
| |
| @Override |
| protected void createFormContent(final IManagedForm managedForm) { |
| IThemeManager themeManager = getSite().getWorkbenchWindow().getWorkbench().getThemeManager(); |
| colorIncoming = themeManager.getCurrentTheme().getColorRegistry().get(CommonThemes.COLOR_INCOMING_BACKGROUND); |
| |
| super.createFormContent(managedForm); |
| form = managedForm.getForm(); |
| form.setRedraw(false); |
| try { |
| refreshEnabled = false; |
| |
| toolkit = managedForm.getToolkit(); |
| registerDropListener(form); |
| |
| editorComposite = form.getBody(); |
| GridLayout editorLayout = new GridLayout(); |
| editorComposite.setLayout(editorLayout); |
| editorComposite.setLayoutData(new GridData(GridData.FILL_BOTH)); |
| |
| if (taskData != null) { |
| createSections(); |
| } |
| |
| AbstractRepositoryConnectorUi connectorUi = TasksUiPlugin.getConnectorUi(repository.getConnectorKind()); |
| if (connectorUi == null) { |
| parentEditor.setMessage("The editor may not be fully loaded", IMessageProvider.INFORMATION, |
| new HyperlinkAdapter() { |
| @Override |
| public void linkActivated(HyperlinkEvent e) { |
| refreshEditor(); |
| } |
| }); |
| } |
| |
| updateHeaderControls(); |
| if (summaryTextViewer != null) { |
| summaryTextViewer.getTextWidget().setFocus(); |
| } |
| |
| form.setRedraw(true); |
| } finally { |
| refreshEnabled = true; |
| } |
| |
| form.reflow(true); |
| } |
| |
| private void updateHeaderControls() { |
| if (taskData == null) { |
| parentEditor.setMessage( |
| "Task data not available. Press synchronize button (right) to retrieve latest data.", |
| IMessageProvider.WARNING, new HyperlinkAdapter() { |
| @Override |
| public void linkActivated(HyperlinkEvent e) { |
| if (synchronizeEditorAction != null) { |
| synchronizeEditorAction.run(); |
| } |
| } |
| }); |
| } |
| getParentEditor().updateHeaderToolBar(); |
| } |
| |
| /** |
| * Override for customizing the toolbar. |
| * |
| * @since 2.1 (NOTE: likely to change for 3.0) |
| */ |
| public void fillToolBar(IToolBarManager toolBarManager) { |
| ControlContribution repositoryLabelControl = new ControlContribution("Title") { //$NON-NLS-1$ |
| @Override |
| protected Control createControl(Composite parent) { |
| Composite composite = toolkit.createComposite(parent); |
| composite.setLayout(new RowLayout()); |
| composite.setBackground(null); |
| String label = repository.getRepositoryLabel(); |
| if (label.indexOf("//") != -1) { |
| label = label.substring((repository.getRepositoryUrl().indexOf("//") + 2)); |
| } |
| |
| Hyperlink link = new Hyperlink(composite, SWT.NONE); |
| link.setText(label); |
| link.setFont(TITLE_FONT); |
| link.setForeground(toolkit.getColors().getColor(IFormColors.TITLE)); |
| link.addHyperlinkListener(new HyperlinkAdapter() { |
| |
| @Override |
| public void linkActivated(HyperlinkEvent e) { |
| TasksUiUtil.openEditRepositoryWizard(repository); |
| } |
| }); |
| |
| return composite; |
| } |
| }; |
| toolBarManager.add(repositoryLabelControl); |
| |
| if ((taskData != null && !taskData.isNew()) || repositoryTask != null) { |
| synchronizeEditorAction = new SynchronizeEditorAction(); |
| synchronizeEditorAction.selectionChanged(new StructuredSelection(this)); |
| toolBarManager.add(synchronizeEditorAction); |
| } |
| |
| if (taskData != null && !taskData.isNew()) { |
| if (repositoryTask != null) { |
| clearOutgoingAction = new ClearOutgoingAction( |
| Collections.singletonList((IRepositoryElement) repositoryTask)); |
| |
| if (clearOutgoingAction.isEnabled()) { |
| toolBarManager.add(clearOutgoingAction); |
| } |
| |
| newSubTaskAction = new NewSubTaskAction(); |
| newSubTaskAction.selectionChanged(newSubTaskAction, new StructuredSelection(getRepositoryTask())); |
| if (newSubTaskAction.isEnabled()) { |
| toolBarManager.add(newSubTaskAction); |
| } |
| } |
| |
| if (getHistoryUrl() != null) { |
| historyAction = new Action() { |
| @Override |
| public void run() { |
| TasksUiUtil.openUrl(getHistoryUrl()); |
| } |
| }; |
| |
| historyAction.setImageDescriptor(TasksUiImages.TASK_REPOSITORY_HISTORY); |
| historyAction.setToolTipText(LABEL_HISTORY); |
| toolBarManager.add(historyAction); |
| } |
| |
| if (connector != null) { |
| String taskUrl = connector.getTaskUrl(taskData.getRepositoryUrl(), taskData.getTaskKey()); |
| if (taskUrl == null && repositoryTask != null && TasksUiInternal.hasValidUrl(repositoryTask)) { |
| taskUrl = repositoryTask.getUrl(); |
| } |
| |
| final String taskUrlToOpen = taskUrl; |
| |
| if (taskUrlToOpen != null) { |
| openBrowserAction = new Action() { |
| @Override |
| public void run() { |
| TasksUiUtil.openUrl(taskUrlToOpen); |
| } |
| }; |
| |
| openBrowserAction.setImageDescriptor(CommonImages.BROWSER_OPEN_TASK); |
| openBrowserAction.setToolTipText("Open with Web Browser"); |
| toolBarManager.add(openBrowserAction); |
| } |
| } |
| } |
| } |
| |
| private void createSections() { |
| createSummaryLayout(editorComposite); |
| |
| Composite attribComp = createAttributeSection(); |
| createAttributeLayout(attribComp); |
| createCustomAttributeLayout(attribComp); |
| |
| createRelatedBugsSection(editorComposite); |
| |
| if (showAttachments) { |
| createAttachmentLayout(editorComposite); |
| } |
| createDescriptionLayout(editorComposite); |
| createCommentLayout(editorComposite); |
| createNewCommentLayout(editorComposite); |
| Composite bottomComposite = toolkit.createComposite(editorComposite); |
| bottomComposite.setLayout(new GridLayout(2, false)); |
| GridDataFactory.fillDefaults().grab(true, false).applyTo(bottomComposite); |
| |
| createActionsLayout(bottomComposite); |
| createPeopleLayout(bottomComposite); |
| bottomComposite.pack(true); |
| |
| getSite().getPage().addSelectionListener(selectionListener); |
| getSite().setSelectionProvider(selectionProvider); |
| |
| FocusListener listener = new FocusAdapter() { |
| @Override |
| public void focusGained(FocusEvent e) { |
| lastFocusControl = (Control) e.widget; |
| } |
| }; |
| addFocusListener(editorComposite, listener); |
| } |
| |
| 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 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); |
| } |
| } |
| } |
| |
| private void removeSections() { |
| menu = editorComposite.getMenu(); |
| setMenu(editorComposite, null); |
| for (Control control : editorComposite.getChildren()) { |
| control.dispose(); |
| } |
| lastFocusControl = null; |
| } |
| |
| /** |
| * @author Raphael Ackermann (modifications) (bug 195514) |
| */ |
| protected void createSummaryLayout(Composite composite) { |
| addSummaryText(composite); |
| if (summaryTextViewer != null) { |
| summaryTextViewer.prependVerifyKeyListener(new TabVerifyKeyListener()); |
| } |
| |
| headerInfoComposite = toolkit.createComposite(composite); |
| GridLayout headerLayout = new GridLayout(11, false); |
| headerLayout.verticalSpacing = 1; |
| headerLayout.marginHeight = 1; |
| headerLayout.marginHeight = 1; |
| headerLayout.marginWidth = 1; |
| headerLayout.horizontalSpacing = 6; |
| headerInfoComposite.setLayout(headerLayout); |
| |
| RepositoryTaskAttribute statusAtribute = taskData.getAttribute(RepositoryTaskAttribute.STATUS); |
| addNameValue(headerInfoComposite, statusAtribute); |
| toolkit.paintBordersFor(headerInfoComposite); |
| |
| RepositoryTaskAttribute priorityAttribute = taskData.getAttribute(RepositoryTaskAttribute.PRIORITY); |
| addNameValue(headerInfoComposite, priorityAttribute); |
| |
| String idLabel = (repositoryTask != null) ? repositoryTask.getTaskKey() : taskData.getTaskKey(); |
| if (idLabel != null) { |
| |
| Composite nameValue = toolkit.createComposite(headerInfoComposite); |
| nameValue.setLayout(new GridLayout(2, false)); |
| Label label = toolkit.createLabel(nameValue, "ID:");// .setFont(TITLE_FONT); |
| label.setForeground(toolkit.getColors().getColor(IFormColors.TITLE)); |
| // toolkit.createText(nameValue, idLabel, SWT.FLAT | SWT.READ_ONLY); |
| Text text = new Text(nameValue, SWT.FLAT | SWT.READ_ONLY); |
| toolkit.adapt(text, true, true); |
| text.setText(idLabel); |
| } |
| |
| String openedDateString = ""; |
| String modifiedDateString = ""; |
| final AbstractTaskDataHandler taskDataManager = connector.getLegacyTaskDataHandler(); |
| if (taskDataManager != null) { |
| Date created = taskData.getAttributeFactory().getDateForAttributeType( |
| RepositoryTaskAttribute.DATE_CREATION, taskData.getCreated()); |
| openedDateString = created != null ? new SimpleDateFormat(HEADER_DATE_FORMAT).format(created) : ""; |
| |
| Date modified = taskData.getAttributeFactory().getDateForAttributeType( |
| RepositoryTaskAttribute.DATE_MODIFIED, taskData.getLastModified()); |
| modifiedDateString = modified != null ? new SimpleDateFormat(HEADER_DATE_FORMAT).format(modified) : ""; |
| } |
| |
| RepositoryTaskAttribute creationAttribute = taskData.getAttribute(RepositoryTaskAttribute.DATE_CREATION); |
| if (creationAttribute != null) { |
| Composite nameValue = toolkit.createComposite(headerInfoComposite); |
| nameValue.setLayout(new GridLayout(2, false)); |
| createLabel(nameValue, creationAttribute); |
| // toolkit.createText(nameValue, openedDateString, SWT.FLAT | |
| // SWT.READ_ONLY); |
| Text text = new Text(nameValue, SWT.FLAT | SWT.READ_ONLY); |
| toolkit.adapt(text, true, true); |
| text.setText(openedDateString); |
| } |
| |
| RepositoryTaskAttribute modifiedAttribute = taskData.getAttribute(RepositoryTaskAttribute.DATE_MODIFIED); |
| if (modifiedAttribute != null) { |
| Composite nameValue = toolkit.createComposite(headerInfoComposite); |
| nameValue.setLayout(new GridLayout(2, false)); |
| createLabel(nameValue, modifiedAttribute); |
| // toolkit.createText(nameValue, modifiedDateString, SWT.FLAT | |
| // SWT.READ_ONLY); |
| Text text = new Text(nameValue, SWT.FLAT | SWT.READ_ONLY); |
| toolkit.adapt(text, true, true); |
| text.setText(modifiedDateString); |
| } |
| } |
| |
| private void addNameValue(Composite parent, RepositoryTaskAttribute attribute) { |
| Composite nameValue = toolkit.createComposite(parent); |
| nameValue.setLayout(new GridLayout(2, false)); |
| if (attribute != null) { |
| createLabel(nameValue, attribute); |
| createTextField(nameValue, attribute, SWT.FLAT | SWT.READ_ONLY); |
| } |
| } |
| |
| /** |
| * Utility method to create text field sets background to TaskListColorsAndFonts.COLOR_ATTRIBUTE_CHANGED if |
| * attribute has changed. |
| * |
| * @param composite |
| * @param attribute |
| * @param style |
| */ |
| protected Text createTextField(Composite composite, RepositoryTaskAttribute attribute, int style) { |
| String value; |
| if (attribute == null || attribute.getValue() == null) { |
| value = ""; |
| } else { |
| value = attribute.getValue(); |
| } |
| |
| final Text text; |
| if ((SWT.READ_ONLY & style) == SWT.READ_ONLY) { |
| text = new Text(composite, style); |
| toolkit.adapt(text, true, true); |
| text.setData(FormToolkit.KEY_DRAW_BORDER, Boolean.FALSE); |
| text.setText(value); |
| } else { |
| text = toolkit.createText(composite, value, style); |
| } |
| |
| if (attribute != null && !attribute.isReadOnly()) { |
| text.setData(attribute); |
| text.addModifyListener(new ModifyListener() { |
| public void modifyText(ModifyEvent e) { |
| String newValue = text.getText(); |
| RepositoryTaskAttribute attribute = (RepositoryTaskAttribute) text.getData(); |
| attribute.setValue(newValue); |
| attributeChanged(attribute); |
| } |
| }); |
| } |
| if (hasChanged(attribute)) { |
| text.setBackground(colorIncoming); |
| } |
| return text; |
| } |
| |
| protected Label createLabel(Composite composite, RepositoryTaskAttribute attribute) { |
| Label label; |
| if (hasOutgoingChange(attribute)) { |
| label = toolkit.createLabel(composite, "*" + attribute.getName()); |
| } else { |
| label = toolkit.createLabel(composite, attribute.getName()); |
| } |
| label.setForeground(toolkit.getColors().getColor(IFormColors.TITLE)); |
| GridDataFactory.fillDefaults().align(SWT.RIGHT, SWT.CENTER).applyTo(label); |
| return label; |
| } |
| |
| public String getSectionLabel(SECTION_NAME labelName) { |
| return labelName.getPrettyName(); |
| } |
| |
| protected Composite createAttributeSection() { |
| attributesSection = createSection(editorComposite, getSectionLabel(SECTION_NAME.ATTRIBTUES_SECTION), |
| expandedStateAttributes || hasAttributeChanges); |
| |
| Composite toolbarComposite = toolkit.createComposite(attributesSection); |
| toolbarComposite.setBackground(null); |
| RowLayout rowLayout = new RowLayout(); |
| rowLayout.marginTop = 0; |
| rowLayout.marginBottom = 0; |
| toolbarComposite.setLayout(rowLayout); |
| UpdateRepositoryConfigurationAction repositoryConfigRefresh = new UpdateRepositoryConfigurationAction() { |
| @Override |
| public void performUpdate(TaskRepository repository, AbstractRepositoryConnector connector, |
| IProgressMonitor monitor) { |
| PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { |
| |
| public void run() { |
| setGlobalBusy(true); |
| |
| } |
| }); |
| try { |
| super.performUpdate(repository, connector, monitor); |
| if (connector != null) { |
| TasksUiInternal.synchronizeTask(connector, repositoryTask, true, new JobChangeAdapter() { |
| |
| @Override |
| public void done(IJobChangeEvent event) { |
| PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { |
| |
| public void run() { |
| refreshEditor(); |
| } |
| }); |
| |
| } |
| }); |
| } |
| } catch (Exception e) { |
| PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { |
| |
| public void run() { |
| refreshEditor(); |
| } |
| }); |
| } |
| } |
| }; |
| repositoryConfigRefresh.setImageDescriptor(TasksUiImages.REPOSITORY_SYNCHRONIZE_SMALL); |
| repositoryConfigRefresh.selectionChanged(new StructuredSelection(repository)); |
| repositoryConfigRefresh.setToolTipText("Refresh Attributes"); |
| |
| ToolBarManager barManager = new ToolBarManager(SWT.FLAT); |
| barManager.add(repositoryConfigRefresh); |
| repositoryConfigRefresh.setEnabled(supportsRefreshAttributes()); |
| barManager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); |
| barManager.createControl(toolbarComposite); |
| attributesSection.setTextClient(toolbarComposite); |
| |
| // Attributes Composite- this holds all the combo fields and text fields |
| final Composite attribComp = toolkit.createComposite(attributesSection); |
| attribComp.addListener(SWT.MouseDown, new Listener() { |
| public void handleEvent(Event event) { |
| Control focus = event.display.getFocusControl(); |
| if (focus instanceof Text && ((Text) focus).getEditable() == false) { |
| form.setFocus(); |
| } |
| } |
| }); |
| attributesSection.setClient(attribComp); |
| |
| GridLayout attributesLayout = new GridLayout(); |
| attributesLayout.numColumns = 4; |
| attributesLayout.horizontalSpacing = 5; |
| attributesLayout.verticalSpacing = 4; |
| attribComp.setLayout(attributesLayout); |
| |
| GridData attributesData = new GridData(GridData.FILL_BOTH); |
| attributesData.horizontalSpan = 1; |
| attributesData.grabExcessVerticalSpace = false; |
| attribComp.setLayoutData(attributesData); |
| |
| return attribComp; |
| } |
| |
| /** |
| * @since 2.2 |
| */ |
| protected boolean supportsRefreshAttributes() { |
| return true; |
| } |
| |
| /** |
| * Creates the attribute section, which contains most of the basic attributes of the task (some of which are |
| * editable). |
| */ |
| protected void createAttributeLayout(Composite attributesComposite) { |
| int numColumns = ((GridLayout) attributesComposite.getLayout()).numColumns; |
| int currentCol = 1; |
| |
| for (final RepositoryTaskAttribute attribute : taskData.getAttributes()) { |
| if (attribute.isHidden()) { |
| continue; |
| } |
| |
| GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL); |
| data.horizontalSpan = 1; |
| |
| if (attribute.hasOptions() && !attribute.isReadOnly()) { |
| Label label = createLabel(attributesComposite, attribute); |
| GridDataFactory.fillDefaults().align(SWT.RIGHT, SWT.CENTER).applyTo(label); |
| final CCombo attributeCombo = new CCombo(attributesComposite, SWT.FLAT | SWT.READ_ONLY); |
| toolkit.adapt(attributeCombo, true, true); |
| attributeCombo.setFont(TEXT_FONT); |
| attributeCombo.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TEXT_BORDER); |
| if (hasChanged(attribute)) { |
| attributeCombo.setBackground(colorIncoming); |
| } |
| attributeCombo.setLayoutData(data); |
| |
| List<String> values = attribute.getOptions(); |
| if (values != null) { |
| for (String val : values) { |
| if (val != null) { |
| attributeCombo.add(val); |
| } |
| } |
| } |
| |
| String value = attribute.getValue(); |
| if (value == null) { |
| value = ""; |
| } |
| if (attributeCombo.indexOf(value) != -1) { |
| attributeCombo.select(attributeCombo.indexOf(value)); |
| } |
| attributeCombo.clearSelection(); |
| attributeCombo.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent event) { |
| if (attributeCombo.getSelectionIndex() > -1) { |
| String sel = attributeCombo.getItem(attributeCombo.getSelectionIndex()); |
| attribute.setValue(sel); |
| attributeChanged(attribute); |
| attributeCombo.clearSelection(); |
| } |
| } |
| }); |
| currentCol += 2; |
| } else { |
| Label label = createLabel(attributesComposite, attribute); |
| GridDataFactory.fillDefaults().align(SWT.RIGHT, SWT.CENTER).applyTo(label); |
| Composite textFieldComposite = toolkit.createComposite(attributesComposite); |
| GridLayout textLayout = new GridLayout(); |
| textLayout.marginWidth = 1; |
| textLayout.marginHeight = 2; |
| textFieldComposite.setLayout(textLayout); |
| GridData textData = new GridData(GridData.HORIZONTAL_ALIGN_FILL); |
| textData.horizontalSpan = 1; |
| textData.widthHint = 135; |
| |
| if (attribute.isReadOnly()) { |
| final Text text = createTextField(textFieldComposite, attribute, SWT.FLAT | SWT.READ_ONLY); |
| text.setLayoutData(textData); |
| } else { |
| final Text text = createTextField(textFieldComposite, attribute, SWT.FLAT); |
| // text.setFont(COMMENT_FONT); |
| text.setLayoutData(textData); |
| toolkit.paintBordersFor(textFieldComposite); |
| text.setData(attribute); |
| |
| if (hasContentAssist(attribute)) { |
| ContentAssistCommandAdapter adapter = applyContentAssist(text, |
| createContentProposalProvider(attribute)); |
| |
| ILabelProvider propsalLabelProvider = createProposalLabelProvider(attribute); |
| if (propsalLabelProvider != null) { |
| adapter.setLabelProvider(propsalLabelProvider); |
| } |
| adapter.setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE); |
| } |
| } |
| |
| currentCol += 2; |
| } |
| |
| if (currentCol > numColumns) { |
| currentCol -= numColumns; |
| } |
| } |
| |
| // make sure that we are in the first column |
| if (currentCol > 1) { |
| while (currentCol <= numColumns) { |
| toolkit.createLabel(attributesComposite, ""); |
| currentCol++; |
| } |
| } |
| |
| toolkit.paintBordersFor(attributesComposite); |
| } |
| |
| /** |
| * Adds a related bugs section to the bug editor |
| */ |
| protected void createRelatedBugsSection(Composite composite) { |
| // Section relatedBugsSection = createSection(editorComposite, getSectionLabel(SECTION_NAME.RELATEDBUGS_SECTION)); |
| // Composite relatedBugsComposite = toolkit.createComposite(relatedBugsSection); |
| // relatedBugsComposite.setLayout(new GridLayout(4, false)); |
| // relatedBugsComposite.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL)); |
| // relatedBugsSection.setClient(relatedBugsComposite); |
| // relatedBugsSection.setExpanded(repositoryTask == null); |
| // |
| // List<AbstractDuplicateDetector> allCollectors = new ArrayList<AbstractDuplicateDetector>(); |
| // if (getDuplicateSearchCollectorsList() != null) { |
| // allCollectors.addAll(getDuplicateSearchCollectorsList()); |
| // } |
| // if (!allCollectors.isEmpty()) { |
| // duplicateDetectorLabel = new Label(relatedBugsComposite, SWT.LEFT); |
| // duplicateDetectorLabel.setText(LABEL_SELECT_DETECTOR); |
| // |
| // duplicateDetectorChooser = new CCombo(relatedBugsComposite, SWT.FLAT | SWT.READ_ONLY | SWT.BORDER); |
| // |
| // duplicateDetectorChooser.setLayoutData(GridDataFactory.swtDefaults().hint(150, SWT.DEFAULT).create()); |
| // duplicateDetectorChooser.setFont(TEXT_FONT); |
| // |
| // Collections.sort(allCollectors, new Comparator<AbstractDuplicateDetector>() { |
| // |
| // public int compare(AbstractDuplicateDetector c1, AbstractDuplicateDetector c2) { |
| // return c1.getName().compareToIgnoreCase(c2.getName()); |
| // } |
| // |
| // }); |
| // |
| // for (AbstractDuplicateDetector detector : allCollectors) { |
| // duplicateDetectorChooser.add(detector.getName()); |
| // } |
| // |
| // duplicateDetectorChooser.select(0); |
| // duplicateDetectorChooser.setEnabled(true); |
| // duplicateDetectorChooser.setData(allCollectors); |
| // |
| // if (allCollectors.size() > 0) { |
| // |
| // searchForDuplicates = toolkit.createButton(relatedBugsComposite, LABEL_SEARCH_DUPS, SWT.NONE); |
| // GridData searchDuplicatesButtonData = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); |
| // searchForDuplicates.setLayoutData(searchDuplicatesButtonData); |
| // searchForDuplicates.addListener(SWT.Selection, new Listener() { |
| // public void handleEvent(Event e) { |
| // searchForDuplicates(); |
| // } |
| // }); |
| // } |
| // } else { |
| // Label label = new Label(relatedBugsComposite, SWT.LEFT); |
| // label.setText(LABEL_NO_DETECTOR); |
| // } |
| } |
| |
| /** |
| * @since 3.0 |
| */ |
| protected AbstractLegacyDuplicateDetector getDuplicateDetector(String name) { |
| String duplicateDetectorName = name.equals("default") ? "Stack Trace" : name; |
| Set<AbstractLegacyDuplicateDetector> allDetectors = getDuplicateDetectorList(); |
| |
| for (AbstractLegacyDuplicateDetector detector : allDetectors) { |
| if (detector.getName().equals(duplicateDetectorName)) { |
| return detector; |
| } |
| } |
| // didn't find it |
| return null; |
| } |
| |
| /** |
| * @since 3.0 |
| */ |
| protected Set<AbstractLegacyDuplicateDetector> getDuplicateDetectorList() { |
| Set<AbstractLegacyDuplicateDetector> duplicateDetectors = new HashSet<AbstractLegacyDuplicateDetector>(); |
| for (AbstractDuplicateDetector abstractDuplicateDetector : TasksUiPlugin.getDefault() |
| .getDuplicateSearchCollectorsList()) { |
| if (abstractDuplicateDetector instanceof AbstractLegacyDuplicateDetector |
| && (abstractDuplicateDetector.getConnectorKind() == null || abstractDuplicateDetector.getConnectorKind() |
| .equals(getConnector().getConnectorKind()))) { |
| duplicateDetectors.add((AbstractLegacyDuplicateDetector) abstractDuplicateDetector); |
| } |
| } |
| return duplicateDetectors; |
| } |
| |
| public boolean searchForDuplicates() { |
| String duplicateDetectorName = duplicateDetectorChooser.getItem(duplicateDetectorChooser.getSelectionIndex()); |
| AbstractLegacyDuplicateDetector duplicateDetector = getDuplicateDetector(duplicateDetectorName); |
| if (duplicateDetector != null) { |
| RepositoryQuery duplicatesQuery = duplicateDetector.getDuplicatesQuery(repository, taskData); |
| if (duplicatesQuery != null) { |
| SearchHitCollector collector = new SearchHitCollector(TasksUiInternal.getTaskList(), repository, |
| duplicatesQuery); |
| NewSearchUI.runQueryInBackground(collector); |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Adds content assist to the given text field. |
| * |
| * @param text |
| * text field to decorate. |
| * @param proposalProvider |
| * instance providing content proposals |
| * @return the ContentAssistCommandAdapter for the field. |
| */ |
| protected ContentAssistCommandAdapter applyContentAssist(Text text, IContentProposalProvider proposalProvider) { |
| ControlDecoration controlDecoration = new ControlDecoration(text, (SWT.TOP | SWT.LEFT)); |
| controlDecoration.setMarginWidth(0); |
| controlDecoration.setShowHover(true); |
| controlDecoration.setShowOnlyOnFocus(true); |
| |
| FieldDecoration contentProposalImage = FieldDecorationRegistry.getDefault().getFieldDecoration( |
| FieldDecorationRegistry.DEC_CONTENT_PROPOSAL); |
| controlDecoration.setImage(contentProposalImage.getImage()); |
| |
| TextContentAdapter textContentAdapter = new TextContentAdapter(); |
| |
| ContentAssistCommandAdapter adapter = new ContentAssistCommandAdapter(text, textContentAdapter, |
| proposalProvider, "org.eclipse.ui.edit.text.contentAssist.proposals", new char[0]); |
| |
| IBindingService bindingService = (IBindingService) PlatformUI.getWorkbench().getService(IBindingService.class); |
| controlDecoration.setDescriptionText(NLS.bind("Content Assist Available ({0})", |
| bindingService.getBestActiveBindingFormattedFor(adapter.getCommandId()))); |
| |
| return adapter; |
| } |
| |
| /** |
| * Creates an IContentProposalProvider to provide content assist proposals for the given attribute. |
| * |
| * @param attribute |
| * attribute for which to provide content assist. |
| * @return the IContentProposalProvider. |
| */ |
| protected IContentProposalProvider createContentProposalProvider(RepositoryTaskAttribute attribute) { |
| return new PersonProposalProvider(repositoryTask, taskData); |
| } |
| |
| /** |
| * Creates an IContentProposalProvider to provide content assist proposals for the given operation. |
| * |
| * @param operation |
| * operation for which to provide content assist. |
| * @return the IContentProposalProvider. |
| */ |
| protected IContentProposalProvider createContentProposalProvider(RepositoryOperation operation) { |
| |
| return new PersonProposalProvider(repositoryTask, taskData); |
| } |
| |
| protected ILabelProvider createProposalLabelProvider(RepositoryTaskAttribute attribute) { |
| return new PersonProposalLabelProvider(); |
| } |
| |
| protected ILabelProvider createProposalLabelProvider(RepositoryOperation operation) { |
| |
| return new PersonProposalLabelProvider(); |
| } |
| |
| /** |
| * Called to check if there's content assist available for the given attribute. |
| * |
| * @param attribute |
| * the attribute |
| * @return true if content assist is available for the specified attribute. |
| */ |
| protected boolean hasContentAssist(RepositoryTaskAttribute attribute) { |
| return false; |
| } |
| |
| /** |
| * Called to check if there's content assist available for the given operation. |
| * |
| * @param operation |
| * the operation |
| * @return true if content assist is available for the specified operation. |
| */ |
| protected boolean hasContentAssist(RepositoryOperation operation) { |
| return false; |
| } |
| |
| /** |
| * Adds a text editor with spell checking enabled to display and edit the task's summary. |
| * |
| * @author Raphael Ackermann (modifications) (bug 195514) |
| * @param attributesComposite |
| * The composite to add the text editor to. |
| */ |
| protected void addSummaryText(Composite attributesComposite) { |
| Composite summaryComposite = toolkit.createComposite(attributesComposite); |
| GridLayout summaryLayout = new GridLayout(2, false); |
| summaryLayout.verticalSpacing = 0; |
| summaryLayout.marginHeight = 2; |
| summaryComposite.setLayout(summaryLayout); |
| GridDataFactory.fillDefaults().grab(true, false).applyTo(summaryComposite); |
| |
| if (taskData != null) { |
| RepositoryTaskAttribute attribute = taskData.getAttribute(RepositoryTaskAttribute.SUMMARY); |
| if (attribute == null) { |
| taskData.setAttributeValue(RepositoryTaskAttribute.SUMMARY, ""); |
| attribute = taskData.getAttribute(RepositoryTaskAttribute.SUMMARY); |
| } |
| |
| final RepositoryTaskAttribute summaryAttribute = attribute; |
| |
| summaryTextViewer = addTextEditor(repository, summaryComposite, attribute.getValue(), true, SWT.FLAT |
| | SWT.SINGLE); |
| Composite hiddenComposite = new Composite(summaryComposite, SWT.NONE); |
| hiddenComposite.setLayout(new GridLayout()); |
| GridData hiddenLayout = new GridData(); |
| hiddenLayout.exclude = true; |
| hiddenComposite.setLayoutData(hiddenLayout); |
| |
| // bugg#210695 - work around for 2.0 api breakage |
| summaryText = new Text(hiddenComposite, SWT.NONE); |
| summaryText.setText(attribute.getValue()); |
| summaryText.addKeyListener(new KeyListener() { |
| |
| public void keyPressed(KeyEvent e) { |
| if (summaryTextViewer != null && !summaryTextViewer.getTextWidget().isDisposed()) { |
| String newValue = summaryText.getText(); |
| String oldValue = summaryTextViewer.getTextWidget().getText(); |
| if (!newValue.equals(oldValue)) { |
| summaryTextViewer.getTextWidget().setText(newValue); |
| summaryAttribute.setValue(newValue); |
| attributeChanged(summaryAttribute); |
| } |
| } |
| } |
| |
| public void keyReleased(KeyEvent e) { |
| // ignore |
| } |
| }); |
| |
| summaryText.addFocusListener(new FocusListener() { |
| |
| public void focusGained(FocusEvent e) { |
| summaryTextViewer.getTextWidget().setFocus(); |
| } |
| |
| public void focusLost(FocusEvent e) { |
| // ignore |
| } |
| }); |
| |
| summaryTextViewer.setEditable(true); |
| summaryTextViewer.getTextWidget().setIndent(2); |
| GridDataFactory.fillDefaults().grab(true, false).applyTo(summaryTextViewer.getControl()); |
| |
| if (hasChanged(attribute)) { |
| summaryTextViewer.getTextWidget().setBackground(colorIncoming); |
| } |
| summaryTextViewer.getControl().setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TEXT_BORDER); |
| |
| summaryTextViewer.addTextListener(new ITextListener() { |
| public void textChanged(TextEvent event) { |
| String newValue = summaryTextViewer.getTextWidget().getText(); |
| if (!newValue.equals(summaryAttribute.getValue())) { |
| summaryAttribute.setValue(newValue); |
| attributeChanged(summaryAttribute); |
| } |
| if (summaryText != null && !newValue.equals(summaryText.getText())) { |
| summaryText.setText(newValue); |
| } |
| } |
| }); |
| |
| } |
| toolkit.paintBordersFor(summaryComposite); |
| } |
| |
| protected boolean supportsAttachmentDelete() { |
| return false; |
| } |
| |
| protected void deleteAttachment(RepositoryAttachment attachment) { |
| |
| } |
| |
| protected void createAttachmentLayout(Composite composite) { |
| // TODO: expand to show new attachments |
| Section section = createSection(composite, getSectionLabel(SECTION_NAME.ATTACHMENTS_SECTION), false); |
| section.setText(section.getText() + " (" + taskData.getAttachments().size() + ")"); |
| final Composite attachmentsComposite = toolkit.createComposite(section); |
| attachmentsComposite.setLayout(new GridLayout(1, false)); |
| attachmentsComposite.setLayoutData(new GridData(GridData.FILL_BOTH)); |
| section.setClient(attachmentsComposite); |
| |
| if (taskData.getAttachments().size() > 0) { |
| |
| attachmentsTable = toolkit.createTable(attachmentsComposite, SWT.MULTI | SWT.BORDER | SWT.FULL_SELECTION); |
| attachmentsTable.setLinesVisible(true); |
| attachmentsTable.setHeaderVisible(true); |
| attachmentsTable.setLayout(new GridLayout()); |
| GridData tableGridData = new GridData(SWT.FILL, SWT.FILL, true, true); |
| attachmentsTable.setLayoutData(tableGridData); |
| |
| for (int i = 0; i < attachmentsColumns.length; i++) { |
| TableColumn column = new TableColumn(attachmentsTable, SWT.LEFT, i); |
| column.setText(attachmentsColumns[i]); |
| column.setWidth(attachmentsColumnWidths[i]); |
| } |
| attachmentsTable.getColumn(3).setAlignment(SWT.RIGHT); |
| |
| attachmentsTableViewer = new TableViewer(attachmentsTable); |
| attachmentsTableViewer.setUseHashlookup(true); |
| attachmentsTableViewer.setColumnProperties(attachmentsColumns); |
| ColumnViewerToolTipSupport.enableFor(attachmentsTableViewer, ToolTip.NO_RECREATE); |
| |
| final AbstractTaskDataHandler offlineHandler = connector.getLegacyTaskDataHandler(); |
| if (offlineHandler != null) { |
| attachmentsTableViewer.setSorter(new ViewerSorter() { |
| @Override |
| public int compare(Viewer viewer, Object e1, Object e2) { |
| RepositoryAttachment attachment1 = (RepositoryAttachment) e1; |
| RepositoryAttachment attachment2 = (RepositoryAttachment) e2; |
| Date created1 = taskData.getAttributeFactory().getDateForAttributeType( |
| RepositoryTaskAttribute.ATTACHMENT_DATE, attachment1.getDateCreated()); |
| Date created2 = taskData.getAttributeFactory().getDateForAttributeType( |
| RepositoryTaskAttribute.ATTACHMENT_DATE, attachment2.getDateCreated()); |
| if (created1 != null && created2 != null) { |
| return created1.compareTo(created2); |
| } else if (created1 == null && created2 != null) { |
| return -1; |
| } else if (created1 != null && created2 == null) { |
| return 1; |
| } else { |
| return 0; |
| } |
| } |
| }); |
| } |
| |
| attachmentsTableViewer.setContentProvider(new AttachmentsTableContentProvider(taskData.getAttachments())); |
| |
| attachmentsTableViewer.setLabelProvider(new AttachmentTableLabelProvider(this, new LabelProvider(), |
| PlatformUI.getWorkbench().getDecoratorManager().getLabelDecorator())); |
| |
| attachmentsTableViewer.addDoubleClickListener(new IDoubleClickListener() { |
| public void doubleClick(DoubleClickEvent event) { |
| if (!event.getSelection().isEmpty()) { |
| StructuredSelection selection = (StructuredSelection) event.getSelection(); |
| RepositoryAttachment attachment = (RepositoryAttachment) selection.getFirstElement(); |
| TasksUiUtil.openUrl(attachment.getUrl()); |
| } |
| } |
| }); |
| |
| attachmentsTableViewer.setInput(taskData); |
| |
| final Action openWithBrowserAction = new Action(LABEL_BROWSER) { |
| @Override |
| public void run() { |
| RepositoryAttachment attachment = (RepositoryAttachment) (((StructuredSelection) attachmentsTableViewer.getSelection()).getFirstElement()); |
| if (attachment != null) { |
| TasksUiUtil.openUrl(attachment.getUrl()); |
| } |
| } |
| }; |
| |
| final Action openWithDefaultAction = new Action(LABEL_DEFAULT_EDITOR) { |
| @Override |
| public void run() { |
| // browser shortcut |
| RepositoryAttachment attachment = (RepositoryAttachment) (((StructuredSelection) attachmentsTableViewer.getSelection()).getFirstElement()); |
| if (attachment == null) { |
| return; |
| } |
| |
| if (attachment.getContentType().endsWith(CTYPE_HTML)) { |
| TasksUiUtil.openUrl(attachment.getUrl()); |
| return; |
| } |
| |
| IStorageEditorInput input = new RepositoryAttachmentEditorInput(repository, attachment); |
| IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); |
| if (page == null) { |
| return; |
| } |
| IEditorDescriptor desc = PlatformUI.getWorkbench().getEditorRegistry().getDefaultEditor( |
| input.getName()); |
| try { |
| page.openEditor(input, desc.getId()); |
| } catch (PartInitException e) { |
| StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN, |
| "Unable to open editor for: " + attachment.getDescription(), e)); |
| } |
| } |
| }; |
| |
| final Action openWithTextEditorAction = new Action(LABEL_TEXT_EDITOR) { |
| @Override |
| public void run() { |
| RepositoryAttachment attachment = (RepositoryAttachment) (((StructuredSelection) attachmentsTableViewer.getSelection()).getFirstElement()); |
| IStorageEditorInput input = new RepositoryAttachmentEditorInput(repository, attachment); |
| IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); |
| if (page == null) { |
| return; |
| } |
| |
| try { |
| page.openEditor(input, "org.eclipse.ui.DefaultTextEditor"); |
| } catch (PartInitException e) { |
| StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN, |
| "Unable to open editor for: " + attachment.getDescription(), e)); |
| } |
| } |
| }; |
| |
| final Action saveAction = new Action(LABEL_SAVE) { |
| @Override |
| public void run() { |
| ITaskAttachment attachment = (ITaskAttachment) (((StructuredSelection) attachmentsTableViewer.getSelection()).getFirstElement()); |
| /* Launch Browser */ |
| FileDialog fileChooser = new FileDialog(attachmentsTable.getShell(), SWT.SAVE); |
| String fname = attachment.getFileName(); |
| // Default name if none is found |
| if (fname.equals("")) { |
| String ctype = attachment.getContentType(); |
| if (ctype.endsWith(CTYPE_HTML)) { |
| fname = ATTACHMENT_DEFAULT_NAME + ".html"; |
| } else if (ctype.startsWith(CTYPE_TEXT)) { |
| fname = ATTACHMENT_DEFAULT_NAME + ".txt"; |
| } else if (ctype.endsWith(CTYPE_OCTET_STREAM)) { |
| fname = ATTACHMENT_DEFAULT_NAME; |
| } else if (ctype.endsWith(CTYPE_ZIP)) { |
| fname = ATTACHMENT_DEFAULT_NAME + "." + CTYPE_ZIP; |
| } else { |
| fname = ATTACHMENT_DEFAULT_NAME + "." + ctype.substring(ctype.indexOf("/") + 1); |
| } |
| } |
| fileChooser.setFileName(fname); |
| String filePath = fileChooser.open(); |
| // Check if the dialog was canceled or an error occurred |
| if (filePath == null) { |
| return; |
| } |
| |
| DownloadAttachmentJob job = new DownloadAttachmentJob(attachment, new File(filePath)); |
| job.setUser(true); |
| job.schedule(); |
| } |
| }; |
| |
| final Action copyURLToClipAction = new Action(LABEL_COPY_URL_TO_CLIPBOARD) { |
| @Override |
| public void run() { |
| RepositoryAttachment attachment = (RepositoryAttachment) (((StructuredSelection) attachmentsTableViewer.getSelection()).getFirstElement()); |
| Clipboard clip = new Clipboard(PlatformUI.getWorkbench().getDisplay()); |
| clip.setContents(new Object[] { attachment.getUrl() }, |
| new Transfer[] { TextTransfer.getInstance() }); |
| clip.dispose(); |
| } |
| }; |
| |
| final Action copyToClipAction = new Action(LABEL_COPY_TO_CLIPBOARD) { |
| @Override |
| public void run() { |
| RepositoryAttachment attachment = (RepositoryAttachment) (((StructuredSelection) attachmentsTableViewer.getSelection()).getFirstElement()); |
| CopyAttachmentToClipboardJob job = new CopyAttachmentToClipboardJob(attachment); |
| job.setUser(true); |
| job.schedule(); |
| } |
| }; |
| |
| final MenuManager popupMenu = new MenuManager(); |
| final Menu menu = popupMenu.createContextMenu(attachmentsTable); |
| attachmentsTable.setMenu(menu); |
| final MenuManager openMenu = new MenuManager("Open With"); |
| popupMenu.addMenuListener(new IMenuListener() { |
| public void menuAboutToShow(IMenuManager manager) { |
| popupMenu.removeAll(); |
| |
| IStructuredSelection selection = (IStructuredSelection) attachmentsTableViewer.getSelection(); |
| if (selection.isEmpty()) { |
| return; |
| } |
| |
| if (selection.size() == 1) { |
| RepositoryAttachment att = (RepositoryAttachment) ((StructuredSelection) selection).getFirstElement(); |
| |
| // reinitialize open menu |
| popupMenu.add(openMenu); |
| openMenu.removeAll(); |
| |
| IStorageEditorInput input = new RepositoryAttachmentEditorInput(repository, att); |
| IEditorDescriptor desc = PlatformUI.getWorkbench().getEditorRegistry().getDefaultEditor( |
| input.getName()); |
| if (desc != null) { |
| openMenu.add(openWithDefaultAction); |
| } |
| openMenu.add(openWithBrowserAction); |
| openMenu.add(openWithTextEditorAction); |
| |
| popupMenu.add(new Separator()); |
| popupMenu.add(saveAction); |
| |
| popupMenu.add(copyURLToClipAction); |
| if (att.getContentType().startsWith(CTYPE_TEXT) || att.getContentType().endsWith("xml")) { |
| popupMenu.add(copyToClipAction); |
| } |
| } |
| popupMenu.add(new Separator("actions")); |
| |
| // TODO: use workbench mechanism for this? |
| ObjectActionContributorManager.getManager().contributeObjectActions( |
| AbstractRepositoryTaskEditor.this, popupMenu, attachmentsTableViewer); |
| } |
| }); |
| } else { |
| Label label = toolkit.createLabel(attachmentsComposite, "No attachments"); |
| registerDropListener(label); |
| } |
| |
| final Composite attachmentControlsComposite = toolkit.createComposite(attachmentsComposite); |
| attachmentControlsComposite.setLayout(new GridLayout(2, false)); |
| attachmentControlsComposite.setLayoutData(new GridData(GridData.BEGINNING)); |
| |
| Button attachFileButton = toolkit.createButton(attachmentControlsComposite, AttachAction.LABEL, SWT.PUSH); |
| attachFileButton.setImage(WorkbenchImages.getImage(ISharedImages.IMG_OBJ_FILE)); |
| |
| Button attachScreenshotButton = toolkit.createButton(attachmentControlsComposite, AttachScreenshotAction.LABEL, |
| SWT.PUSH); |
| attachScreenshotButton.setImage(CommonImages.getImage(CommonImages.IMAGE_CAPTURE)); |
| |
| final ITask task = TasksUiInternal.getTaskList().getTask(repository.getRepositoryUrl(), taskData.getTaskId()); |
| if (task == null) { |
| attachFileButton.setEnabled(false); |
| attachScreenshotButton.setEnabled(false); |
| } |
| |
| attachFileButton.addSelectionListener(new SelectionListener() { |
| public void widgetDefaultSelected(SelectionEvent e) { |
| // ignore |
| } |
| |
| public void widgetSelected(SelectionEvent e) { |
| AbstractTaskEditorAction attachFileAction = new AttachAction(); |
| attachFileAction.selectionChanged(new StructuredSelection(task)); |
| attachFileAction.setEditor(parentEditor); |
| attachFileAction.run(); |
| } |
| }); |
| |
| attachScreenshotButton.addSelectionListener(new SelectionListener() { |
| public void widgetDefaultSelected(SelectionEvent e) { |
| // ignore |
| } |
| |
| public void widgetSelected(SelectionEvent e) { |
| AttachScreenshotAction attachScreenshotAction = new AttachScreenshotAction(); |
| attachScreenshotAction.selectionChanged(new StructuredSelection(task)); |
| attachScreenshotAction.setEditor(parentEditor); |
| attachScreenshotAction.run(); |
| } |
| }); |
| |
| Button deleteAttachmentButton = null; |
| if (supportsAttachmentDelete()) { |
| deleteAttachmentButton = toolkit.createButton(attachmentControlsComposite, "Delete Attachment...", SWT.PUSH); |
| |
| deleteAttachmentButton.addSelectionListener(new SelectionListener() { |
| public void widgetDefaultSelected(SelectionEvent e) { |
| // ignore |
| } |
| |
| public void widgetSelected(SelectionEvent e) { |
| ITask task = TasksUiInternal.getTaskList().getTask(repository.getRepositoryUrl(), |
| taskData.getTaskId()); |
| if (task == null) { |
| // Should not happen |
| return; |
| } |
| if (AbstractRepositoryTaskEditor.this.isDirty |
| || task.getSynchronizationState().equals(SynchronizationState.OUTGOING)) { |
| MessageDialog.openInformation(attachmentsComposite.getShell(), |
| "Task not synchronized or dirty editor", |
| "Commit edits or synchronize task before deleting attachments."); |
| return; |
| } else { |
| if (attachmentsTableViewer != null |
| && attachmentsTableViewer.getSelection() != null |
| && ((StructuredSelection) attachmentsTableViewer.getSelection()).getFirstElement() != null) { |
| RepositoryAttachment attachment = (RepositoryAttachment) (((StructuredSelection) attachmentsTableViewer.getSelection()).getFirstElement()); |
| deleteAttachment(attachment); |
| submitToRepository(); |
| } |
| } |
| } |
| }); |
| |
| } |
| registerDropListener(section); |
| registerDropListener(attachmentsComposite); |
| registerDropListener(attachFileButton); |
| if (supportsAttachmentDelete()) { |
| registerDropListener(deleteAttachmentButton); |
| } |
| } |
| |
| private void registerDropListener(final Control control) { |
| 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); |
| |
| // Adapted from eclipse.org DND Article by Veronika Irvine, IBM OTI Labs |
| // http://www.eclipse.org/articles/Article-SWT-DND/DND-in-SWT.html#_dt10D |
| target.addDropListener(new RepositoryTaskEditorDropListener(this, repository, taskData, fileTransfer, |
| textTransfer, control)); |
| } |
| |
| protected void createDescriptionLayout(Composite composite) { |
| Section descriptionSection = createSection(composite, getSectionLabel(SECTION_NAME.DESCRIPTION_SECTION)); |
| final Composite sectionComposite = toolkit.createComposite(descriptionSection); |
| descriptionSection.setClient(sectionComposite); |
| GridLayout addCommentsLayout = new GridLayout(); |
| addCommentsLayout.numColumns = 1; |
| sectionComposite.setLayout(addCommentsLayout); |
| |
| RepositoryTaskAttribute attribute = taskData.getDescriptionAttribute(); |
| if (attribute != null && !attribute.isReadOnly()) { |
| if (getRenderingEngine() != null) { |
| // composite with StackLayout to hold text editor and preview widget |
| Composite descriptionComposite = toolkit.createComposite(sectionComposite); |
| descriptionComposite.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TEXT_BORDER); |
| GridData descriptionGridData = new GridData(GridData.FILL_BOTH); |
| descriptionGridData.widthHint = DESCRIPTION_WIDTH; |
| descriptionGridData.minimumHeight = DESCRIPTION_HEIGHT; |
| descriptionGridData.grabExcessHorizontalSpace = true; |
| descriptionComposite.setLayoutData(descriptionGridData); |
| final StackLayout descriptionLayout = new StackLayout(); |
| descriptionComposite.setLayout(descriptionLayout); |
| |
| descriptionTextViewer = addTextEditor(repository, descriptionComposite, taskData.getDescription(), |
| true, SWT.FLAT | SWT.MULTI | SWT.WRAP | SWT.V_SCROLL); |
| descriptionLayout.topControl = descriptionTextViewer.getControl(); |
| descriptionComposite.layout(); |
| |
| // composite for edit/preview button |
| Composite buttonComposite = toolkit.createComposite(sectionComposite); |
| buttonComposite.setLayout(new GridLayout()); |
| createPreviewButton(buttonComposite, descriptionTextViewer, descriptionComposite, descriptionLayout); |
| } else { |
| descriptionTextViewer = addTextEditor(repository, sectionComposite, taskData.getDescription(), true, |
| SWT.FLAT | SWT.MULTI | SWT.WRAP | SWT.V_SCROLL); |
| final GridData gd = new GridData(GridData.FILL_HORIZONTAL); |
| // wrap text at this margin, see comment below |
| gd.widthHint = DESCRIPTION_WIDTH; |
| gd.minimumHeight = DESCRIPTION_HEIGHT; |
| gd.grabExcessHorizontalSpace = true; |
| descriptionTextViewer.getControl().setLayoutData(gd); |
| descriptionTextViewer.getControl().setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TEXT_BORDER); |
| // the goal is to make the text viewer as big as the text so it does not require scrolling when first drawn |
| // on screen: when the descriptionTextViewer calculates its height it wraps the text according to the widthHint |
| // which does not reflect the actual size of the widget causing the widget to be taller |
| // (actual width > gd.widhtHint) or shorter (actual width < gd.widthHint) therefore the widthHint is tweaked |
| // once in the listener |
| sectionComposite.addControlListener(new ControlAdapter() { |
| private boolean first; |
| |
| @Override |
| public void controlResized(ControlEvent e) { |
| if (!first) { |
| first = true; |
| int width = sectionComposite.getSize().x; |
| Point size = descriptionTextViewer.getTextWidget().computeSize(width, SWT.DEFAULT, true); |
| // limit width to parent widget |
| gd.widthHint = width; |
| // limit height to avoid dynamic resizing of the text widget |
| gd.heightHint = Math.min(Math.max(DESCRIPTION_HEIGHT, size.y), DESCRIPTION_HEIGHT * 4); |
| sectionComposite.layout(); |
| } |
| } |
| }); |
| } |
| descriptionTextViewer.setEditable(true); |
| descriptionTextViewer.addTextListener(new ITextListener() { |
| public void textChanged(TextEvent event) { |
| String newValue = descriptionTextViewer.getTextWidget().getText(); |
| RepositoryTaskAttribute attribute = taskData.getAttribute(RepositoryTaskAttribute.DESCRIPTION); |
| if (attribute != null && !newValue.equals(attribute.getValue())) { |
| attribute.setValue(newValue); |
| attributeChanged(attribute); |
| taskData.setDescription(newValue); |
| } |
| } |
| }); |
| StyledText styledText = descriptionTextViewer.getTextWidget(); |
| controlBySelectableObject.put(taskData.getDescription(), styledText); |
| } else { |
| String text = taskData.getDescription(); |
| descriptionTextViewer = addTextViewer(repository, sectionComposite, text, SWT.MULTI | SWT.WRAP); |
| StyledText styledText = descriptionTextViewer.getTextWidget(); |
| GridDataFactory.fillDefaults().hint(DESCRIPTION_WIDTH, SWT.DEFAULT).applyTo( |
| descriptionTextViewer.getControl()); |
| |
| controlBySelectableObject.put(text, styledText); |
| } |
| |
| if (hasChanged(taskData.getAttribute(RepositoryTaskAttribute.DESCRIPTION))) { |
| descriptionTextViewer.getTextWidget().setBackground(colorIncoming); |
| } |
| descriptionTextViewer.getTextWidget().addListener(SWT.FocusIn, new DescriptionListener()); |
| |
| Composite replyComp = toolkit.createComposite(descriptionSection); |
| replyComp.setLayout(new RowLayout()); |
| replyComp.setBackground(null); |
| |
| createReplyHyperlink(0, replyComp, taskData.getDescription()); |
| descriptionSection.setTextClient(replyComp); |
| addDuplicateDetection(sectionComposite); |
| toolkit.paintBordersFor(sectionComposite); |
| |
| if (descriptionTextViewer.isEditable()) { |
| descriptionSection.setExpanded(true); |
| } |
| } |
| |
| protected ImageHyperlink createReplyHyperlink(final int commentNum, Composite composite, final String commentBody) { |
| final ImageHyperlink replyLink = new ImageHyperlink(composite, SWT.NULL); |
| toolkit.adapt(replyLink, true, true); |
| replyLink.setImage(CommonImages.getImage(TasksUiImages.COMMENT_REPLY)); |
| replyLink.setToolTipText(LABEL_REPLY); |
| // no need for the background - transparency will take care of it |
| replyLink.setBackground(null); |
| // replyLink.setBackground(section.getTitleBarGradientBackground()); |
| replyLink.addHyperlinkListener(new HyperlinkAdapter() { |
| @Override |
| public void linkActivated(HyperlinkEvent e) { |
| String oldText = newCommentTextViewer.getDocument().get(); |
| StringBuilder strBuilder = new StringBuilder(); |
| strBuilder.append(oldText); |
| if (strBuilder.length() != 0) { |
| strBuilder.append("\n"); |
| } |
| strBuilder.append(" (In reply to comment #" + commentNum + ")\n"); |
| CommentQuoter quoter = new CommentQuoter(); |
| strBuilder.append(quoter.quote(commentBody)); |
| newCommentTextViewer.getDocument().set(strBuilder.toString()); |
| RepositoryTaskAttribute attribute = taskData.getAttribute(RepositoryTaskAttribute.COMMENT_NEW); |
| if (attribute != null) { |
| attribute.setValue(strBuilder.toString()); |
| attributeChanged(attribute); |
| } |
| selectNewComment(); |
| newCommentTextViewer.getTextWidget().setCaretOffset(strBuilder.length()); |
| } |
| }); |
| |
| return replyLink; |
| } |
| |
| protected void addDuplicateDetection(Composite composite) { |
| List<AbstractLegacyDuplicateDetector> allCollectors = new ArrayList<AbstractLegacyDuplicateDetector>(); |
| if (getDuplicateDetectorList() != null) { |
| allCollectors.addAll(getDuplicateDetectorList()); |
| } |
| if (!allCollectors.isEmpty()) { |
| Section duplicatesSection = toolkit.createSection(composite, ExpandableComposite.TWISTIE |
| | ExpandableComposite.SHORT_TITLE_BAR); |
| duplicatesSection.setText(LABEL_SELECT_DETECTOR); |
| duplicatesSection.setLayout(new GridLayout()); |
| GridDataFactory.fillDefaults().indent(SWT.DEFAULT, 15).applyTo(duplicatesSection); |
| Composite relatedBugsComposite = toolkit.createComposite(duplicatesSection); |
| relatedBugsComposite.setLayout(new GridLayout(4, false)); |
| relatedBugsComposite.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL)); |
| duplicatesSection.setClient(relatedBugsComposite); |
| duplicateDetectorLabel = new Label(relatedBugsComposite, SWT.LEFT); |
| duplicateDetectorLabel.setText("Detector:"); |
| |
| duplicateDetectorChooser = new CCombo(relatedBugsComposite, SWT.FLAT | SWT.READ_ONLY); |
| toolkit.adapt(duplicateDetectorChooser, true, true); |
| duplicateDetectorChooser.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TEXT_BORDER); |
| duplicateDetectorChooser.setFont(TEXT_FONT); |
| duplicateDetectorChooser.setLayoutData(GridDataFactory.swtDefaults().hint(150, SWT.DEFAULT).create()); |
| |
| Collections.sort(allCollectors, new Comparator<AbstractLegacyDuplicateDetector>() { |
| |
| public int compare(AbstractLegacyDuplicateDetector c1, AbstractLegacyDuplicateDetector c2) { |
| return c1.getName().compareToIgnoreCase(c2.getName()); |
| } |
| |
| }); |
| |
| for (AbstractLegacyDuplicateDetector detector : allCollectors) { |
| duplicateDetectorChooser.add(detector.getName()); |
| } |
| |
| duplicateDetectorChooser.select(0); |
| duplicateDetectorChooser.setEnabled(true); |
| duplicateDetectorChooser.setData(allCollectors); |
| |
| if (allCollectors.size() > 0) { |
| |
| searchForDuplicates = toolkit.createButton(relatedBugsComposite, LABEL_SEARCH_DUPS, SWT.NONE); |
| GridData searchDuplicatesButtonData = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); |
| searchForDuplicates.setLayoutData(searchDuplicatesButtonData); |
| searchForDuplicates.addListener(SWT.Selection, new Listener() { |
| public void handleEvent(Event e) { |
| searchForDuplicates(); |
| } |
| }); |
| } |
| // } else { |
| // Label label = new Label(composite, SWT.LEFT); |
| // label.setText(LABEL_NO_DETECTOR); |
| |
| toolkit.paintBordersFor(relatedBugsComposite); |
| |
| } |
| |
| } |
| |
| protected void createCustomAttributeLayout(Composite composite) { |
| // override |
| } |
| |
| protected void createPeopleLayout(Composite composite) { |
| Section peopleSection = createSection(composite, getSectionLabel(SECTION_NAME.PEOPLE_SECTION)); |
| GridDataFactory.fillDefaults().align(SWT.FILL, SWT.TOP).grab(true, true).applyTo(peopleSection); |
| Composite peopleComposite = toolkit.createComposite(peopleSection); |
| GridLayout layout = new GridLayout(2, false); |
| layout.marginWidth = 5; |
| peopleComposite.setLayout(layout); |
| |
| addAssignedTo(peopleComposite); |
| boolean haveRealName = false; |
| RepositoryTaskAttribute reporterAttribute = taskData.getAttribute(RepositoryTaskAttribute.USER_REPORTER_NAME); |
| if (reporterAttribute == null) { |
| reporterAttribute = taskData.getAttribute(RepositoryTaskAttribute.USER_REPORTER); |
| } else { |
| haveRealName = true; |
| } |
| if (reporterAttribute != null) { |
| Label label = createLabel(peopleComposite, reporterAttribute); |
| GridDataFactory.fillDefaults().align(SWT.RIGHT, SWT.CENTER).applyTo(label); |
| Text textField = createTextField(peopleComposite, reporterAttribute, SWT.FLAT | SWT.READ_ONLY); |
| GridDataFactory.fillDefaults().grab(true, false).applyTo(textField); |
| if (haveRealName) { |
| textField.setText(textField.getText() + " <" |
| + taskData.getAttributeValue(RepositoryTaskAttribute.USER_REPORTER) + ">"); |
| } |
| } |
| addSelfToCC(peopleComposite); |
| addCCList(peopleComposite); |
| getManagedForm().getToolkit().paintBordersFor(peopleComposite); |
| peopleSection.setClient(peopleComposite); |
| peopleSection.setEnabled(true); |
| } |
| |
| protected void addCCList(Composite attributesComposite) { |
| |
| RepositoryTaskAttribute addCCattribute = taskData.getAttribute(RepositoryTaskAttribute.NEW_CC); |
| if (addCCattribute == null) { |
| // TODO: remove once TRAC is priming taskData with NEW_CC attribute |
| taskData.setAttributeValue(RepositoryTaskAttribute.NEW_CC, ""); |
| addCCattribute = taskData.getAttribute(RepositoryTaskAttribute.NEW_CC); |
| } |
| if (addCCattribute != null) { |
| Label label = createLabel(attributesComposite, addCCattribute); |
| GridDataFactory.fillDefaults().align(SWT.RIGHT, SWT.CENTER).applyTo(label); |
| Text text = createTextField(attributesComposite, addCCattribute, SWT.FLAT); |
| GridDataFactory.fillDefaults().hint(150, SWT.DEFAULT).applyTo(text); |
| |
| if (hasContentAssist(addCCattribute)) { |
| ContentAssistCommandAdapter adapter = applyContentAssist(text, |
| createContentProposalProvider(addCCattribute)); |
| ILabelProvider propsalLabelProvider = createProposalLabelProvider(addCCattribute); |
| if (propsalLabelProvider != null) { |
| adapter.setLabelProvider(propsalLabelProvider); |
| } |
| adapter.setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE); |
| } |
| } |
| |
| RepositoryTaskAttribute CCattribute = taskData.getAttribute(RepositoryTaskAttribute.USER_CC); |
| if (CCattribute != null) { |
| Label label = createLabel(attributesComposite, CCattribute); |
| GridDataFactory.fillDefaults().align(SWT.RIGHT, SWT.TOP).applyTo(label); |
| ccList = new org.eclipse.swt.widgets.List(attributesComposite, SWT.MULTI | SWT.V_SCROLL);// SWT.BORDER |
| ccList.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TEXT_BORDER); |
| ccList.setFont(TEXT_FONT); |
| GridData ccListData = new GridData(GridData.HORIZONTAL_ALIGN_FILL); |
| ccListData.horizontalSpan = 1; |
| ccListData.widthHint = 150; |
| ccListData.heightHint = 95; |
| ccList.setLayoutData(ccListData); |
| if (hasChanged(taskData.getAttribute(RepositoryTaskAttribute.USER_CC))) { |
| ccList.setBackground(colorIncoming); |
| } |
| java.util.List<String> ccs = taskData.getCc(); |
| if (ccs != null) { |
| for (String cc : ccs) { |
| ccList.add(cc); |
| } |
| } |
| java.util.List<String> removedCCs = taskData.getAttributeValues(RepositoryTaskAttribute.REMOVE_CC); |
| if (removedCCs != null) { |
| for (String item : removedCCs) { |
| int i = ccList.indexOf(item); |
| if (i != -1) { |
| ccList.select(i); |
| } |
| } |
| } |
| ccList.addSelectionListener(new SelectionListener() { |
| |
| public void widgetSelected(SelectionEvent e) { |
| for (String cc : ccList.getItems()) { |
| int index = ccList.indexOf(cc); |
| if (ccList.isSelected(index)) { |
| List<String> remove = taskData.getAttributeValues(RepositoryTaskAttribute.REMOVE_CC); |
| if (!remove.contains(cc)) { |
| taskData.addAttributeValue(RepositoryTaskAttribute.REMOVE_CC, cc); |
| } |
| } else { |
| taskData.removeAttributeValue(RepositoryTaskAttribute.REMOVE_CC, cc); |
| } |
| } |
| attributeChanged(taskData.getAttribute(RepositoryTaskAttribute.REMOVE_CC)); |
| } |
| |
| public void widgetDefaultSelected(SelectionEvent e) { |
| } |
| }); |
| toolkit.createLabel(attributesComposite, ""); |
| label = toolkit.createLabel(attributesComposite, "(Select to remove)"); |
| GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(label); |
| } |
| |
| } |
| |
| /** |
| * A listener for selection of the summary field. |
| * |
| * @since 2.1 |
| */ |
| protected class DescriptionListener implements Listener { |
| public DescriptionListener() { |
| } |
| |
| public void handleEvent(Event event) { |
| fireSelectionChanged(new SelectionChangedEvent(selectionProvider, new StructuredSelection( |
| new RepositoryTaskSelection(taskData.getTaskId(), taskData.getRepositoryUrl(), |
| taskData.getConnectorKind(), getSectionLabel(SECTION_NAME.DESCRIPTION_SECTION), true, |
| taskData.getSummary())))); |
| } |
| } |
| |
| protected boolean supportsCommentDelete() { |
| return false; |
| } |
| |
| protected void deleteComment(TaskComment comment) { |
| |
| } |
| |
| private boolean expandCommentSection() { |
| for (final TaskComment taskComment : taskData.getComments()) { |
| if ((repositoryTask != null && repositoryTask.getLastReadTimeStamp() == null) |
| || editorInput.getOldTaskData() == null) { |
| return true; |
| } else if (isNewComment(taskComment)) { |
| return true; |
| } |
| } |
| |
| if (taskData.getComments() == null || taskData.getComments().size() == 0) { |
| return false; |
| } else if (editorInput.getTaskData() != null && editorInput.getOldTaskData() != null) { |
| List<TaskComment> newTaskComments = editorInput.getTaskData().getComments(); |
| List<TaskComment> oldTaskComments = editorInput.getOldTaskData().getComments(); |
| if (newTaskComments == null || oldTaskComments == null) { |
| return true; |
| } else if (newTaskComments.size() != oldTaskComments.size()) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| protected void createCommentLayout(Composite composite) { |
| commentsSection = createSection(composite, getSectionLabel(SECTION_NAME.COMMENTS_SECTION), |
| expandCommentSection()); |
| commentsSection.setText(commentsSection.getText() + " (" + taskData.getComments().size() + ")"); |
| |
| final Composite commentsSectionClient = toolkit.createComposite(commentsSection); |
| RowLayout rowLayout = new RowLayout(); |
| rowLayout.pack = true; |
| rowLayout.marginLeft = 0; |
| rowLayout.marginBottom = 0; |
| rowLayout.marginTop = 0; |
| commentsSectionClient.setLayout(rowLayout); |
| commentsSectionClient.setBackground(null); |
| |
| if (supportsCommentSort()) { |
| sortHyperlink = new ImageHyperlink(commentsSectionClient, SWT.NONE); |
| sortHyperlink.setToolTipText("Change order of comments"); |
| toolkit.adapt(sortHyperlink, true, true); |
| sortHyperlink.setBackground(null); |
| |
| sortHyperlink.addHyperlinkListener(new HyperlinkAdapter() { |
| @Override |
| public void linkActivated(HyperlinkEvent e) { |
| sortComments(); |
| } |
| }); |
| sortHyperlinkState(false); |
| } |
| |
| if (taskData.getComments().size() > 0) { |
| commentsSection.setEnabled(true); |
| commentsSection.addExpansionListener(new ExpansionAdapter() { |
| @Override |
| public void expansionStateChanged(ExpansionEvent e) { |
| if (commentsSection.isExpanded()) { |
| if (supportsCommentSort()) { |
| sortHyperlinkState(true); |
| } |
| } else { |
| if (supportsCommentSort()) { |
| sortHyperlinkState(false); |
| } |
| } |
| } |
| }); |
| |
| ImageHyperlink collapseAllHyperlink = new ImageHyperlink(commentsSectionClient, SWT.NONE); |
| collapseAllHyperlink.setToolTipText("Collapse All Comments"); |
| toolkit.adapt(collapseAllHyperlink, true, true); |
| collapseAllHyperlink.setBackground(null); |
| collapseAllHyperlink.setImage(CommonImages.getImage(CommonImages.COLLAPSE_ALL_SMALL)); |
| collapseAllHyperlink.addHyperlinkListener(new HyperlinkAdapter() { |
| @Override |
| public void linkActivated(HyperlinkEvent e) { |
| hideAllComments(); |
| } |
| }); |
| |
| ImageHyperlink expandAllHyperlink = new ImageHyperlink(commentsSectionClient, SWT.NONE); |
| expandAllHyperlink.setToolTipText("Expand All Comments"); |
| toolkit.adapt(expandAllHyperlink, true, true); |
| expandAllHyperlink.setBackground(null); |
| expandAllHyperlink.setImage(CommonImages.getImage(CommonImages.EXPAND_ALL_SMALL)); |
| expandAllHyperlink.addHyperlinkListener(new HyperlinkAdapter() { |
| @Override |
| public void linkActivated(HyperlinkEvent e) { |
| BusyIndicator.showWhile(getControl().getDisplay(), new Runnable() { |
| public void run() { |
| revealAllComments(); |
| if (supportsCommentSort()) { |
| sortHyperlinkState(true); |
| } |
| } |
| }); |
| } |
| }); |
| commentsSection.setTextClient(commentsSectionClient); |
| } else { |
| commentsSection.setEnabled(false); |
| } |
| |
| // Additional (read-only) Comments Area |
| addCommentsComposite = toolkit.createComposite(commentsSection); |
| commentsSection.setClient(addCommentsComposite); |
| GridLayout addCommentsLayout = new GridLayout(); |
| addCommentsLayout.numColumns = 1; |
| addCommentsComposite.setLayout(addCommentsLayout); |
| GridDataFactory.fillDefaults().grab(true, false).applyTo(addCommentsComposite); |
| |
| boolean foundNew = false; |
| |
| for (final TaskComment taskComment : taskData.getComments()) { |
| final ExpandableComposite expandableComposite = toolkit.createExpandableComposite(addCommentsComposite, |
| ExpandableComposite.TREE_NODE | ExpandableComposite.LEFT_TEXT_CLIENT_ALIGNMENT); |
| |
| expandableComposite.setTitleBarForeground(toolkit.getColors().getColor(IFormColors.TITLE)); |
| |
| final Composite toolbarComp = toolkit.createComposite(expandableComposite); |
| rowLayout = new RowLayout(); |
| rowLayout.pack = true; |
| rowLayout.marginLeft = 0; |
| rowLayout.marginBottom = 0; |
| rowLayout.marginTop = 0; |
| toolbarComp.setLayout(rowLayout); |
| toolbarComp.setBackground(null); |
| |
| ImageHyperlink formHyperlink = toolkit.createImageHyperlink(toolbarComp, SWT.NONE); |
| formHyperlink.setBackground(null); |
| formHyperlink.setFont(expandableComposite.getFont()); |
| formHyperlink.setForeground(toolkit.getColors().getColor(IFormColors.TITLE)); |
| if (taskComment.getAuthor() != null && taskComment.getAuthor().equalsIgnoreCase(repository.getUserName())) { |
| formHyperlink.setImage(CommonImages.getImage(CommonImages.PERSON_ME_NARROW)); |
| } else { |
| formHyperlink.setImage(CommonImages.getImage(CommonImages.PERSON_NARROW)); |
| } |
| |
| String authorName = taskComment.getAuthorName(); |
| String tooltipText = taskComment.getAuthor(); |
| if (authorName.length() == 0) { |
| authorName = taskComment.getAuthor(); |
| tooltipText = null; |
| } |
| |
| formHyperlink.setText(taskComment.getNumber() + ": " + authorName + ", " |
| + formatDate(taskComment.getCreated())); |
| |
| formHyperlink.setToolTipText(tooltipText); |
| formHyperlink.setEnabled(true); |
| formHyperlink.setUnderlined(false); |
| |
| final Composite toolbarButtonComp = toolkit.createComposite(toolbarComp); |
| RowLayout buttonCompLayout = new RowLayout(); |
| buttonCompLayout.marginBottom = 0; |
| buttonCompLayout.marginTop = 0; |
| toolbarButtonComp.setLayout(buttonCompLayout); |
| toolbarButtonComp.setBackground(null); |
| |
| if (supportsCommentDelete()) { |
| final ImageHyperlink deleteComment = new ImageHyperlink(toolbarButtonComp, SWT.NULL); |
| toolkit.adapt(deleteComment, true, true); |
| deleteComment.setImage(CommonImages.getImage(CommonImages.REMOVE)); |
| deleteComment.setToolTipText("Remove"); |
| |
| deleteComment.addHyperlinkListener(new HyperlinkAdapter() { |
| |
| @Override |
| public void linkActivated(HyperlinkEvent e) { |
| deleteComment(taskComment); |
| submitToRepository(); |
| } |
| }); |
| |
| } |
| |
| final ImageHyperlink replyLink = createReplyHyperlink(taskComment.getNumber(), toolbarButtonComp, |
| taskComment.getText()); |
| |
| formHyperlink.addHyperlinkListener(new HyperlinkAdapter() { |
| |
| @Override |
| public void linkActivated(HyperlinkEvent e) { |
| toggleExpandableComposite(!expandableComposite.isExpanded(), expandableComposite); |
| } |
| |
| @Override |
| public void linkEntered(HyperlinkEvent e) { |
| replyLink.setUnderlined(true); |
| super.linkEntered(e); |
| } |
| |
| @Override |
| public void linkExited(HyperlinkEvent e) { |
| replyLink.setUnderlined(false); |
| super.linkExited(e); |
| } |
| }); |
| |
| expandableComposite.setTextClient(toolbarComp); |
| |
| toolbarButtonComp.setVisible(expandableComposite.isExpanded()); |
| |
| // HACK: This is necessary |
| // due to a bug in SWT's ExpandableComposite. |
| // 165803: Expandable bars should expand when clicking anywhere |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?taskId=165803 |
| expandableComposite.setData(toolbarButtonComp); |
| |
| expandableComposite.setLayout(new GridLayout()); |
| expandableComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); |
| |
| final Composite ecComposite = toolkit.createComposite(expandableComposite); |
| GridLayout ecLayout = new GridLayout(); |
| ecLayout.marginHeight = 0; |
| ecLayout.marginBottom = 3; |
| ecLayout.marginLeft = 15; |
| ecComposite.setLayout(ecLayout); |
| ecComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); |
| expandableComposite.setClient(ecComposite); |
| // code for outline |
| commentComposites.add(expandableComposite); |
| controlBySelectableObject.put(taskComment, expandableComposite); |
| expandableComposite.addExpansionListener(new ExpansionAdapter() { |
| @Override |
| public void expansionStateChanged(ExpansionEvent e) { |
| toolbarButtonComp.setVisible(expandableComposite.isExpanded()); |
| TextViewer viewer = null; |
| if (e.getState() && expandableComposite.getData("viewer") == null) { |
| viewer = addTextViewer(repository, ecComposite, taskComment.getText().trim(), SWT.MULTI |
| | SWT.WRAP); |
| expandableComposite.setData("viewer", viewer.getTextWidget()); |
| viewer.getTextWidget().addFocusListener(new FocusListener() { |
| |
| public void focusGained(FocusEvent e) { |
| selectedComment = taskComment; |
| |
| } |
| |
| public void focusLost(FocusEvent e) { |
| selectedComment = null; |
| } |
| }); |
| |
| StyledText styledText = viewer.getTextWidget(); |
| GridDataFactory.fillDefaults().hint(DESCRIPTION_WIDTH, SWT.DEFAULT).applyTo(styledText); |
| resetLayout(); |
| } else { |
| // dispose viewer |
| if (expandableComposite.getData("viewer") instanceof StyledText) { |
| ((StyledText) expandableComposite.getData("viewer")).dispose(); |
| expandableComposite.setData("viewer", null); |
| } |
| resetLayout(); |
| } |
| } |
| }); |
| |
| if ((repositoryTask != null && repositoryTask.getLastReadTimeStamp() == null) |
| || editorInput.getOldTaskData() == null) { |
| // hit or lost task data, expose all comments |
| toggleExpandableComposite(true, expandableComposite); |
| foundNew = true; |
| } else if (isNewComment(taskComment)) { |
| expandableComposite.setBackground(colorIncoming); |
| toggleExpandableComposite(true, expandableComposite); |
| foundNew = true; |
| } |
| |
| } |
| if (supportsCommentSort()) { |
| if (commentSortIsUp) { |
| commentSortIsUp = !commentSortIsUp; |
| sortComments(); |
| } else { |
| sortHyperlinkState(commentSortEnable); |
| } |
| } |
| if (foundNew) { |
| if (supportsCommentSort()) { |
| sortHyperlinkState(true); |
| } |
| } else if (taskData.getComments() == null || taskData.getComments().size() == 0) { |
| if (supportsCommentSort()) { |
| sortHyperlinkState(false); |
| } |
| } else if (editorInput.getTaskData() != null && editorInput.getOldTaskData() != null) { |
| List<TaskComment> newTaskComments = editorInput.getTaskData().getComments(); |
| List<TaskComment> oldTaskComments = editorInput.getOldTaskData().getComments(); |
| if (newTaskComments == null || oldTaskComments == null) { |
| if (supportsCommentSort()) { |
| sortHyperlinkState(true); |
| } |
| } else if (newTaskComments.size() != oldTaskComments.size()) { |
| if (supportsCommentSort()) { |
| sortHyperlinkState(true); |
| } |
| } |
| } |
| } |
| |
| public String formatDate(String dateString) { |
| return dateString; |
| } |
| |
| private boolean isNewComment(TaskComment comment) { |
| |
| // Simple test (will not reveal new comments if offline data was lost |
| if (editorInput.getOldTaskData() != null) { |
| return (comment.getNumber() > editorInput.getOldTaskData().getComments().size()); |
| } |
| return false; |
| |
| // OLD METHOD FOR DETERMINING NEW COMMENTS |
| // if (repositoryTask != null) { |
| // if (repositoryTask.getLastSyncDateStamp() == null) { |
| // // new hit |
| // return true; |
| // } |
| // AbstractRepositoryConnector connector = (AbstractRepositoryConnector) |
| // TasksUiPlugin.getRepositoryManager() |
| // .getRepositoryConnector(taskData.getRepositoryKind()); |
| // AbstractTaskDataHandler offlineHandler = connector.getTaskDataHandler(); |
| // if (offlineHandler != null) { |
| // |
| // Date lastSyncDate = |
| // taskData.getAttributeFactory().getDateForAttributeType( |
| // RepositoryTaskAttribute.DATE_MODIFIED, |
| // repositoryTask.getLastSyncDateStamp()); |
| // |
| // if (lastSyncDate != null) { |
| // |
| // // reduce granularity to minutes |
| // Calendar calLastMod = Calendar.getInstance(); |
| // calLastMod.setTimeInMillis(lastSyncDate.getTime()); |
| // calLastMod.set(Calendar.SECOND, 0); |
| // |
| // Date commentDate = |
| // taskData.getAttributeFactory().getDateForAttributeType( |
| // RepositoryTaskAttribute.COMMENT_DATE, comment.getCreated()); |
| // if (commentDate != null) { |
| // |
| // Calendar calComment = Calendar.getInstance(); |
| // calComment.setTimeInMillis(commentDate.getTime()); |
| // calComment.set(Calendar.SECOND, 0); |
| // if (calComment.after(calLastMod)) { |
| // return true; |
| // } |
| // } |
| // } |
| // } |
| // } |
| // return false; |
| |
| } |
| |
| /** |
| * Subclasses that support HTML preview of ticket description and comments override this method to return an |
| * instance of AbstractRenderingEngine |
| * |
| * @return <code>null</code> if HTML preview is not supported for the repository (default) |
| * @since 2.1 |
| */ |
| protected AbstractRenderingEngine getRenderingEngine() { |
| return null; |
| } |
| |
| protected void createNewCommentLayout(Composite composite) { |
| // Section newCommentSection = createSection(composite, |
| // getSectionLabel(SECTION_NAME.NEWCOMMENT_SECTION)); |
| |
| Section newCommentSection = toolkit.createSection(composite, ExpandableComposite.TITLE_BAR); |
| newCommentSection.setText(getSectionLabel(SECTION_NAME.NEWCOMMENT_SECTION)); |
| newCommentSection.setLayout(new GridLayout()); |
| newCommentSection.setLayoutData(new GridData(GridData.FILL_BOTH)); |
| |
| Composite newCommentsComposite = toolkit.createComposite(newCommentSection); |
| newCommentsComposite.setLayout(new GridLayout()); |
| |
| // HACK: new new comment attribute not created by connector, create one. |
| if (taskData.getAttribute(RepositoryTaskAttribute.COMMENT_NEW) == null) { |
| taskData.setAttributeValue(RepositoryTaskAttribute.COMMENT_NEW, ""); |
| } |
| final RepositoryTaskAttribute attribute = taskData.getAttribute(RepositoryTaskAttribute.COMMENT_NEW); |
| |
| if (getRenderingEngine() != null) { |
| // composite with StackLayout to hold text editor and preview widget |
| Composite editPreviewComposite = toolkit.createComposite(newCommentsComposite); |
| GridData editPreviewData = new GridData(GridData.FILL_BOTH); |
| editPreviewData.widthHint = DESCRIPTION_WIDTH; |
| editPreviewData.minimumHeight = DESCRIPTION_HEIGHT; |
| editPreviewData.grabExcessHorizontalSpace = true; |
| editPreviewComposite.setLayoutData(editPreviewData); |
| editPreviewComposite.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TEXT_BORDER); |
| |
| final StackLayout editPreviewLayout = new StackLayout(); |
| editPreviewComposite.setLayout(editPreviewLayout); |
| |
| newCommentTextViewer = addTextEditor(repository, editPreviewComposite, attribute.getValue(), true, SWT.FLAT |
| | SWT.MULTI | SWT.WRAP | SWT.V_SCROLL); |
| |
| editPreviewLayout.topControl = newCommentTextViewer.getControl(); |
| editPreviewComposite.layout(); |
| |
| // composite for edit/preview button |
| Composite buttonComposite = toolkit.createComposite(newCommentsComposite); |
| buttonComposite.setLayout(new GridLayout()); |
| createPreviewButton(buttonComposite, newCommentTextViewer, editPreviewComposite, editPreviewLayout); |
| } else { |
| newCommentTextViewer = addTextEditor(repository, newCommentsComposite, attribute.getValue(), true, SWT.FLAT |
| | SWT.MULTI | SWT.WRAP | SWT.V_SCROLL); |
| GridData addCommentsTextData = new GridData(GridData.FILL_BOTH); |
| addCommentsTextData.widthHint = DESCRIPTION_WIDTH; |
| addCommentsTextData.minimumHeight = DESCRIPTION_HEIGHT; |
| addCommentsTextData.grabExcessHorizontalSpace = true; |
| newCommentTextViewer.getControl().setLayoutData(addCommentsTextData); |
| newCommentTextViewer.getControl().setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TEXT_BORDER); |
| } |
| newCommentTextViewer.setEditable(true); |
| newCommentTextViewer.addTextListener(new ITextListener() { |
| public void textChanged(TextEvent event) { |
| String newValue = addCommentsTextBox.getText(); |
| if (!newValue.equals(attribute.getValue())) { |
| attribute.setValue(newValue); |
| attributeChanged(attribute); |
| } |
| } |
| }); |
| |
| newCommentTextViewer.getTextWidget().addListener(SWT.FocusIn, new NewCommentListener()); |
| addCommentsTextBox = newCommentTextViewer.getTextWidget(); |
| |
| newCommentSection.setClient(newCommentsComposite); |
| |
| toolkit.paintBordersFor(newCommentsComposite); |
| } |
| |
| private Browser addBrowser(Composite parent, int style) { |
| Browser browser = new Browser(parent, style); |
| // intercept links to open tasks in rich editor and urls in separate browser |
| browser.addLocationListener(new LocationAdapter() { |
| @Override |
| public void changing(LocationEvent event) { |
| // ignore events that are caused by manually setting the contents of the browser |
| if (ignoreLocationEvents) { |
| return; |
| } |
| |
| if (event.location != null && !event.location.startsWith("about")) { |
| event.doit = false; |
| IHyperlink link = new TaskUrlHyperlink( |
| new Region(0, 0)/* a fake region just to make constructor happy */, event.location); |
| link.open(); |
| } |
| } |
| |
| }); |
| |
| return browser; |
| } |
| |
| /** |
| * Creates and sets up the button for switching between text editor and HTML preview. Subclasses that support HTML |
| * preview of new comments must override this method. |
| * |
| * @param buttonComposite |
| * the composite that holds the button |
| * @param editor |
| * the TextViewer for editing text |
| * @param previewBrowser |
| * the Browser for displaying the preview |
| * @param editorLayout |
| * the StackLayout of the <code>editorComposite</code> |
| * @param editorComposite |
| * the composite that holds <code>editor</code> and <code>previewBrowser</code> |
| * @since 2.1 |
| */ |
| private void createPreviewButton(final Composite buttonComposite, final TextViewer editor, |
| final Composite editorComposite, final StackLayout editorLayout) { |
| // create an anonymous object that encapsulates the edit/preview button together with |
| // its state and String constants for button text; |
| // this implementation keeps all information needed to set up the button |
| // in this object and the method parameters, and this method is reused by both the |
| // description section and new comments section. |
| new Object() { |
| private static final String LABEL_BUTTON_PREVIEW = "Preview"; |
| |
| private static final String LABEL_BUTTON_EDIT = "Edit"; |
| |
| private int buttonState = 0; |
| |
| private Button previewButton; |
| |
| private Browser previewBrowser; |
| |
| { |
| previewButton = toolkit.createButton(buttonComposite, LABEL_BUTTON_PREVIEW, SWT.PUSH); |
| GridData previewButtonData = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); |
| previewButtonData.widthHint = 100; |
| //previewButton.setImage(TasksUiImages.getImage(TasksUiImages.PREVIEW)); |
| previewButton.setLayoutData(previewButtonData); |
| previewButton.addListener(SWT.Selection, new Listener() { |
| public void handleEvent(Event e) { |
| if (previewBrowser == null) { |
| previewBrowser = addBrowser(editorComposite, SWT.NONE); |
| } |
| |
| buttonState = ++buttonState % 2; |
| if (buttonState == 1) { |
| setText(previewBrowser, "Loading preview..."); |
| previewWiki(previewBrowser, editor.getTextWidget().getText()); |
| } |
| previewButton.setText(buttonState == 0 ? LABEL_BUTTON_PREVIEW : LABEL_BUTTON_EDIT); |
| editorLayout.topControl = (buttonState == 0 ? editor.getControl() : previewBrowser); |
| editorComposite.layout(); |
| } |
| }); |
| } |
| |
| }; |
| } |
| |
| private void setText(Browser browser, String html) { |
| try { |
| ignoreLocationEvents = true; |
| browser.setText((html != null) ? html : ""); |
| } finally { |
| ignoreLocationEvents = false; |
| } |
| |
| } |
| |
| private void previewWiki(final Browser browser, String sourceText) { |
| final class PreviewWikiJob extends Job { |
| private final String sourceText; |
| |
| private String htmlText; |
| |
| private IStatus jobStatus; |
| |
| public PreviewWikiJob(String sourceText) { |
| super("Formatting Wiki Text"); |
| |
| if (sourceText == null) { |
| throw new IllegalArgumentException("source text must not be null"); |
| } |
| |
| this.sourceText = sourceText; |
| } |
| |
| @Override |
| protected IStatus run(IProgressMonitor monitor) { |
| AbstractRenderingEngine htmlRenderingEngine = getRenderingEngine(); |
| if (htmlRenderingEngine == null) { |
| jobStatus = new RepositoryStatus(repository, IStatus.INFO, TasksUiPlugin.ID_PLUGIN, |
| RepositoryStatus.ERROR_INTERNAL, "The repository does not support HTML preview."); |
| return Status.OK_STATUS; |
| } |
| |
| jobStatus = Status.OK_STATUS; |
| try { |
| htmlText = htmlRenderingEngine.renderAsHtml(repository, sourceText, monitor); |
| } catch (CoreException e) { |
| jobStatus = e.getStatus(); |
| } |
| return Status.OK_STATUS; |
| } |
| |
| public String getHtmlText() { |
| return htmlText; |
| } |
| |
| public IStatus getStatus() { |
| return jobStatus; |
| } |
| |
| } |
| |
| final PreviewWikiJob job = new PreviewWikiJob(sourceText); |
| |
| job.addJobChangeListener(new JobChangeAdapter() { |
| |
| @Override |
| public void done(final IJobChangeEvent event) { |
| if (!form.isDisposed()) { |
| if (job.getStatus().isOK()) { |
| getPartControl().getDisplay().asyncExec(new Runnable() { |
| public void run() { |
| AbstractRepositoryTaskEditor.this.setText(browser, job.getHtmlText()); |
| parentEditor.setMessage(null, IMessageProvider.NONE); |
| } |
| }); |
| } else { |
| getPartControl().getDisplay().asyncExec(new Runnable() { |
| public void run() { |
| parentEditor.setMessage(job.getStatus().getMessage(), IMessageProvider.ERROR); |
| } |
| }); |
| } |
| } |
| super.done(event); |
| } |
| }); |
| |
| job.setUser(true); |
| job.schedule(); |
| } |
| |
| /** |
| * Creates the button layout. This displays options and buttons at the bottom of the editor to allow actions to be |
| * performed on the bug. |
| */ |
| protected void createActionsLayout(Composite composite) { |
| Section section = createSection(composite, getSectionLabel(SECTION_NAME.ACTIONS_SECTION)); |
| GridDataFactory.fillDefaults().align(SWT.FILL, SWT.TOP).grab(true, true).applyTo(section); |
| Composite buttonComposite = toolkit.createComposite(section); |
| GridLayout buttonLayout = new GridLayout(); |
| GridDataFactory.fillDefaults().align(SWT.FILL, SWT.TOP).applyTo(buttonComposite); |
| buttonLayout.numColumns = 4; |
| buttonComposite.setLayout(buttonLayout); |
| addRadioButtons(buttonComposite); |
| addActionButtons(buttonComposite); |
| section.setClient(buttonComposite); |
| } |
| |
| protected Section createSection(Composite composite, String title) { |
| return createSection(composite, title, true); |
| } |
| |
| /** |
| * @Since 2.3 |
| */ |
| private Section createSection(Composite composite, String title, boolean expandedState) { |
| int style = ExpandableComposite.TITLE_BAR | Section.TWISTIE; |
| if (expandedState) { |
| style |= ExpandableComposite.EXPANDED; |
| } |
| Section section = toolkit.createSection(composite, style); |
| section.setText(title); |
| section.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); |
| return section; |
| } |
| |
| /** |
| * Adds buttons to this composite. Subclasses can override this method to provide different/additional buttons. |
| * |
| * @param buttonComposite |
| * Composite to add the buttons to. |
| */ |
| protected void addActionButtons(Composite buttonComposite) { |
| submitButton = toolkit.createButton(buttonComposite, LABEL_BUTTON_SUBMIT, SWT.NONE); |
| GridData submitButtonData = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); |
| submitButtonData.widthHint = 100; |
| submitButton.setImage(CommonImages.getImage(TasksUiImages.REPOSITORY_SUBMIT)); |
| submitButton.setLayoutData(submitButtonData); |
| submitButton.addListener(SWT.Selection, new Listener() { |
| public void handleEvent(Event e) { |
| submitToRepository(); |
| } |
| }); |
| |
| setSubmitEnabled(true); |
| |
| toolkit.createLabel(buttonComposite, " "); |
| |
| ITask task = TasksUiInternal.getTaskList().getTask(repository.getRepositoryUrl(), taskData.getTaskId()); |
| if (attachContextEnabled && task != null) { |
| addAttachContextButton(buttonComposite, task); |
| } |
| } |
| |
| private void setSubmitEnabled(boolean enabled) { |
| if (submitButton != null && !submitButton.isDisposed()) { |
| submitButton.setEnabled(enabled); |
| if (enabled) { |
| submitButton.setToolTipText("Submit to " + this.repository.getRepositoryUrl()); |
| } |
| } |
| } |
| |
| /** |
| * Override to make hyperlink available. If not overridden hyperlink will simply not be displayed. |
| * |
| * @return url String form of url that points to task's past activity |
| */ |
| protected String getHistoryUrl() { |
| return null; |
| } |
| |
| protected void saveTaskOffline(IProgressMonitor progressMonitor) { |
| if (taskData == null) { |
| return; |
| } |
| if (repositoryTask != null) { |
| try { |
| localChange = true; |
| |
| TasksUiPlugin.getTaskDataManager().saveOutgoing(repositoryTask, changedAttributes); |
| } finally { |
| localChange = false; |
| } |
| } |
| markDirty(false); |
| } |
| |
| // once the following bug is fixed, this check for first focus is probably |
| // not needed -> Bug# 172033: Restore editor focus |
| private boolean firstFocus = true; |
| |
| @Override |
| public void setFocus() { |
| if (lastFocusControl != null && !lastFocusControl.isDisposed()) { |
| lastFocusControl.setFocus(); |
| } else if (firstFocus && summaryTextViewer != null) { |
| summaryTextViewer.getControl().setFocus(); |
| firstFocus = false; |
| } |
| } |
| |
| /** |
| * Updates the title of the editor |
| * |
| */ |
| protected void updateEditorTitle() { |
| setPartName(editorInput.getName()); |
| ((TaskEditor) this.getEditor()).updateTitle(editorInput.getName()); |
| } |
| |
| @Override |
| public boolean isSaveAsAllowed() { |
| return false; |
| } |
| |
| @Override |
| public void doSave(IProgressMonitor monitor) { |
| saveTaskOffline(monitor); |
| updateEditorTitle(); |
| updateHeaderControls(); |
| // fillToolBar(getParentEditor().getTopForm().getToolBarManager()); |
| } |
| |
| @Override |
| public void doSaveAs() { |
| // we don't save, so no need to implement |
| } |
| |
| /** |
| * @return The composite for the whole editor. |
| */ |
| public Composite getEditorComposite() { |
| return editorComposite; |
| } |
| |
| @Override |
| public void dispose() { |
| TasksUiInternal.getTaskList().removeChangeListener(TASKLIST_CHANGE_LISTENER); |
| getSite().getPage().removeSelectionListener(selectionListener); |
| if (waitCursor != null) { |
| waitCursor.dispose(); |
| } |
| if (activateAction != null) { |
| activateAction.dispose(); |
| } |
| super.dispose(); |
| } |
| |
| /** |
| * Fires a <code>SelectionChangedEvent</code> to all listeners registered under |
| * <code>selectionChangedListeners</code>. |
| * |
| * @param event |
| * The selection event. |
| */ |
| protected void fireSelectionChanged(final SelectionChangedEvent event) { |
| Object[] listeners = selectionChangedListeners.toArray(); |
| for (Object listener : listeners) { |
| final ISelectionChangedListener l = (ISelectionChangedListener) listener; |
| SafeRunnable.run(new SafeRunnable() { |
| public void run() { |
| l.selectionChanged(event); |
| } |
| }); |
| } |
| } |
| |
| /*----------------------------------------------------------* |
| * CODE TO SCROLL TO A COMMENT OR OTHER PIECE OF TEXT |
| *----------------------------------------------------------*/ |
| |
| private final HashMap<Object, Control> controlBySelectableObject = new HashMap<Object, Control>(); |
| |
| private final List<ExpandableComposite> commentComposites = new ArrayList<ExpandableComposite>(); |
| |
| private StyledText addCommentsTextBox = null; |
| |
| protected TextViewer descriptionTextViewer = null; |
| |
| private void revealAllComments() { |
| try { |
| form.setRedraw(false); |
| refreshEnabled = false; |
| if (supportsCommentSort()) { |
| sortHyperlinkState(false); |
| } |
| |
| if (commentsSection != null && !commentsSection.isExpanded()) { |
| commentsSection.setExpanded(true); |
| if (supportsCommentSort()) { |
| sortHyperlinkState(true); |
| } |
| } |
| for (ExpandableComposite composite : commentComposites) { |
| if (composite.isDisposed()) { |
| continue; |
| } |
| if (!composite.isExpanded()) { |
| toggleExpandableComposite(true, composite); |
| } |
| // Composite comp = composite.getParent(); |
| // while (comp != null && !comp.isDisposed()) { |
| // if (comp instanceof ExpandableComposite && !comp.isDisposed()) { |
| // ExpandableComposite ex = (ExpandableComposite) comp; |
| // setExpandableCompositeState(true, ex); |
| // |
| // // HACK: This is necessary |
| // // due to a bug in SWT's ExpandableComposite. |
| // // 165803: Expandable bars should expand when clicking |
| // // anywhere |
| // // https://bugs.eclipse.org/bugs/show_bug.cgi?taskId=165803 |
| // if (ex.getData() != null && ex.getData() instanceof Composite) { |
| // ((Composite) ex.getData()).setVisible(true); |
| // } |
| // |
| // break; |
| // } |
| // comp = comp.getParent(); |
| // } |
| } |
| } finally { |
| refreshEnabled = true; |
| form.setRedraw(true); |
| } |
| resetLayout(); |
| } |
| |
| private void hideAllComments() { |
| try { |
| refreshEnabled = false; |
| |
| for (ExpandableComposite composite : commentComposites) { |
| if (composite.isDisposed()) { |
| continue; |
| } |
| |
| if (composite.isExpanded()) { |
| toggleExpandableComposite(false, composite); |
| } |
| |
| // Composite comp = composite.getParent(); |
| // while (comp != null && !comp.isDisposed()) { |
| // if (comp instanceof ExpandableComposite && !comp.isDisposed()) { |
| // ExpandableComposite ex = (ExpandableComposite) comp; |
| // setExpandableCompositeState(false, ex); |
| // |
| // // HACK: This is necessary |
| // // due to a bug in SWT's ExpandableComposite. |
| // // 165803: Expandable bars should expand when clicking anywhere |
| // // https://bugs.eclipse.org/bugs/show_bug.cgi?taskId=165803 |
| // if (ex.getData() != null && ex.getData() instanceof Composite) { |
| // ((Composite) ex.getData()).setVisible(false); |
| // } |
| // |
| // break; |
| // } |
| // comp = comp.getParent(); |
| // } |
| } |
| |
| // if (commentsSection != null) { |
| // commentsSection.setExpanded(false); |
| // } |
| |
| } finally { |
| refreshEnabled = true; |
| } |
| resetLayout(); |
| } |
| |
| private void sortHyperlinkState(boolean enabled) { |
| commentSortEnable = enabled; |
| |
| if (sortHyperlink == null) { |
| return; |
| } |
| |
| sortHyperlink.setEnabled(enabled); |
| if (enabled) { |
| if (commentSortIsUp) { |
| sortHyperlink.setImage(CommonImages.getImage(TasksUiImages.COMMENT_SORT_UP)); |
| } else { |
| sortHyperlink.setImage(CommonImages.getImage(TasksUiImages.COMMENT_SORT_DOWN)); |
| } |
| } else { |
| if (commentSortIsUp) { |
| sortHyperlink.setImage(CommonImages.getImage(TasksUiImages.COMMENT_SORT_UP_GRAY)); |
| } else { |
| sortHyperlink.setImage(CommonImages.getImage(TasksUiImages.COMMENT_SORT_DOWN_GRAY)); |
| } |
| } |
| } |
| |
| private void sortComments() { |
| if (addCommentsComposite != null) { |
| Control[] commentControlList = addCommentsComposite.getChildren(); |
| int commentControlListLength = commentControlList.length; |
| for (int i = 1; i < commentControlListLength; i++) { |
| commentControlList[commentControlListLength - i].moveAbove(commentControlList[0]); |
| } |
| } |
| commentSortIsUp = !commentSortIsUp; |
| TasksUiPlugin.getDefault().getPreferenceStore().setValue( |
| PREF_SORT_ORDER_PREFIX + repository.getConnectorKind(), commentSortIsUp); |
| sortHyperlinkState(commentSortEnable); |
| resetLayout(); |
| } |
| |
| /** |
| * Selects the given object in the editor. |
| * |
| * @param o |
| * The object to be selected. |
| * @param highlight |
| * Whether or not the object should be highlighted. |
| */ |
| public boolean select(Object o, boolean highlight) { |
| Control control = controlBySelectableObject.get(o); |
| if (control != null && !control.isDisposed()) { |
| |
| // expand all children |
| if (control instanceof ExpandableComposite) { |
| ExpandableComposite ex = (ExpandableComposite) control; |
| if (!ex.isExpanded()) { |
| toggleExpandableComposite(true, ex); |
| } |
| } |
| |
| // expand all parents of control |
| Composite comp = control.getParent(); |
| while (comp != null) { |
| if (comp instanceof Section) { |
| if (!((Section) comp).isExpanded()) { |
| ((Section) comp).setExpanded(true); |
| } |
| } else if (comp instanceof ExpandableComposite) { |
| ExpandableComposite ex = (ExpandableComposite) comp; |
| if (!ex.isExpanded()) { |
| toggleExpandableComposite(true, ex); |
| } |
| |
| // HACK: This is necessary |
| // due to a bug in SWT's ExpandableComposite. |
| // 165803: Expandable bars should expand when clicking anywhere |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?taskId=165803 |
| if (ex.getData() != null && ex.getData() instanceof Composite) { |
| ((Composite) ex.getData()).setVisible(true); |
| } |
| } |
| comp = comp.getParent(); |
| } |
| focusOn(control, highlight); |
| } else if (o instanceof RepositoryTaskData) { |
| focusOn(null, highlight); |
| } else { |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * Programmatically expand the provided ExpandableComposite, using reflection to fire the expansion listeners (see |
| * bug#70358) |
| * |
| * @param comp |
| */ |
| private void toggleExpandableComposite(boolean expanded, ExpandableComposite comp) { |
| if (comp.isExpanded() != expanded) { |
| Method method = null; |
| try { |
| method = comp.getClass().getDeclaredMethod("programmaticToggleState"); |
| method.setAccessible(true); |
| method.invoke(comp); |
| } catch (Exception e) { |
| // ignore |
| } |
| } |
| } |
| |
| private void selectNewComment() { |
| focusOn(addCommentsTextBox, false); |
| } |
| |
| /** |
| * @author Raphael Ackermann (bug 195514) |
| * @since 2.1 |
| */ |
| protected void focusAttributes() { |
| if (attributesSection != null) { |
| focusOn(attributesSection, false); |
| } |
| } |
| |
| private void focusDescription() { |
| if (descriptionTextViewer != null) { |
| focusOn(descriptionTextViewer.getTextWidget(), false); |
| } |
| } |
| |
| /** |
| * Scroll to a specified piece of text |
| * |
| * @param selectionComposite |
| * The StyledText to scroll to |
| */ |
| private void focusOn(Control selectionComposite, boolean highlight) { |
| int pos = 0; |
| // if (previousText != null && !previousText.isDisposed()) { |
| // previousText.setsetSelection(0); |
| // } |
| |
| // if (selectionComposite instanceof FormText) |
| // previousText = (FormText) selectionComposite; |
| |
| if (selectionComposite != null) { |
| |
| // if (highlight && selectionComposite instanceof FormText && |
| // !selectionComposite.isDisposed()) |
| // ((FormText) selectionComposite).set.setSelection(0, ((FormText) |
| // selectionComposite).getText().length()); |
| |
| // get the position of the text in the composite |
| pos = 0; |
| Control s = selectionComposite; |
| if (s.isDisposed()) { |
| return; |
| } |
| s.setEnabled(true); |
| s.setFocus(); |
| s.forceFocus(); |
| while (s != null && s != getEditorComposite()) { |
| if (!s.isDisposed()) { |
| pos += s.getLocation().y; |
| s = s.getParent(); |
| } |
| } |
| |
| pos = pos - 60; // form.getOrigin().y; |
| |
| } |
| if (!form.getBody().isDisposed()) { |
| form.setOrigin(0, pos); |
| } |
| } |
| |
| private RepositoryTaskOutlinePage outlinePage = null; |
| |
| @SuppressWarnings("unchecked") |
| @Override |
| public Object getAdapter(Class adapter) { |
| return getAdapterDelgate(adapter); |
| } |
| |
| public Object getAdapterDelgate(Class<?> adapter) { |
| if (IContentOutlinePage.class.equals(adapter)) { |
| if (outlinePage == null && editorInput != null && taskOutlineModel != null) { |
| outlinePage = new RepositoryTaskOutlinePage(taskOutlineModel); |
| } |
| return outlinePage; |
| } |
| return super.getAdapter(adapter); |
| } |
| |
| public RepositoryTaskOutlinePage getOutline() { |
| return outlinePage; |
| } |
| |
| private Button[] radios; |
| |
| private Control[] radioOptions; |
| |
| private Button attachContextButton; |
| |
| private AbstractLegacyRepositoryConnector connector; |
| |
| private Cursor waitCursor; |
| |
| private boolean formBusy = false; |
| |
| private Composite headerInfoComposite; |
| |
| private Section attributesSection; |
| |
| public void close() { |
| Display activeDisplay = getSite().getShell().getDisplay(); |
| activeDisplay.asyncExec(new Runnable() { |
| public void run() { |
| if (getSite() != null && getSite().getPage() != null && !getManagedForm().getForm().isDisposed()) { |
| if (parentEditor != null) { |
| getSite().getPage().closeEditor(parentEditor, false); |
| } else { |
| getSite().getPage().closeEditor(AbstractRepositoryTaskEditor.this, false); |
| } |
| } |
| } |
| }); |
| } |
| |
| public void addAttributeListener(IRepositoryTaskAttributeListener listener) { |
| attributesListeners.add(listener); |
| } |
| |
| public void removeAttributeListener(IRepositoryTaskAttributeListener listener) { |
| attributesListeners.remove(listener); |
| } |
| |
| public void setParentEditor(TaskEditor parentEditor) { |
| this.parentEditor = parentEditor; |
| } |
| |
| /** |
| * @since 2.1 |
| */ |
| public TaskEditor getParentEditor() { |
| return parentEditor; |
| } |
| |
| public RepositoryTaskOutlineNode getTaskOutlineModel() { |
| return taskOutlineModel; |
| } |
| |
| public void setTaskOutlineModel(RepositoryTaskOutlineNode taskOutlineModel) { |
| this.taskOutlineModel = taskOutlineModel; |
| } |
| |
| /** |
| * A listener for selection of the textbox where a new comment is entered in. |
| */ |
| private class NewCommentListener implements Listener { |
| public void handleEvent(Event event) { |
| fireSelectionChanged(new SelectionChangedEvent(selectionProvider, new StructuredSelection( |
| new RepositoryTaskSelection(taskData.getTaskId(), taskData.getRepositoryUrl(), |
| taskData.getConnectorKind(), getSectionLabel(SECTION_NAME.NEWCOMMENT_SECTION), false, |
| taskData.getSummary())))); |
| } |
| } |
| |
| public Control getControl() { |
| return form; |
| } |
| |
| public void setSummaryText(String text) { |
| if (summaryTextViewer != null && summaryTextViewer.getTextWidget() != null) { |
| summaryTextViewer.getTextWidget().setText(text); |
| } |
| } |
| |
| public void setDescriptionText(String text) { |
| this.descriptionTextViewer.getDocument().set(text); |
| } |
| |
| protected void addRadioButtons(Composite buttonComposite) { |
| int i = 0; |
| Button selected = null; |
| radios = new Button[taskData.getOperations().size()]; |
| radioOptions = new Control[taskData.getOperations().size()]; |
| for (RepositoryOperation o : taskData.getOperations()) { |
| radios[i] = toolkit.createButton(buttonComposite, "", SWT.RADIO); |
| radios[i].setFont(TEXT_FONT); |
| GridData radioData = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); |
| if (!o.hasOptions() && !o.isInput()) { |
| radioData.horizontalSpan = 4; |
| } else { |
| radioData.horizontalSpan = 1; |
| } |
| radioData.heightHint = 20; |
| String opName = o.getOperationName(); |
| opName = opName.replaceAll("</.*>", ""); |
| opName = opName.replaceAll("<.*>", ""); |
| radios[i].setText(opName); |
| radios[i].setLayoutData(radioData); |
| // radios[i].setBackground(background); |
| radios[i].addSelectionListener(new RadioButtonListener()); |
| |
| if (o.hasOptions()) { |
| radioData = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); |
| radioData.horizontalSpan = 3; |
| radioData.heightHint = 20; |
| radioData.widthHint = RADIO_OPTION_WIDTH; |
| radioOptions[i] = new CCombo(buttonComposite, SWT.FLAT | SWT.READ_ONLY); |
| radioOptions[i].setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TEXT_BORDER); |
| toolkit.adapt(radioOptions[i], true, true); |
| radioOptions[i].setFont(TEXT_FONT); |
| radioOptions[i].setLayoutData(radioData); |
| |
| Object[] a = o.getOptionNames().toArray(); |
| Arrays.sort(a); |
| for (int j = 0; j < a.length; j++) { |
| if (a[j] != null) { |
| ((CCombo) radioOptions[i]).add((String) a[j]); |
| if (((String) a[j]).equals(o.getOptionSelection())) { |
| ((CCombo) radioOptions[i]).select(j); |
| } |
| } |
| } |
| ((CCombo) radioOptions[i]).addSelectionListener(new RadioButtonListener()); |
| } else if (o.isInput()) { |
| radioData = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); |
| radioData.horizontalSpan = 3; |
| radioData.widthHint = RADIO_OPTION_WIDTH - 10; |
| |
| String assignmentValue = ""; |
| // NOTE: removed this because we now have content assit |
| // if (opName.equals(REASSIGN_BUG_TO)) { |
| // assignmentValue = repository.getUserName(); |
| // } |
| radioOptions[i] = toolkit.createText(buttonComposite, assignmentValue); |
| radioOptions[i].setFont(TEXT_FONT); |
| radioOptions[i].setLayoutData(radioData); |
| // radioOptions[i].setBackground(background); |
| ((Text) radioOptions[i]).setText(o.getInputValue()); |
| ((Text) radioOptions[i]).addModifyListener(new RadioButtonListener()); |
| |
| if (hasContentAssist(o)) { |
| ContentAssistCommandAdapter adapter = applyContentAssist((Text) radioOptions[i], |
| createContentProposalProvider(o)); |
| ILabelProvider propsalLabelProvider = createProposalLabelProvider(o); |
| if (propsalLabelProvider != null) { |
| adapter.setLabelProvider(propsalLabelProvider); |
| } |
| adapter.setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE); |
| } |
| } |
| |
| if (i == 0 || o.isChecked()) { |
| if (selected != null) { |
| selected.setSelection(false); |
| } |
| selected = radios[i]; |
| radios[i].setSelection(true); |
| if (o.hasOptions() && o.getOptionSelection() != null) { |
| int j = 0; |
| for (String s : ((CCombo) radioOptions[i]).getItems()) { |
| if (s.compareTo(o.getOptionSelection()) == 0) { |
| ((CCombo) radioOptions[i]).select(j); |
| } |
| j++; |
| } |
| } |
| taskData.setSelectedOperation(o); |
| } |
| |
| i++; |
| } |
| |
| toolkit.paintBordersFor(buttonComposite); |
| } |
| |
| /** |
| * If implementing custom attributes you may need to override this method |
| * |
| * @return true if one or more attributes exposed in the editor have |
| */ |
| protected boolean hasVisibleAttributeChanges() { |
| if (taskData == null) { |
| return false; |
| } |
| for (RepositoryTaskAttribute attribute : taskData.getAttributes()) { |
| if (!attribute.isHidden()) { |
| if (hasChanged(attribute)) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| protected boolean hasOutgoingChange(RepositoryTaskAttribute newAttribute) { |
| return editorInput.getOldEdits().contains(newAttribute); |
| } |
| |
| protected boolean hasChanged(RepositoryTaskAttribute newAttribute) { |
| if (newAttribute == null) { |
| return false; |
| } |
| RepositoryTaskData oldTaskData = editorInput.getOldTaskData(); |
| if (oldTaskData == null) { |
| return false; |
| } |
| |
| if (hasOutgoingChange(newAttribute)) { |
| return false; |
| } |
| |
| RepositoryTaskAttribute oldAttribute = oldTaskData.getAttribute(newAttribute.getId()); |
| if (oldAttribute == null) { |
| return true; |
| } |
| if (oldAttribute.getValue() != null && !oldAttribute.getValue().equals(newAttribute.getValue())) { |
| return true; |
| } else if (oldAttribute.getValues() != null && !oldAttribute.getValues().equals(newAttribute.getValues())) { |
| return true; |
| } |
| return false; |
| } |
| |
| protected void addAttachContextButton(Composite buttonComposite, ITask task) { |
| attachContextButton = toolkit.createButton(buttonComposite, "Attach Context", SWT.CHECK); |
| attachContextButton.setImage(CommonImages.getImage(TasksUiImages.CONTEXT_ATTACH)); |
| } |
| |
| /** |
| * Creates a check box for adding the repository user to the cc list. Does nothing if the repository does not have a |
| * valid username, the repository user is the assignee, reporter or already on the the cc list. |
| */ |
| protected void addSelfToCC(Composite composite) { |
| |
| if (repository.getUserName() == null) { |
| return; |
| } |
| |
| RepositoryTaskAttribute owner = taskData.getAttribute(RepositoryTaskAttribute.USER_ASSIGNED); |
| if (owner != null && owner.getValue().indexOf(repository.getUserName()) != -1) { |
| return; |
| } |
| |
| RepositoryTaskAttribute reporter = taskData.getAttribute(RepositoryTaskAttribute.USER_REPORTER); |
| if (reporter != null && reporter.getValue().indexOf(repository.getUserName()) != -1) { |
| return; |
| } |
| |
| RepositoryTaskAttribute ccAttribute = taskData.getAttribute(RepositoryTaskAttribute.USER_CC); |
| if (ccAttribute != null && ccAttribute.getValues().contains(repository.getUserName())) { |
| return; |
| } |
| |
| FormToolkit toolkit = getManagedForm().getToolkit(); |
| toolkit.createLabel(composite, ""); |
| final Button addSelfButton = toolkit.createButton(composite, "Add me to CC", SWT.CHECK); |
| addSelfButton.setSelection(RepositoryTaskAttribute.TRUE.equals(taskData.getAttributeValue(RepositoryTaskAttribute.ADD_SELF_CC))); |
| addSelfButton.setImage(CommonImages.getImage(CommonImages.PERSON)); |
| addSelfButton.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| if (addSelfButton.getSelection()) { |
| taskData.setAttributeValue(RepositoryTaskAttribute.ADD_SELF_CC, RepositoryTaskAttribute.TRUE); |
| } else { |
| taskData.setAttributeValue(RepositoryTaskAttribute.ADD_SELF_CC, RepositoryTaskAttribute.FALSE); |
| } |
| RepositoryTaskAttribute attribute = taskData.getAttribute(RepositoryTaskAttribute.ADD_SELF_CC); |
| changedAttributes.add(attribute); |
| markDirty(true); |
| } |
| }); |
| } |
| |
| public boolean getAttachContext() { |
| if (attachContextButton == null || attachContextButton.isDisposed()) { |
| return false; |
| } else { |
| return attachContextButton.getSelection(); |
| } |
| } |
| |
| public void setExpandAttributeSection(boolean expandAttributeSection) { |
| this.expandedStateAttributes = expandAttributeSection; |
| } |
| |
| public void setAttachContextEnabled(boolean attachContextEnabled) { |
| this.attachContextEnabled = attachContextEnabled; |
| // if (attachContextButton != null && attachContextButton.isEnabled()) { |
| // attachContextButton.setSelection(attachContext); |
| // } |
| } |
| |
| @Override |
| public void showBusy(boolean busy) { |
| if (!getManagedForm().getForm().isDisposed() && busy != formBusy) { |
| // parentEditor.showBusy(busy); |
| if (synchronizeEditorAction != null) { |
| synchronizeEditorAction.setEnabled(!busy); |
| } |
| |
| if (activateAction != null) { |
| activateAction.setEnabled(!busy); |
| } |
| |
| if (openBrowserAction != null) { |
| openBrowserAction.setEnabled(!busy); |
| } |
| |
| if (historyAction != null) { |
| historyAction.setEnabled(!busy); |
| } |
| |
| if (newSubTaskAction != null) { |
| newSubTaskAction.setEnabled(!busy); |
| } |
| |
| if (clearOutgoingAction != null) { |
| clearOutgoingAction.setEnabled(!busy); |
| } |
| |
| if (submitButton != null && !submitButton.isDisposed()) { |
| submitButton.setEnabled(!busy); |
| } |
| |
| setEnabledState(editorComposite, !busy); |
| |
| formBusy = busy; |
| } |
| } |
| |
| private void setEnabledState(Composite composite, boolean enabled) { |
| if (!composite.isDisposed()) { |
| composite.setEnabled(enabled); |
| for (Control control : composite.getChildren()) { |
| control.setEnabled(enabled); |
| if (control instanceof Composite) { |
| setEnabledState(((Composite) control), enabled); |
| } |
| } |
| } |
| } |
| |
| public void setGlobalBusy(boolean busy) { |
| if (parentEditor != null) { |
| parentEditor.showBusy(busy); |
| } else { |
| showBusy(busy); |
| } |
| } |
| |
| public void submitToRepository() { |
| setGlobalBusy(true); |
| |
| if (isDirty()) { |
| saveTaskOffline(new NullProgressMonitor()); |
| markDirty(false); |
| } |
| |
| final boolean attachContext = getAttachContext(); |
| |
| Job submitJob = new Job(LABEL_JOB_SUBMIT) { |
| |
| @Override |
| protected IStatus run(IProgressMonitor monitor) { |
| AbstractTask modifiedTask = null; |
| try { |
| monitor.beginTask("Submitting task", 3); |
| String taskId = connector.getLegacyTaskDataHandler().postTaskData(repository, taskData, |
| new SubProgressMonitor(monitor, 1)); |
| final boolean isNew = taskData.isNew(); |
| if (isNew) { |
| if (taskId != null) { |
| modifiedTask = updateSubmittedTask(taskId, new SubProgressMonitor(monitor, 1)); |
| } else { |
| // null taskId, assume task could not be created... |
| throw new CoreException( |
| new RepositoryStatus(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN, |
| RepositoryStatus.ERROR_INTERNAL, |
| "Task could not be created. No additional information was provided by the connector.")); |
| } |
| } else { |
| modifiedTask = (AbstractTask) TasksUiInternal.getTaskList().getTask( |
| repository.getRepositoryUrl(), taskData.getTaskId()); |
| } |
| |
| // Synchronization accounting... |
| if (modifiedTask != null) { |
| // Attach context if required |
| if (attachContext && connector.getAttachmentHandler() != null) { |
| AttachmentUtil.attachContext(connector.getAttachmentHandler(), repository, modifiedTask, |
| "", new SubProgressMonitor(monitor, 1)); |
| } |
| |
| modifiedTask.setSubmitting(true); |
| final AbstractTask finalModifiedTask = modifiedTask; |
| TasksUiInternal.synchronizeTask(connector, modifiedTask, true, new JobChangeAdapter() { |
| |
| @Override |
| public void done(IJobChangeEvent event) { |
| |
| if (isNew) { |
| close(); |
| TasksUiPlugin.getTaskDataManager().setTaskRead(finalModifiedTask, true); |
| PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { |
| public void run() { |
| TasksUiUtil.openTask(finalModifiedTask); |
| } |
| }); |
| } else { |
| // PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { |
| // public void run() { |
| // refreshEditor(); |
| // } |
| // }); |
| } |
| } |
| }); |
| TasksUiPlugin.getSynchronizationScheduler().synchronize(repository); |
| } else { |
| close(); |
| // For some reason the task wasn't retrieved. |
| // Try to |
| // open local then via web browser... |
| PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { |
| public void run() { |
| TasksUiUtil.openTask(repository.getRepositoryUrl(), taskData.getTaskId(), |
| connector.getTaskUrl(taskData.getRepositoryUrl(), taskData.getTaskId())); |
| } |
| }); |
| } |
| |
| return Status.OK_STATUS; |
| } catch (OperationCanceledException e) { |
| if (modifiedTask != null) { |
| modifiedTask.setSubmitting(false); |
| } |
| PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { |
| public void run() { |
| setGlobalBusy(false);// enableButtons(); |
| } |
| }); |
| return Status.CANCEL_STATUS; |
| } catch (CoreException e) { |
| if (modifiedTask != null) { |
| modifiedTask.setSubmitting(false); |
| } |
| return handleSubmitError(e); |
| } catch (Exception e) { |
| if (modifiedTask != null) { |
| modifiedTask.setSubmitting(false); |
| } |
| StatusHandler.fail(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN, e.getMessage(), e)); |
| PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { |
| public void run() { |
| setGlobalBusy(false);// enableButtons(); |
| } |
| }); |
| } finally { |
| monitor.done(); |
| } |
| return Status.OK_STATUS; |
| } |
| |
| }; |
| |
| IJobChangeListener jobListener = getSubmitJobListener(); |
| if (jobListener != null) { |
| submitJob.addJobChangeListener(jobListener); |
| } |
| submitJob.schedule(); |
| } |
| |
| /** |
| * @since 2.0 If existing task editor, update contents in place |
| */ |
| public void refreshEditor() { |
| try { |
| if (!getManagedForm().getForm().isDisposed()) { |
| if (this.isDirty && taskData != null && !taskData.isNew()) { |
| this.doSave(new NullProgressMonitor()); |
| } |
| setGlobalBusy(true); |
| changedAttributes.clear(); |
| commentComposites.clear(); |
| controlBySelectableObject.clear(); |
| editorInput.refreshInput(); |
| |
| // Note: Marking read must run synchronously |
| // If not, incomings resulting from subsequent synchronization |
| // can get marked as read (without having been viewed by user |
| if (repositoryTask != null) { |
| try { |
| refreshing = true; |
| TasksUiPlugin.getTaskDataManager().setTaskRead(repositoryTask, true); |
| } finally { |
| refreshing = false; |
| } |
| } |
| |
| this.setInputWithNotify(this.getEditorInput()); |
| this.init(this.getEditorSite(), this.getEditorInput()); |
| |
| // Header must be updated after init is called to task data is available |
| updateHeaderControls(); |
| |
| PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { |
| |
| public void run() { |
| if (editorComposite != null && !editorComposite.isDisposed()) { |
| if (taskData != null) { |
| updateEditorTitle(); |
| menu = editorComposite.getMenu(); |
| removeSections(); |
| editorComposite.setMenu(menu); |
| createSections(); |
| form.reflow(true); |
| // setFormHeaderLabel(); |
| markDirty(false); |
| parentEditor.setMessage(null, 0); |
| AbstractRepositoryTaskEditor.this.getEditor().setActivePage( |
| AbstractRepositoryTaskEditor.this.getId()); |
| |
| // Activate editor disabled: bug#179078 |
| // AbstractTaskEditor.this.getEditor().getEditorSite().getPage().activate( |
| // AbstractTaskEditor.this); |
| |
| // TODO: expand sections that were previously |
| // expanded |
| |
| if (taskOutlineModel != null && outlinePage != null |
| && !outlinePage.getControl().isDisposed()) { |
| outlinePage.getOutlineTreeViewer().setInput(taskOutlineModel); |
| outlinePage.getOutlineTreeViewer().refresh(true); |
| } |
| |
| // if (repositoryTask != null) { |
| // TasksUiPlugin.getTaskDataManager().setTaskRead(repositoryTask, true); |
| // } |
| |
| setSubmitEnabled(true); |
| } |
| } |
| } |
| }); |
| |
| } else { |
| // Editor possibly closed as part of submit, mark read |
| |
| // Note: Marking read must run synchronously |
| // If not, incomings resulting from subsequent synchronization |
| // can get marked as read (without having been viewed by user |
| if (repositoryTask != null) { |
| TasksUiPlugin.getTaskDataManager().setTaskRead(repositoryTask, true); |
| } |
| } |
| } finally { |
| if (!getManagedForm().getForm().isDisposed()) { |
| setGlobalBusy(false); |
| } |
| } |
| } |
| |
| /** |
| * Used to prevent form menu from being disposed when disposing elements on the form during refresh |
| */ |
| private void setMenu(Composite comp, Menu menu) { |
| if (!comp.isDisposed()) { |
| comp.setMenu(null); |
| for (Control child : comp.getChildren()) { |
| child.setMenu(null); |
| if (child instanceof Composite) { |
| setMenu((Composite) child, menu); |
| } |
| } |
| } |
| } |
| |
| protected IJobChangeListener getSubmitJobListener() { |
| return null; |
| } |
| |
| protected AbstractTaskCategory getCategory() { |
| return null; |
| } |
| |
| protected IStatus handleSubmitError(final CoreException exception) { |
| PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { |
| public void run() { |
| if (form != null && !form.isDisposed()) { |
| if (exception.getStatus().getCode() == RepositoryStatus.ERROR_IO) { |
| parentEditor.setMessage(ERROR_NOCONNECTIVITY, IMessageProvider.ERROR); |
| StatusHandler.log(exception.getStatus()); |
| } else if (exception.getStatus().getCode() == RepositoryStatus.REPOSITORY_COMMENT_REQUIRED) { |
| TasksUiInternal.displayStatus("Comment required", exception.getStatus()); |
| if (!getManagedForm().getForm().isDisposed() && newCommentTextViewer != null |
| && !newCommentTextViewer.getControl().isDisposed()) { |
| newCommentTextViewer.getControl().setFocus(); |
| } |
| } else if (exception.getStatus().getCode() == RepositoryStatus.ERROR_REPOSITORY_LOGIN) { |
| if (TasksUiUtil.openEditRepositoryWizard(repository) == MessageDialog.OK) { |
| submitToRepository(); |
| return; |
| } |
| } else { |
| TasksUiInternal.displayStatus("Submit failed", exception.getStatus()); |
| } |
| setGlobalBusy(false); |
| } |
| } |
| |
| }); |
| return Status.OK_STATUS; |
| } |
| |
| protected AbstractTask updateSubmittedTask(String postResult, IProgressMonitor monitor) throws CoreException { |
| final AbstractTask newTask = (AbstractTask) TasksUiInternal.createTask(repository, postResult, monitor); |
| |
| if (newTask != null) { |
| PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { |
| public void run() { |
| if (getCategory() != null) { |
| TasksUiInternal.getTaskList().addTask(newTask, getCategory()); |
| } |
| } |
| }); |
| } |
| |
| return newTask; |
| } |
| |
| /** |
| * Class to handle the selection change of the radio buttons. |
| */ |
| private class RadioButtonListener implements SelectionListener, ModifyListener { |
| |
| public void widgetDefaultSelected(SelectionEvent e) { |
| widgetSelected(e); |
| } |
| |
| public void widgetSelected(SelectionEvent e) { |
| Button selected = null; |
| for (Button radio : radios) { |
| if (radio.getSelection()) { |
| selected = radio; |
| } |
| } |
| // determine the operation to do to the bug |
| for (int i = 0; i < radios.length; i++) { |
| if (radios[i] != e.widget && radios[i] != selected) { |
| radios[i].setSelection(false); |
| } |
| |
| if (e.widget == radios[i]) { |
| RepositoryOperation o = taskData.getOperation(radios[i].getText()); |
| taskData.setSelectedOperation(o); |
| markDirty(true); |
| } else if (e.widget == radioOptions[i]) { |
| RepositoryOperation o = taskData.getOperation(radios[i].getText()); |
| o.setOptionSelection(((CCombo) radioOptions[i]).getItem(((CCombo) radioOptions[i]).getSelectionIndex())); |
| |
| if (taskData.getSelectedOperation() != null) { |
| taskData.getSelectedOperation().setChecked(false); |
| } |
| o.setChecked(true); |
| |
| taskData.setSelectedOperation(o); |
| radios[i].setSelection(true); |
| if (selected != null && selected != radios[i]) { |
| selected.setSelection(false); |
| } |
| markDirty(true); |
| } |
| } |
| validateInput(); |
| } |
| |
| public void modifyText(ModifyEvent e) { |
| Button selected = null; |
| for (Button radio : radios) { |
| if (radio.getSelection()) { |
| selected = radio; |
| } |
| } |
| // determine the operation to do to the bug |
| for (int i = 0; i < radios.length; i++) { |
| if (radios[i] != e.widget && radios[i] != selected) { |
| radios[i].setSelection(false); |
| } |
| |
| if (e.widget == radios[i]) { |
| RepositoryOperation o = taskData.getOperation(radios[i].getText()); |
| taskData.setSelectedOperation(o); |
| markDirty(true); |
| } else if (e.widget == radioOptions[i]) { |
| RepositoryOperation o = taskData.getOperation(radios[i].getText()); |
| o.setInputValue(((Text) radioOptions[i]).getText()); |
| |
| if (taskData.getSelectedOperation() != null) { |
| taskData.getSelectedOperation().setChecked(false); |
| } |
| o.setChecked(true); |
| |
| taskData.setSelectedOperation(o); |
| radios[i].setSelection(true); |
| if (selected != null && selected != radios[i]) { |
| selected.setSelection(false); |
| } |
| markDirty(true); |
| } |
| } |
| validateInput(); |
| } |
| } |
| |
| public AbstractRepositoryConnector getConnector() { |
| return connector; |
| } |
| |
| public void setShowAttachments(boolean showAttachments) { |
| this.showAttachments = showAttachments; |
| } |
| |
| public String getCommonDateFormat() { |
| return HEADER_DATE_FORMAT; |
| } |
| |
| public Color getColorIncoming() { |
| return colorIncoming; |
| } |
| |
| /** |
| * @see #select(Object, boolean) |
| */ |
| public void addSelectableControl(Object item, Control control) { |
| controlBySelectableObject.put(item, control); |
| } |
| |
| /** |
| * @see #addSelectableControl(Object, Control) |
| */ |
| public void removeSelectableControl(Object item) { |
| controlBySelectableObject.remove(item); |
| } |
| |
| /** |
| * This method allow you to overwrite the generation of the form area for "assigned to" in the peopleLayout.<br> |
| * <br> |
| * The overwrite is used for Bugzilla Versions > 3.0 |
| * |
| * @since 2.1 |
| * @author Frank Becker (bug 198027) |
| */ |
| protected void addAssignedTo(Composite peopleComposite) { |
| boolean haveRealName = false; |
| RepositoryTaskAttribute assignedAttribute = taskData.getAttribute(RepositoryTaskAttribute.USER_ASSIGNED_NAME); |
| if (assignedAttribute == null) { |
| assignedAttribute = taskData.getAttribute(RepositoryTaskAttribute.USER_ASSIGNED); |
| } else { |
| haveRealName = true; |
| } |
| if (assignedAttribute != null) { |
| Label label = createLabel(peopleComposite, assignedAttribute); |
| GridDataFactory.fillDefaults().align(SWT.RIGHT, SWT.CENTER).applyTo(label); |
| Text textField; |
| if (assignedAttribute.isReadOnly()) { |
| textField = createTextField(peopleComposite, assignedAttribute, SWT.FLAT | SWT.READ_ONLY); |
| } else { |
| textField = createTextField(peopleComposite, assignedAttribute, SWT.FLAT); |
| ContentAssistCommandAdapter adapter = applyContentAssist(textField, |
| createContentProposalProvider(assignedAttribute)); |
| ILabelProvider propsalLabelProvider = createProposalLabelProvider(assignedAttribute); |
| if (propsalLabelProvider != null) { |
| adapter.setLabelProvider(propsalLabelProvider); |
| } |
| adapter.setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE); |
| } |
| GridDataFactory.fillDefaults().grab(true, false).applyTo(textField); |
| if (haveRealName) { |
| textField.setText(textField.getText() + " <" |
| + taskData.getAttributeValue(RepositoryTaskAttribute.USER_ASSIGNED) + ">"); |
| } |
| |
| } |
| } |
| |
| /** |
| * force a re-layout of entire form |
| */ |
| protected void resetLayout() { |
| if (refreshEnabled) { |
| form.layout(true, true); |
| form.reflow(true); |
| } |
| } |
| |
| /** |
| * @since 2.3 |
| */ |
| protected Hyperlink createTaskListHyperlink(Composite parent, final String taskId, final String taskUrl, |
| final AbstractTask task) { |
| TaskHyperlink hyperlink = new TaskHyperlink(parent, SWT.SHORT | getManagedForm().getToolkit().getOrientation()); |
| getManagedForm().getToolkit().adapt(hyperlink, true, true); |
| getManagedForm().getToolkit().getHyperlinkGroup().add(hyperlink); |
| hyperlink.setTask(task); |
| if (task == null) { |
| hyperlink.setText(taskId); |
| } |
| hyperlink.addHyperlinkListener(new HyperlinkAdapter() { |
| @Override |
| public void linkActivated(HyperlinkEvent e) { |
| if (task != null) { |
| TasksUiInternal.refreshAndOpenTaskListElement(task); |
| } else { |
| TasksUiUtil.openTask(repository.getRepositoryUrl(), taskId, taskUrl); |
| } |
| } |
| }); |
| return hyperlink; |
| } |
| |
| } |