Java tutorial
/******************************************************************************* * 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; } }