diff options
Diffstat (limited to 'org.eclipse.mylyn.tasks.ui/src')
17 files changed, 4025 insertions, 136 deletions
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/OfflineTaskManager.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/OfflineTaskManager.java index f03440cb9..60cdb961a 100644 --- a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/OfflineTaskManager.java +++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/OfflineTaskManager.java @@ -23,7 +23,6 @@ import org.eclipse.core.runtime.CoreException; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.mylar.provisional.tasklist.AbstractRepositoryConnector; import org.eclipse.mylar.provisional.tasklist.MylarTaskListPlugin; -import org.eclipse.mylar.provisional.tasklist.AbstractRepositoryTask.RepositoryTaskSyncState; /** * Class to persist the data for the offline reports list @@ -62,93 +61,115 @@ public class OfflineTaskManager { /** - * Add an offline report to the offline reports list + * Add a RepositoryTaskData to the offline reports file * * @param entry * The bug to add */ - public RepositoryTaskSyncState add(final RepositoryTaskData entry, boolean forceSync) throws CoreException { + public void add(final RepositoryTaskData entry) throws CoreException { + int index = -1; + if ((index = find(entry.getRepositoryUrl(), entry.getId())) >= 0) { - RepositoryTaskSyncState status = RepositoryTaskSyncState.SYNCHRONIZED; -// -// try { -// -// String handle = AbstractRepositoryTask.getHandle(entry.getRepositoryUrl(), entry.getId()); -// ITask task = MylarTaskListPlugin.getTaskListManager().getTaskList().getTask(handle); -// -// if (task != null && task instanceof AbstractRepositoryTask) { -// AbstractRepositoryTask repositoryTask = (AbstractRepositoryTask) task; -// -// TaskRepository repository = MylarTaskListPlugin.getRepositoryManager().getRepository( -// repositoryTask.getRepositoryKind(), repositoryTask.getRepositoryUrl()); -// -// if (repository == null) { -// throw new Exception("No repository associated with task. Unable to retrieve timezone information."); -// } -// -// TimeZone repositoryTimeZone = DateUtil.getTimeZone(repository.getTimeZoneId()); + list.remove(index); - int index = -1; - if ((index = find(entry.getRepositoryUrl(), entry.getId())) >= 0) { - //RepositoryTaskData oldBug = list.get(index); + list.add(entry); + writeFile(); -// if (repositoryTask.getLastSynchronized() == null -// || entry.getLastModified(repositoryTimeZone) -// .compareTo(repositoryTask.getLastSynchronized()) > 0 || forceSync) { + } else { + list.add(entry); + writeFile(); + } + } + + +// /** +// * Add an offline report to the offline reports list +// * +// * @param entry +// * The bug to add +// */ +// public RepositoryTaskSyncState add(final RepositoryTaskData entry, boolean forceSync) throws CoreException { // -// if (oldBug.hasChanges()) { +// RepositoryTaskSyncState status = RepositoryTaskSyncState.SYNCHRONIZED; +//// +//// try { +//// +//// String handle = AbstractRepositoryTask.getHandle(entry.getRepositoryUrl(), entry.getId()); +//// ITask task = MylarTaskListPlugin.getTaskListManager().getTaskList().getTask(handle); +//// +//// if (task != null && task instanceof AbstractRepositoryTask) { +//// AbstractRepositoryTask repositoryTask = (AbstractRepositoryTask) task; +//// +//// TaskRepository repository = MylarTaskListPlugin.getRepositoryManager().getRepository( +//// repositoryTask.getRepositoryKind(), repositoryTask.getRepositoryUrl()); +//// +//// if (repository == null) { +//// throw new Exception("No repository associated with task. Unable to retrieve timezone information."); +//// } +//// +//// TimeZone repositoryTimeZone = DateUtil.getTimeZone(repository.getTimeZoneId()); // -// PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() { -// public void run() { -// updateLocalCopy = MessageDialog -// .openQuestion( -// null, -// "Update Local Copy", -// "Local copy of Report " -// + entry.getId() -// + " on " -// + entry.getRepositoryUrl() -// + " has changes.\nWould you like to override local changes? \n\nNote: if you select No, only the new comment will be saved with the updated bug, all other changes will be lost."); -// } -// }); +// int index = -1; +// if ((index = find(entry.getRepositoryUrl(), entry.getId())) >= 0) { +// //RepositoryTaskData oldBug = list.get(index); // -// if (!updateLocalCopy) { -// ((RepositoryTaskData) entry).setNewComment(((RepositoryTaskData) oldBug).getNewComment()); -// ((RepositoryTaskData) entry).setHasChanged(true); -// status = RepositoryTaskSyncState.CONFLICT; -// } else { -// ((RepositoryTaskData) entry).setHasChanged(false); -// status = RepositoryTaskSyncState.SYNCHRONIZED; -// } -// } else { -// if (forceSync) { -// status = RepositoryTaskSyncState.SYNCHRONIZED; -// } else { -// status = RepositoryTaskSyncState.INCOMING; -// } -// } - list.remove(index); -// if (entry.hasChanges() && status != RepositoryTaskSyncState.CONFLICT) { -// status = RepositoryTaskSyncState.OUTGOING; -// } - list.add(entry); - writeFile(); - - } else { - // report doesn't exist in offline reports - list.add(entry); - writeFile(); - } - //repositoryTask.setLastSynchronized(entry.getLastModified(repositoryTimeZone)); +//// if (repositoryTask.getLastSynchronized() == null +//// || entry.getLastModified(repositoryTimeZone) +//// .compareTo(repositoryTask.getLastSynchronized()) > 0 || forceSync) { +//// +//// if (oldBug.hasChanges()) { +//// +//// PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() { +//// public void run() { +//// updateLocalCopy = MessageDialog +//// .openQuestion( +//// null, +//// "Update Local Copy", +//// "Local copy of Report " +//// + entry.getId() +//// + " on " +//// + entry.getRepositoryUrl() +//// + " has changes.\nWould you like to override local changes? \n\nNote: if you select No, only the new comment will be saved with the updated bug, all other changes will be lost."); +//// } +//// }); +//// +//// if (!updateLocalCopy) { +//// ((RepositoryTaskData) entry).setNewComment(((RepositoryTaskData) oldBug).getNewComment()); +//// ((RepositoryTaskData) entry).setHasChanged(true); +//// status = RepositoryTaskSyncState.CONFLICT; +//// } else { +//// ((RepositoryTaskData) entry).setHasChanged(false); +//// status = RepositoryTaskSyncState.SYNCHRONIZED; +//// } +//// } else { +//// if (forceSync) { +//// status = RepositoryTaskSyncState.SYNCHRONIZED; +//// } else { +//// status = RepositoryTaskSyncState.INCOMING; +//// } +//// } +// list.remove(index); +//// if (entry.hasChanges() && status != RepositoryTaskSyncState.CONFLICT) { +//// status = RepositoryTaskSyncState.OUTGOING; +//// } +// list.add(entry); +// writeFile(); +// +// } else { +// // report doesn't exist in offline reports +// list.add(entry); +// writeFile(); +// } +// //repositoryTask.setLastSynchronized(entry.getLastModified(repositoryTimeZone)); +//// } +//// } catch (Exception e) { +//// e.printStackTrace(); +//// IStatus runtimestatus = new Status(IStatus.ERROR, MylarTaskListPlugin.PLUGIN_ID, IStatus.OK, +//// "failed to add offline report", e); +//// throw new CoreException(runtimestatus); +//// } +// return status; // } -// } catch (Exception e) { -// e.printStackTrace(); -// IStatus runtimestatus = new Status(IStatus.ERROR, MylarTaskListPlugin.PLUGIN_ID, IStatus.OK, -// "failed to add offline report", e); -// throw new CoreException(runtimestatus); -// } - return status; - } diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/RepositoryTaskAttribute.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/RepositoryTaskAttribute.java index 8b5a16c0f..ce2359c69 100644 --- a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/RepositoryTaskAttribute.java +++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/RepositoryTaskAttribute.java @@ -18,7 +18,7 @@ import java.util.List; import java.util.Map; /** - * Class representing a report attribute may contain child attributes + * Class representing a report attribute * * @author Rob Elves */ diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/TaskUiUtil.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/TaskUiUtil.java index 66545bef9..ab5a92f11 100644 --- a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/TaskUiUtil.java +++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/TaskUiUtil.java @@ -16,6 +16,9 @@ import java.net.URL; import java.util.ArrayList; import java.util.List; +import org.eclipse.core.runtime.jobs.IJobChangeEvent; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.core.runtime.jobs.JobChangeAdapter; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.mylar.internal.core.util.MylarStatusHandler; import org.eclipse.mylar.internal.tasklist.TaskListPreferenceConstants; @@ -142,52 +145,37 @@ public class TaskUiUtil { } if (task instanceof AbstractRepositoryTask) { - String repositoryKind = ((AbstractRepositoryTask) task).getRepositoryKind(); + final AbstractRepositoryTask repositoryTask = (AbstractRepositoryTask) task; + String repositoryKind = repositoryTask.getRepositoryKind(); final AbstractRepositoryConnector connector = MylarTaskListPlugin.getRepositoryManager() .getRepositoryConnector(repositoryKind); - TaskRepository repository = MylarTaskListPlugin.getRepositoryManager().getRepository(repositoryKind, ((AbstractRepositoryTask) task).getRepositoryUrl()); + TaskRepository repository = MylarTaskListPlugin.getRepositoryManager().getRepository(repositoryKind, repositoryTask.getRepositoryUrl()); if (!repository.hasCredentials()) { MessageDialog.openInformation(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), MylarTaskListPlugin.TITLE_DIALOG, "Repository does not have credentials set, verify via " + TaskRepositoriesView.NAME + " view"); } - if (connector != null) { - TaskUiUtil.openEditor(task, false); - connector.synchronize((AbstractRepositoryTask) task, false, null); - - -// Job refreshJob = connector.synchronize((AbstractRepositoryTask) task, forceUpdate, -// new IJobChangeListener() { -// -// public void done(IJobChangeEvent event) { -// TaskUiUtil.openEditor(task, false); -// } -// -// public void aboutToRun(IJobChangeEvent event) { -// // ignore -// } -// -// public void awake(IJobChangeEvent event) { -// // ignore -// } -// -// public void running(IJobChangeEvent event) { -// // ignore -// } -// -// public void scheduled(IJobChangeEvent event) { -// // ignore -// } -// -// public void sleeping(IJobChangeEvent event) { -// // ignore -// } -// }); -// if (refreshJob == null) { -// TaskUiUtil.openEditor(task, false); -// } - } + if (connector != null) + if (repositoryTask.getTaskData() != null) { + TaskUiUtil.openEditor(task, false); + // TODO: Eventually will need to check that this task + // isn't a new report awaiting submission if so + // don't synchronize + connector.synchronize((AbstractRepositoryTask) task, false, null); + } else { + Job refreshJob = connector.synchronize((AbstractRepositoryTask) task, true, + new JobChangeAdapter() { + public void done(IJobChangeEvent event) { + if (repositoryTask.getTaskData() != null) { + TaskUiUtil.openEditor(task, false); + } + } + }); + if (refreshJob == null) { + TaskUiUtil.openEditor(task, false); + } + } } else { TaskUiUtil.openEditor(task, false); } diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/AbstractBugEditorInput.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/AbstractBugEditorInput.java new file mode 100644 index 000000000..08a93404a --- /dev/null +++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/AbstractBugEditorInput.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2004 - 2006 University Of British Columbia 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: + * University Of British Columbia - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylar.internal.tasklist.ui.editors; + +import java.net.Proxy; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.mylar.internal.tasklist.RepositoryTaskData; +import org.eclipse.mylar.provisional.tasklist.MylarTaskListPlugin; +import org.eclipse.mylar.provisional.tasklist.TaskRepository; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IPersistableElement; + +/** + * Abstract base implementation of an <code>IEditorInput</code> for a subclass + * of <code>AbstractRepositoryTaskEditor</code>. + */ +public abstract class AbstractBugEditorInput implements IEditorInput { + + protected String toolTipText = ""; + + protected Proxy proxySettings; + + protected AbstractBugEditorInput() { + this.proxySettings = MylarTaskListPlugin.getDefault().getProxySettings(); + } + + /** + * Sets the tool tip text for this editor input. + * + * @param str + * The new tool tip text. + */ + protected void setToolTipText(String str) { + // 03-20-03 Allows editor to store title (once it is known) + toolTipText = str; + } + + public boolean exists() { + return true; + } + + public abstract RepositoryTaskData getRepositoryTaskData(); + + public ImageDescriptor getImageDescriptor() { + return null; + } + + public IPersistableElement getPersistable() { + return null; + } + + public String getToolTipText() { + return toolTipText; + } + + public Object getAdapter(Class adapter) { + return null; + } + + /** + * @return <code>true</code> if the argument is an editor input on the + * same bug. + */ + @Override + public abstract boolean equals(Object o); + + public Proxy getProxySettings() { + return proxySettings; + } + + + public abstract TaskRepository getRepository(); +} diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/AbstractRepositoryTaskEditor.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/AbstractRepositoryTaskEditor.java new file mode 100644 index 000000000..21d0f1931 --- /dev/null +++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/AbstractRepositoryTaskEditor.java @@ -0,0 +1,2401 @@ +/******************************************************************************* + * Copyright (c) 2003 - 2006 University Of British Columbia 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: + * University Of British Columbia - initial API and implementation + *******************************************************************************/ +package org.eclipse.mylar.internal.tasklist.ui.editors; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.action.GroupMarker; +import org.eclipse.jface.action.IMenuListener; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.action.Separator; +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.text.Document; +import org.eclipse.jface.text.TextViewer; +import org.eclipse.jface.util.SafeRunnable; +import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.IDoubleClickListener; +import org.eclipse.jface.viewers.ILabelProviderListener; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.ITableLabelProvider; +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.mylar.internal.core.util.MylarStatusHandler; +import org.eclipse.mylar.internal.tasklist.Comment; +import org.eclipse.mylar.internal.tasklist.LocalAttachment; +import org.eclipse.mylar.internal.tasklist.RepositoryAttachment; +import org.eclipse.mylar.internal.tasklist.RepositoryTaskAttribute; +import org.eclipse.mylar.internal.tasklist.RepositoryTaskData; +import org.eclipse.mylar.internal.tasklist.ui.TaskListImages; +import org.eclipse.mylar.internal.tasklist.ui.TaskUiUtil; +import org.eclipse.mylar.provisional.tasklist.AbstractRepositoryConnector; +import org.eclipse.mylar.provisional.tasklist.AbstractRepositoryTask; +import org.eclipse.mylar.provisional.tasklist.MylarTaskListPlugin; +import org.eclipse.mylar.provisional.tasklist.TaskRepository; +import org.eclipse.mylar.provisional.tasklist.AbstractRepositoryTask.RepositoryTaskSyncState; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.CCombo; +import org.eclipse.swt.custom.StyledText; +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.graphics.Font; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.FileDialog; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.ISelectionListener; +import org.eclipse.ui.ISharedImages; +import org.eclipse.ui.IWorkbenchActionConstants; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.actions.ActionFactory; +import org.eclipse.ui.actions.RetargetAction; +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.events.IExpansionListener; +import org.eclipse.ui.forms.widgets.ExpandableComposite; +import org.eclipse.ui.forms.widgets.FormToolkit; +import org.eclipse.ui.forms.widgets.ImageHyperlink; +import org.eclipse.ui.forms.widgets.ScrolledForm; +import org.eclipse.ui.forms.widgets.Section; +import org.eclipse.ui.internal.WorkbenchImages; +import org.eclipse.ui.internal.WorkbenchMessages; +import org.eclipse.ui.part.EditorPart; +import org.eclipse.ui.themes.IThemeManager; +import org.eclipse.ui.views.contentoutline.ContentOutline; +import org.eclipse.ui.views.contentoutline.IContentOutlinePage; + +/** + * Abstract base implementation for an editor to view a bugzilla report. + * + * @author Mik Kersten (some hardening of prototype) + * @author Rob Elves (Conversion to Eclipse Forms) + */ +public abstract class AbstractRepositoryTaskEditor extends EditorPart { + + protected static final String CONTEXT_MENU_ID = "#BugEditor"; + + public static final String REPOSITORY_TEXT_ID = "org.eclipse.mylar.tasklist.ui.fonts.task.editor.comment"; + + public static final String HYPERLINK_TYPE_TASK = "task"; + + public static final String HYPERLINK_TYPE_JAVA = "java"; + + private static final String LABEL_BUTTON_SUBMIT = "Submit to Repository"; + + private static final String LABEL_SECTION_ACTIONS = "Actions"; + + private static final String LABEL_SECTION_ATTRIBUTES = "Attributes"; + + private static final String LABEL_SECTION_ATTACHMENTS = "Attachments"; + + protected static final String LABEL_SECTION_DESCRIPTION = "Description"; + + protected static final String LABEL_SECTION_COMMENTS = "Comments"; + + protected static final String LABEL_SECTION_NEW_COMMENT = "New Comment"; + + private FormToolkit toolkit; + + private ScrolledForm form; + + protected TaskRepository repository; + + public static final int WRAP_LENGTH = 90; + + protected Display display; + + public static final Font TITLE_FONT = JFaceResources.getBannerFont(); + + public static final Font TEXT_FONT = JFaceResources.getDefaultFont(); + + // public static final Font COMMENT_FONT = + // JFaceResources.getFontRegistry().get(JFaceResources.TEXT_FONT); + + public static final Font HEADER_FONT = JFaceResources.getDefaultFont(); + + public static final int DESCRIPTION_WIDTH = 79 * 8; // 500; + + public static final int DESCRIPTION_HEIGHT = 10 * 14; + + // protected Color background; + // + // protected Color foreground; + + protected AbstractBugEditorInput editorInput; + + private MylarTaskEditor parentEditor = null; + + protected RepositoryTaskOutlineNode bugzillaOutlineModel = null; + + // private static int MARGIN = 0;// 5 + + protected SimpleDateFormat simpleDateFormat = new SimpleDateFormat("E MMM dd, yyyy hh:mm aa"); + + // "yyyy-MM-dd HH:mm" + + /** + * Style option for function <code>newLayout</code>. This will create a + * plain-styled, selectable text label. + */ + protected final String VALUE = "VALUE"; + + /** + * Style option for function <code>newLayout</code>. This will create a + * bolded, selectable header. It will also have an arrow image before the + * text (simply for decoration). + */ + protected final String HEADER = "HEADER"; + + /** + * Style option for function <code>newLayout</code>. This will create a + * bolded, unselectable label. + */ + protected final String PROPERTY = "PROPERTY"; + + protected final int HORZ_INDENT = 0; + + protected CCombo attributeCombo; + + // protected CCombo versionCombo; + // + // protected CCombo platformCombo; + // + // protected CCombo priorityCombo; + // + // protected CCombo severityCombo; + // + // protected CCombo milestoneCombo; + // + // protected CCombo componentCombo; + + protected Button addSelfToCCCheck; + + // protected Text urlText; + + protected Text summaryText; + + protected Text addCommentsText; + + // protected Text assignedTo; + + protected Text attachmentDesc; + + protected Text attachmentComment; + + protected Button submitButton; + + protected Table attachmentsTable; + + protected TableViewer attachmentTableViewer; + + protected String[] attachmentsColumns = { "Description", "Type", "Creator", "Created" }; + + protected int[] attachmentsColumnWidths = { 200, 100, 100, 200 }; + + protected int scrollIncrement; + + protected int scrollVertPageIncrement; + + protected int scrollHorzPageIncrement; + + public boolean isDirty = false; + + /** Manager controlling the context menu */ + protected MenuManager contextMenuManager; + + protected StyledText currentSelectedText; + + protected static final String cutActionDefId = "org.eclipse.ui.edit.cut"; //$NON-NLS-1$ + + protected static final String copyActionDefId = "org.eclipse.ui.edit.copy"; //$NON-NLS-1$ + + protected static final String pasteActionDefId = "org.eclipse.ui.edit.paste"; //$NON-NLS-1$ + + protected RetargetAction cutAction; + + protected RepositoryTaskEditorCopyAction copyAction; + + // private Action revealAllAction; + + protected RetargetAction pasteAction; + + protected Composite editorComposite; + + // protected CLabel titleLabel; + + // protected ScrolledComposite scrolledComposite; + + // protected Composite scrolledComposite; + + // protected Composite infoArea; + + // protected Hyperlink linkToBug; + + // protected StyledText generalTitleText; + + // private static List<String> contentTypes; + + private static Map<String, String> extensions2Types; + + static { + /* For possible UI */ + // contentTypes = new LinkedList<String>(); + // contentTypes.add("text/plain"); + // contentTypes.add("text/html"); + // contentTypes.add("application/xml"); + // contentTypes.add("image/gif"); + // contentTypes.add("image/jpeg"); + // contentTypes.add("image/png"); + // contentTypes.add("application/octet-stream"); + extensions2Types = new HashMap<String, String>(); + extensions2Types.put("txt", "text/plain"); + extensions2Types.put("html", "text/html"); + extensions2Types.put("htm", "text/html"); + extensions2Types.put("jpg", "image/jpeg"); + extensions2Types.put("jpeg", "image/jpeg"); + extensions2Types.put("gif", "image/gif"); + extensions2Types.put("png", "image/png"); + extensions2Types.put("xml", "application/xml"); + extensions2Types.put("zip", "application/octet-stream"); + extensions2Types.put("tar", "application/octet-stream"); + extensions2Types.put("gz", "application/octet-stream"); + } + + private List<IRepositoryTaskAttributeListener> attributesListeners = new ArrayList<IRepositoryTaskAttributeListener>(); + + protected final ISelectionProvider selectionProvider = new ISelectionProvider() { + public void addSelectionChangedListener(ISelectionChangedListener listener) { + selectionChangedListeners.add(listener); + } + + public ISelection getSelection() { + return null; + } + + public void removeSelectionChangedListener(ISelectionChangedListener listener) { + selectionChangedListeners.remove(listener); + } + + public void setSelection(ISelection selection) { + // No implementation. + } + }; + + protected List<ISelectionChangedListener> selectionChangedListeners = new ArrayList<ISelectionChangedListener>(); + + protected HashMap<CCombo, RepositoryTaskAttribute> comboListenerMap = new HashMap<CCombo, RepositoryTaskAttribute>(); + + private IRepositoryTaskSelection lastSelected = null; + + protected 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 (n != null && lastSelected != null + && OutlineTools.getHandle(n).equals(OutlineTools.getHandle(lastSelected))) { + // we don't need to set the selection if it is already + // set + return; + } + lastSelected = n; + + Object data = n.getData(); + boolean highlight = true; + if (n.getKey().toLowerCase().equals("comments")) { + highlight = false; + } + if (n.getKey().toLowerCase().equals("new comment")) { + selectNewComment(); + } else if (n.getKey().toLowerCase().equals("new description")) { + selectNewDescription(); + } else if (data != null) { + select(data, highlight); + } + } + } + } + }; + + private class ComboSelectionListener extends SelectionAdapter { + + private CCombo combo; + + public ComboSelectionListener(CCombo combo) { + this.combo = combo; + } + + public void widgetDefaultSelected(SelectionEvent event) { + // ignore + } + + public void widgetSelected(SelectionEvent event) { + if (comboListenerMap.containsKey(combo)) { + if (combo.getSelectionIndex() > -1) { + String sel = combo.getItem(combo.getSelectionIndex()); + RepositoryTaskAttribute attribute = comboListenerMap.get(combo); + if (sel != null && !(sel.equals(attribute.getValue()))) { + attribute.setValue(sel); + for (IRepositoryTaskAttributeListener client : attributesListeners) { + client.attributeChanged(attribute.getName(), sel); + } + changeDirtyStatus(true); + } + } + } + } + } + + /** + * Creates a new <code>AbstractRepositoryTaskEditor</code>. Sets up the + * default fonts and cut/copy/paste actions. + */ + public AbstractRepositoryTaskEditor() { + // set the scroll increments so the editor scrolls normally with the + // scroll wheel + FontData[] fd = TEXT_FONT.getFontData(); + int cushion = 4; + scrollIncrement = fd[0].getHeight() + cushion; + scrollVertPageIncrement = 0; + scrollHorzPageIncrement = 0; + + // set up actions for the context menu + cutAction = new RetargetAction(ActionFactory.CUT.getId(), WorkbenchMessages.Workbench_cut); + cutAction.setToolTipText(WorkbenchMessages.Workbench_cutToolTip);// WorkbenchMessages.getString("Workbench.cutToolTip")); + // //$NON-NLS-1$ + cutAction.setImageDescriptor(WorkbenchImages.getImageDescriptor(ISharedImages.IMG_TOOL_CUT)); + cutAction.setHoverImageDescriptor(WorkbenchImages.getImageDescriptor(ISharedImages.IMG_TOOL_CUT)); + cutAction.setDisabledImageDescriptor(WorkbenchImages.getImageDescriptor(ISharedImages.IMG_TOOL_CUT_DISABLED)); + cutAction.setAccelerator(SWT.CTRL | 'x'); + cutAction.setActionDefinitionId(cutActionDefId); + + pasteAction = new RetargetAction(ActionFactory.PASTE.getId(), WorkbenchMessages.Workbench_paste); + pasteAction.setToolTipText(WorkbenchMessages.Workbench_pasteToolTip);// WorkbenchMessages.getString("Workbench.pasteToolTip")); + // //$NON-NLS-1$ + pasteAction.setImageDescriptor(WorkbenchImages.getImageDescriptor(ISharedImages.IMG_TOOL_PASTE)); + pasteAction.setHoverImageDescriptor(WorkbenchImages.getImageDescriptor(ISharedImages.IMG_TOOL_PASTE)); + pasteAction.setDisabledImageDescriptor(WorkbenchImages + .getImageDescriptor(ISharedImages.IMG_TOOL_PASTE_DISABLED)); + pasteAction.setAccelerator(SWT.CTRL | 'v'); + pasteAction.setActionDefinitionId(pasteActionDefId); + + copyAction = new RepositoryTaskEditorCopyAction(this); + copyAction.setText(WorkbenchMessages.Workbench_copy);// WorkbenchMessages.getString("Workbench.copy")); + copyAction.setImageDescriptor(WorkbenchImages.getImageDescriptor(ISharedImages.IMG_TOOL_COPY)); + copyAction.setHoverImageDescriptor(WorkbenchImages.getImageDescriptor(ISharedImages.IMG_TOOL_COPY)); + copyAction.setDisabledImageDescriptor(WorkbenchImages.getImageDescriptor(ISharedImages.IMG_TOOL_COPY_DISABLED)); + copyAction.setAccelerator(SWT.CTRL | 'c'); + + copyAction.setEnabled(false); + + // + // revealAllAction = new ExpandCommentsAction(this); + // revealAllAction.setText("Reveal Comments");// + // WorkbenchMessages.getString("Workbench.copy")); + // revealAllAction.setImageDescriptor(WorkbenchImages.getImageDescriptor(ISharedImages.IMG_TOOL_COPY)); + // revealAllAction.setHoverImageDescriptor(WorkbenchImages.getImageDescriptor(ISharedImages.IMG_TOOL_COPY)); + // revealAllAction.setDisabledImageDescriptor(WorkbenchImages.getImageDescriptor(ISharedImages.IMG_TOOL_COPY_DISABLED)); + // revealAllAction.setAccelerator(SWT.CTRL | 'r'); + // + // revealAllAction.setEnabled(true); + + } + + /** + * @return The task data this editor is displaying. + */ + public abstract RepositoryTaskData getRepositoryTaskData(); + + public String getNewCommentText() { + return addCommentsTextBox.getText(); + } + + /** + * @return Any currently selected text. + */ + protected StyledText getCurrentText() { + return currentSelectedText; + } + + /** + * @return The action used to copy selected text from a bug editor to the + * clipboard. + */ + protected RepositoryTaskEditorCopyAction getCopyAction() { + return copyAction; + } + + @Override + public void createPartControl(Composite parent) { + + if (getRepositoryTaskData() == null) { + + // MessageDialog.openError(Display.getDefault().getActiveShell(), + // "Bugzilla Client Errror", + // "Could not resolve the requested bug, check Bugzilla server and + // version."); + + Composite composite = new Composite(parent, SWT.NULL); + composite.setLayout(new GridLayout()); + Label noBugLabel = new Label(composite, SWT.NULL); + noBugLabel.setText("Could not resolve bug"); + return; + } + + toolkit = new FormToolkit(parent.getDisplay()); + form = toolkit.createScrolledForm(parent); + // String truncatedSummary = getBug().getSummary(); + // int maxLength = 50; + // if (truncatedSummary.length() > maxLength) { + // truncatedSummary = truncatedSummary.substring(0, maxLength) + "..."; + // } + // form.setFont(COMMENT_FONT); + // form.setText("Bugzilla Bug: " + getBug().getSummary()); + + editorComposite = form.getBody(); + editorComposite.setLayout(new GridLayout()); + editorComposite.setLayoutData(new GridData(GridData.FILL_BOTH)); + + // Header information + + Composite summaryComposite = toolkit.createComposite(editorComposite); + summaryComposite.setLayout(new GridLayout(2, false)); + GridDataFactory.fillDefaults().grab(true, false).applyTo(summaryComposite); + addSummaryText(summaryComposite); + toolkit.paintBordersFor(summaryComposite); + Composite headerInfoComposite = toolkit.createComposite(editorComposite); + headerInfoComposite.setLayout(new GridLayout(6, false)); + toolkit.createLabel(headerInfoComposite, "Task# ").setFont(TITLE_FONT); + toolkit.createText(headerInfoComposite, "" + getRepositoryTaskData().getId(), SWT.FLAT | SWT.READ_ONLY); + + toolkit.createLabel(headerInfoComposite, " Opened: ").setFont(TITLE_FONT); + String openedDateString = ""; + if (getRepositoryTaskData().getCreated() != null) { + openedDateString = simpleDateFormat.format(getRepositoryTaskData().getCreated()); + } + toolkit.createText(headerInfoComposite, openedDateString, SWT.FLAT | SWT.READ_ONLY); + + toolkit.createLabel(headerInfoComposite, " Modified: ").setFont(TITLE_FONT); + String lastModifiedDateString = ""; + if (getRepositoryTaskData().getLastModified(null) != null) { + lastModifiedDateString = simpleDateFormat.format(getRepositoryTaskData().getLastModified(null)); + } + toolkit.createText(headerInfoComposite, lastModifiedDateString, SWT.FLAT | SWT.READ_ONLY); + + // openedText.setFont(TITLE_FONT); + // display = parent.getDisplay(); + // background = JFaceColors.getBannerBackground(display); + // foreground = JFaceColors.getBannerForeground(display); + + // createInfoArea(editorComposite); + createContextMenu(); + createAttributeLayout(); + createCustomAttributeLayout(toolkit, form); + createAttachmentLayout(); + createCommentLayout(toolkit, form); + createButtonLayouts(toolkit, form.getBody()); + + // WorkbenchHelpSystem.getInstance().setHelp(parent, + // BugzillaUiPlugin.EDITOR_PAGE_CONTEXT); + + editorComposite.setMenu(contextMenuManager.createContextMenu(editorComposite)); + form.reflow(true); + getSite().getPage().addSelectionListener(selectionListener); + getSite().setSelectionProvider(selectionProvider); + } + + public abstract void createCustomAttributeLayout(); + + /** + * Create a context menu for this editor. + */ + protected void createContextMenu() { + contextMenuManager = new MenuManager(CONTEXT_MENU_ID); + contextMenuManager.setRemoveAllWhenShown(true); + contextMenuManager.addMenuListener(new IMenuListener() { + public void menuAboutToShow(IMenuManager manager) { + manager.add(cutAction); + manager.add(copyAction); + manager.add(pasteAction); + // manager.add(revealAllAction); + manager.add(new Separator()); + manager.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS)); + if (currentSelectedText == null || currentSelectedText.getSelectionText().length() == 0) { + + copyAction.setEnabled(false); + } else { + copyAction.setEnabled(true); + } + } + }); + getSite().registerContextMenu(CONTEXT_MENU_ID, contextMenuManager, getSite().getSelectionProvider()); + } + + // /** + // * Creates the attribute layout, which contains most of the basic + // attributes + // * of the bug (some of which are editable). + // */ + // protected void createAttributeLayout() { + // + // String title = getTitleString(); + // String keywords = ""; + // String url = ""; + // + // Section section = toolkit.createSection(form.getBody(), + // ExpandableComposite.TITLE_BAR | Section.TWISTIE); + // section.setText(LABEL_SECTION_ATTRIBUTES); + // section.setExpanded(true); + // section.setLayout(new GridLayout()); + // section.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + // + // section.addExpansionListener(new IExpansionListener() { + // public void expansionStateChanging(ExpansionEvent e) { + // form.reflow(true); + // } + // + // public void expansionStateChanged(ExpansionEvent e) { + // form.reflow(true); + // } + // }); + // + // // Attributes Composite- this holds all the combo fiels and text fields + // Composite attributesComposite = toolkit.createComposite(section); + // GridLayout attributesLayout = new GridLayout(); + // attributesLayout.numColumns = 4; + // attributesLayout.horizontalSpacing = 14; + // attributesLayout.verticalSpacing = 6; + // attributesComposite.setLayout(attributesLayout); + // GridData attributesData = new GridData(GridData.FILL_BOTH); + // attributesData.horizontalSpan = 1; + // attributesData.grabExcessVerticalSpace = false; + // attributesComposite.setLayoutData(attributesData); + // // attributesComposite.setBackground(background); + // // End Attributes Composite + // + // section.setClient(attributesComposite); + // + // // Attributes Title Area + // // Composite attributesTitleComposite = new + // // Composite(attributesComposite, SWT.NONE); + // // GridLayout attributesTitleLayout = new GridLayout(); + // // attributesTitleLayout.horizontalSpacing = 0; + // // attributesTitleLayout.marginWidth = 0; + // // attributesTitleComposite.setLayout(attributesTitleLayout); + // // attributesTitleComposite.setBackground(background); + // // GridData attributesTitleData = new + // // GridData(GridData.HORIZONTAL_ALIGN_FILL); + // // attributesTitleData.horizontalSpan = 4; + // // attributesTitleData.grabExcessVerticalSpace = false; + // // attributesTitleComposite.setLayoutData(attributesTitleData); + // // End Attributes Title + // + // // Set the Attributes Title + // // newAttributesLayout(attributesTitleComposite); + // // titleLabel.setText(title); + // bugzillaInput.setToolTipText(title); + // int currentCol = 1; + // + // // String ccValue = null; + // + // // Populate Attributes + // for (Iterator<RepositoryTaskAttribute> it = + // getReport().getAttributes().iterator(); it.hasNext();) { + // RepositoryTaskAttribute attribute = it.next(); + // String key = attribute.getID(); + // String name = attribute.getName(); + // String value = checkText(attribute.getValue()); + // System.err.println(">>> AbstractRepositoryTaskEditor>> name: "+name+" + // key: "+key+" + // value:"+value); + // Map<String, String> values = attribute.getOptionValues(); + // + // // make sure we don't try to display a hidden field + // if (attribute.isHidden() || (key != null && + // key.equals("status_whiteboard"))) + // continue; + // + // if (values == null) + // values = new HashMap<String, String>(); + // + // if (key == null) + // key = ""; + // + // GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL); + // data.horizontalSpan = 1; + // data.horizontalIndent = HORZ_INDENT; + // + // if (key.equals(BugzillaReportElement.KEYWORDS.getKeyString())) { + // keywords = attribute.getValue(); + // } else if (key.equals(BugzillaReportElement.CC.getKeyString())) { + // continue; + // } else if (key.equals(BugzillaReportElement.NEWCC.getKeyString())) { + // // force move to first column + // if (currentCol > 1) { + // while (currentCol <= attributesLayout.numColumns) { + // newLayout(attributesComposite, 1, "", PROPERTY); + // currentCol++; + // } + // } + // addCCList(toolkit, "", attributesComposite); + // } else if (key.equals(BugzillaReportElement.DEPENDSON.getKeyString())) { + // // Dependson and blocked are multi valued so need to explicitly + // // be parsed and shown in the AbstractRepositoryTaskEditor + // continue; + // } else if (key.equals(BugzillaReportElement.BLOCKED.getKeyString())) { + // // Dependson and blocked are multi valued so need to explicitly + // // be parsed and shown in the AbstractRepositoryTaskEditor + // continue; + // } else if (key.equals("bug_file_loc")) { + // url = value; + // } else if (key.equals("op_sys")) { + // // newLayout(attributesComposite, 1, name, PROPERTY); + // toolkit.createLabel(attributesComposite, name); + // // oSCombo = new Combo(attributesComposite, SWT.NO_BACKGROUND | + // // SWT.MULTI | SWT.V_SCROLL | SWT.READ_ONLY);//SWT.NONE + // oSCombo = new CCombo(attributesComposite, SWT.FLAT | SWT.READ_ONLY); + // // oSCombo = new Combo(attributesComposite, SWT.FLAT | + // // SWT.READ_ONLY); + // toolkit.adapt(oSCombo, true, true); + // oSCombo.setFont(TEXT_FONT); + // oSCombo.setLayoutData(data); + // // oSCombo.setBackground(background); + // Set<String> s = values.keySet(); + // String[] a = s.toArray(new String[s.size()]); + // Arrays.sort(a); + // for (int i = 0; i < a.length; i++) { + // oSCombo.add(a[i]); + // } + // if (oSCombo.indexOf(value) != -1) { + // oSCombo.select(oSCombo.indexOf(value)); + // } else { + // oSCombo.select(oSCombo.indexOf("All")); + // } + // // oSCombo.addListener(SWT.Modify, this); + // oSCombo.addSelectionListener(new ComboSelectionListener(oSCombo)); + // comboListenerMap.put(oSCombo, attribute); + // oSCombo.addListener(SWT.FocusIn, new GenericListener()); + // currentCol += 2; + // } else if (key.equals("version")) { + // // newLayout(attributesComposite, 1, name, PROPERTY); + // toolkit.createLabel(attributesComposite, name); + // versionCombo = new CCombo(attributesComposite, SWT.FLAT | + // SWT.NO_BACKGROUND | SWT.MULTI | SWT.V_SCROLL + // | SWT.READ_ONLY); + // toolkit.adapt(versionCombo, true, true); + // versionCombo.setFont(TEXT_FONT); + // versionCombo.setLayoutData(data); + // // versionCombo.setBackground(background); + // Set<String> s = values.keySet(); + // String[] a = s.toArray(new String[s.size()]); + // Arrays.sort(a); + // for (int i = 0; i < a.length; i++) { + // versionCombo.add(a[i]); + // } + // versionCombo.select(versionCombo.indexOf(value)); + // // versionCombo.addListener(SWT.Modify, this); + // versionCombo.addSelectionListener(new + // ComboSelectionListener(versionCombo)); + // versionCombo.addListener(SWT.FocusIn, new GenericListener()); + // comboListenerMap.put(versionCombo, attribute); + // currentCol += 2; + // } else if (key.equals("priority")) { + // // newLayout(attributesComposite, 1, "Priority", PROPERTY); + // toolkit.createLabel(attributesComposite, name); + // priorityCombo = new CCombo(attributesComposite, SWT.FLAT | SWT.V_SCROLL | + // SWT.READ_ONLY); + // toolkit.adapt(priorityCombo, true, true); + // priorityCombo.setFont(TEXT_FONT); + // priorityCombo.setLayoutData(data); + // // priorityCombo.setBackground(background); + // Set<String> s = values.keySet(); + // String[] a = s.toArray(new String[s.size()]); + // Arrays.sort(a); + // for (int i = 0; i < a.length; i++) { + // priorityCombo.add(a[i]); + // } + // priorityCombo.select(priorityCombo.indexOf(value)); + // // priorityCombo.addListener(SWT.Modify, this); + // priorityCombo.addSelectionListener(new + // ComboSelectionListener(priorityCombo)); + // priorityCombo.addListener(SWT.FocusIn, new GenericListener()); + // comboListenerMap.put(priorityCombo, attribute); + // currentCol += 2; + // } else if (key.equals("bug_severity")) { + // // newLayout(attributesComposite, 1, name, PROPERTY); + // toolkit.createLabel(attributesComposite, name); + // severityCombo = new CCombo(attributesComposite, SWT.FLAT | + // SWT.READ_ONLY); + // toolkit.adapt(severityCombo, true, true); + // severityCombo.setFont(TEXT_FONT); + // severityCombo.setLayoutData(data); + // // severityCombo.setBackground(background); + // Set<String> s = values.keySet(); + // String[] a = s.toArray(new String[s.size()]); + // Arrays.sort(a); + // for (int i = 0; i < a.length; i++) { + // severityCombo.add(a[i]); + // } + // severityCombo.select(severityCombo.indexOf(value)); + // severityCombo.addSelectionListener(new + // ComboSelectionListener(severityCombo)); + // // severityCombo.addListener(SWT.Modify, this); + // severityCombo.addListener(SWT.FocusIn, new GenericListener()); + // comboListenerMap.put(severityCombo, attribute); + // currentCol += 2; + // } else if (key.equals("target_milestone")) { + // // newLayout(attributesComposite, 1, name, PROPERTY); + // toolkit.createLabel(attributesComposite, name); + // milestoneCombo = new CCombo(attributesComposite, SWT.FLAT | + // SWT.NO_BACKGROUND | SWT.MULTI + // | SWT.V_SCROLL | SWT.READ_ONLY); + // toolkit.adapt(milestoneCombo, true, true); + // milestoneCombo.setFont(TEXT_FONT); + // milestoneCombo.setLayoutData(data); + // // milestoneCombo.setBackground(background); + // Set<String> s = values.keySet(); + // String[] a = s.toArray(new String[s.size()]); + // Arrays.sort(a); + // for (int i = 0; i < a.length; i++) { + // milestoneCombo.add(a[i]); + // } + // milestoneCombo.select(milestoneCombo.indexOf(value)); + // // milestoneCombo.addListener(SWT.Modify, this); + // milestoneCombo.addSelectionListener(new + // ComboSelectionListener(milestoneCombo)); + // milestoneCombo.addListener(SWT.FocusIn, new GenericListener()); + // comboListenerMap.put(milestoneCombo, attribute); + // currentCol += 2; + // } else if (key.equals("rep_platform")) { + // // newLayout(attributesComposite, 1, name, PROPERTY); + // toolkit.createLabel(attributesComposite, name); + // platformCombo = new CCombo(attributesComposite, SWT.FLAT | + // SWT.NO_BACKGROUND | SWT.MULTI | SWT.V_SCROLL + // | SWT.READ_ONLY); + // toolkit.adapt(platformCombo, true, true); + // platformCombo.setFont(TEXT_FONT); + // platformCombo.setLayoutData(data); + // // platformCombo.setBackground(background); + // Set<String> s = values.keySet(); + // String[] a = s.toArray(new String[s.size()]); + // Arrays.sort(a); + // for (int i = 0; i < a.length; i++) { + // platformCombo.add(a[i]); + // } + // platformCombo.select(platformCombo.indexOf(value)); + // // platformCombo.addListener(SWT.Modify, this); + // platformCombo.addSelectionListener(new + // ComboSelectionListener(platformCombo)); + // platformCombo.addListener(SWT.FocusIn, new GenericListener()); + // comboListenerMap.put(platformCombo, attribute); + // currentCol += 2; + // } else if (key.equals("product")) { + // // newLayout(attributesComposite, 1, name, PROPERTY); + // toolkit.createLabel(attributesComposite, name); + // // toolkit.createLabel(attributesComposite, value); + // Composite uneditableComp = toolkit.createComposite(attributesComposite); + // GridLayout textLayout = new GridLayout(); + // textLayout.marginWidth = 1; + // uneditableComp.setLayout(textLayout); + // toolkit.createText(uneditableComp, value, SWT.READ_ONLY);// + // Label(attributesComposite, + // // value); + // // newLayout(attributesComposite, 1, value, + // // VALUE).addListener(SWT.FocusIn, new GenericListener()); + // currentCol += 2; + // } else if (key.equals("assigned_to")) { + // // newLayout(attributesComposite, 1, name, PROPERTY); + // toolkit.createLabel(attributesComposite, name); + // assignedTo = new Text(attributesComposite, SWT.SINGLE | SWT.WRAP); + // assignedTo.setFont(TEXT_FONT); + // assignedTo.setText(value); + // data = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); + // data.horizontalSpan = 1; + // assignedTo.setLayoutData(data); + // + // assignedTo.addListener(SWT.KeyUp, new Listener() { + // public void handleEvent(Event event) { + // String sel = assignedTo.getText(); + // RepositoryTaskAttribute a = getReport().getAttribute( + // BugzillaReportElement.ASSIGNED_TO); + // if (!(a.getValue().equals(sel))) { + // a.setValue(sel); + // changeDirtyStatus(true); + // } + // } + // }); + // assignedTo.addListener(SWT.FocusIn, new GenericListener()); + // + // currentCol += 2; + // } else if (key.equals("component")) { + // // newLayout(attributesComposite, 1, name, PROPERTY); + // toolkit.createLabel(attributesComposite, name); + // componentCombo = new CCombo(attributesComposite, SWT.FLAT | + // SWT.NO_BACKGROUND | SWT.MULTI + // | SWT.V_SCROLL | SWT.READ_ONLY); + // toolkit.adapt(componentCombo, true, true); + // componentCombo.setFont(TEXT_FONT); + // componentCombo.setLayoutData(data); + // // componentCombo.setBackground(background); + // Set<String> s = values.keySet(); + // String[] a = s.toArray(new String[s.size()]); + // Arrays.sort(a); + // for (int i = 0; i < a.length; i++) { + // componentCombo.add(a[i]); + // } + // componentCombo.select(componentCombo.indexOf(value)); + // // componentCombo.addListener(SWT.Modify, this); + // componentCombo.addSelectionListener(new + // ComboSelectionListener(componentCombo)); + // componentCombo.addListener(SWT.FocusIn, new GenericListener()); + // comboListenerMap.put(componentCombo, attribute); + // currentCol += 2; + // } else if (name.equals("Summary")) { + // // Don't show the summary here. + // continue; + // } else if (name.equals("Last Modified")) { + // // Don't show last modified here. + // continue; + // } else if (name.equals("Bug#")) { + // // Don't show bug number here + // continue; + // } else if (key.equals("bug_status")) { + // // newLayout(attributesComposite, 1, name, PROPERTY); + // toolkit.createLabel(attributesComposite, name); + // Composite uneditableComp = toolkit.createComposite(attributesComposite); + // GridLayout textLayout = new GridLayout(); + // textLayout.marginWidth = 1; + // uneditableComp.setLayout(textLayout); + // toolkit.createText(uneditableComp, value, SWT.READ_ONLY);// + // Label(attributesComposite, + // // value); + // // newLayout(attributesComposite, 1, value, + // // VALUE).addListener(SWT.FocusIn, new GenericListener()); + // currentCol += 2; + // } else if (values.isEmpty()) { + // // newLayout(attributesComposite, 1, name, PROPERTY); + // toolkit.createLabel(attributesComposite, name); + // Composite uneditableComp = toolkit.createComposite(attributesComposite); + // GridLayout textLayout = new GridLayout(); + // textLayout.marginWidth = 1; + // uneditableComp.setLayout(textLayout); + // toolkit.createText(uneditableComp, value, SWT.READ_ONLY);// + // Label(attributesComposite, + // // value); + // // newLayout(attributesComposite, 1, value, + // // VALUE).addListener(SWT.FocusIn, new GenericListener()); + // currentCol += 2; + // } + // if (currentCol > attributesLayout.numColumns) { + // currentCol -= attributesLayout.numColumns; + // } + // } + // // End Populate Attributes + // + // // make sure that we are in the first column + // if (currentCol > 1) { + // while (currentCol <= attributesLayout.numColumns) { + // newLayout(attributesComposite, 1, "", PROPERTY); + // currentCol++; + // } + // } + // + // // URL field + // addUrlText(url, attributesComposite); + // + // // keywords text field (not editable) + // addKeywordsList(toolkit, keywords, attributesComposite); + // // if (ccValue != null) { + // // addCCList(toolkit, ccValue, attributesComposite); + // // } + // addSummaryText(attributesComposite); + // // End URL, Keywords, Summary Text Fields + // toolkit.paintBordersFor(attributesComposite); + // } + + /** + * Creates the attribute layout, which contains most of the basic attributes + * of the bug (some of which are editable). + */ + protected void createAttributeLayout() { + + String title = getTitleString(); + Section section = toolkit.createSection(form.getBody(), ExpandableComposite.TITLE_BAR | Section.TWISTIE); + section.setText(LABEL_SECTION_ATTRIBUTES); + section.setExpanded(true); + section.setLayout(new GridLayout()); + section.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + + section.addExpansionListener(new IExpansionListener() { + public void expansionStateChanging(ExpansionEvent e) { + form.reflow(true); + } + + public void expansionStateChanged(ExpansionEvent e) { + form.reflow(true); + } + }); + + // Attributes Composite- this holds all the combo fields and text fields + Composite attributesComposite = toolkit.createComposite(section); + GridLayout attributesLayout = new GridLayout(); + attributesLayout.numColumns = 4; + attributesLayout.horizontalSpacing = 14; + attributesLayout.verticalSpacing = 6; + attributesComposite.setLayout(attributesLayout); + GridData attributesData = new GridData(GridData.FILL_BOTH); + attributesData.horizontalSpan = 1; + attributesData.grabExcessVerticalSpace = false; + attributesComposite.setLayoutData(attributesData); + section.setClient(attributesComposite); + editorInput.setToolTipText(title); + + int currentCol = 1; + + for (RepositoryTaskAttribute attribute : getRepositoryTaskData().getAttributes()) { + + // String key = attribute.getID(); + String name = attribute.getName(); + String value = ""; + // try { + value = checkText(attribute.getValue()); + // value = + // checkText(BugzillaRepositoryUtil.decodeStringFromCharset(attribute.getValue(), + // getReport().getCharset())); + // } catch (UnsupportedEncodingException e1) { + // // ignore + // } + // System.err.println(">>> AbstractRepositoryTaskEditor>> name: + // "+name+" + // key:"+key+" value:"+value+" is hidden"+attribute.isHidden()); + if (attribute.isHidden()) + continue; + Map<String, String> values = attribute.getOptionValues(); + if (values == null) + values = new HashMap<String, String>(); + + GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL); + data.horizontalSpan = 1; + data.horizontalIndent = HORZ_INDENT; + + if (attribute.hasOptions() && !attribute.isReadOnly()) { + toolkit.createLabel(attributesComposite, name); + attributeCombo = new CCombo(attributesComposite, SWT.FLAT | SWT.READ_ONLY); + toolkit.adapt(attributeCombo, true, true); + attributeCombo.setFont(TEXT_FONT); + attributeCombo.setLayoutData(data); + Set<String> s = values.keySet(); + String[] a = s.toArray(new String[s.size()]); + for (int i = 0; i < a.length; i++) { + attributeCombo.add(a[i]); + } + if (attributeCombo.indexOf(value) != -1) { + attributeCombo.select(attributeCombo.indexOf(value)); + } + attributeCombo.addSelectionListener(new ComboSelectionListener(attributeCombo)); + comboListenerMap.put(attributeCombo, attribute); + attributeCombo.addListener(SWT.FocusIn, new GenericListener()); + currentCol += 2; + } else { + toolkit.createLabel(attributesComposite, name); + Composite textFieldComposite = toolkit.createComposite(attributesComposite); + GridLayout textLayout = new GridLayout(); + textLayout.marginWidth = 1; + textFieldComposite.setLayout(textLayout); + GridData textData = new GridData(GridData.HORIZONTAL_ALIGN_FILL); + textData.horizontalSpan = 1; + textData.widthHint = 135; + + if (attribute.isReadOnly()) { + final Text text = toolkit.createText(textFieldComposite, value, SWT.FLAT | SWT.READ_ONLY); + text.setLayoutData(textData); + } else { + final Text text = toolkit.createText(textFieldComposite, value, SWT.FLAT); + // text.setFont(COMMENT_FONT); + text.setLayoutData(textData); + toolkit.paintBordersFor(textFieldComposite); + text.setData(attribute); + text.addListener(SWT.KeyUp, new Listener() { + public void handleEvent(Event event) { + String sel = text.getText(); + RepositoryTaskAttribute a = (RepositoryTaskAttribute) text.getData(); + if (!(a.getValue().equals(sel))) { + a.setValue(sel); + changeDirtyStatus(true); + } + } + }); + text.addListener(SWT.FocusIn, new GenericListener()); + } + + currentCol += 2; + } + if (currentCol > attributesLayout.numColumns) { + currentCol -= attributesLayout.numColumns; + } + + } + + // // make sure that we are in the first column + // if (currentCol > 1) { + // while (currentCol <= attributesLayout.numColumns) { + // toolkit.createLabel(attributesComposite, ""); + // // newLayout(attributesComposite, 1, "", PROPERTY); + // currentCol++; + // } + // } + + // // Perhaps these should be performed in subclass eventually + // + // addCCList(toolkit, "", attributesComposite); + // + // // URL field + // addUrlText(getReport().getAttributeValue(BugzillaReportElement.BUG_FILE_LOC.getKeyString()), + // attributesComposite); + // + // // keywords text field (not editable) + // try { + // addKeywordsList(toolkit, + // getReport().getAttributeValue(BugzillaReportElement.KEYWORDS.getKeyString()), + // attributesComposite); + // } catch (IOException e) { + // MessageDialog.openInformation(null, + // IBugzillaConstants.TITLE_MESSAGE_DIALOG, + // "Could not retrieve keyword list, ensure proper configuration in " + + // TaskRepositoriesView.NAME + // + "\n\nError reported: " + e.getMessage()); + // } + + // addSelfToCCCheck = toolkit.createButton(attributesComposite, "Add + // "+repository.getUserName()+" to CC", SWT.FLAT | SWT.CHECK); + // addSelfToCCCheck.setSelection(true); + // addSelfToCCCheck.addSelectionListener(new SelectionAdapter() { + // + // @Override + // public void widgetSelected(SelectionEvent e) { + // if(addSelfToCCCheck.getSelection()) { + // getReport().setAttributeValue(BugzillaReportElement.ADDSELFCC, "1"); + // } else { + // getReport().setAttributeValue(BugzillaReportElement.ADDSELFCC, "0"); + // } + // }}); + // + // addSummaryText(attributesComposite); + // End URL, Keywords, Summary Text Fields + toolkit.paintBordersFor(attributesComposite); + } + +// protected abstract void addKeywordsList(FormToolkit toolkit, String keywords, Composite attributesComposite) +// throws IOException; +// +// protected abstract void addCCList(FormToolkit toolkit, String value, Composite attributesComposite); + + /** + * Adds a text field to display and edit the bug's summary. + * + * @param attributesComposite + * The composite to add the text field to. + */ + protected void addSummaryText(Composite attributesComposite) { + // newLayout(attributesComposite, 1, "Summary:", PROPERTY); + toolkit.createLabel(attributesComposite, "Summary:").setFont(TITLE_FONT); + summaryText = toolkit.createText(attributesComposite, getRepositoryTaskData().getSummary(), SWT.FLAT); + IThemeManager themeManager = getSite().getWorkbenchWindow().getWorkbench().getThemeManager(); + Font summaryFont = themeManager.getCurrentTheme().getFontRegistry().get(REPOSITORY_TEXT_ID); + summaryText.setFont(summaryFont); + GridData summaryTextData = new GridData(GridData.FILL_HORIZONTAL);// HORIZONTAL_ALIGN_FILL + summaryTextData.horizontalSpan = 1; + // summaryTextData.widthHint = 200; + + summaryText.setLayoutData(summaryTextData); + summaryText.addListener(SWT.KeyUp, new SummaryListener()); + summaryText.addListener(SWT.FocusIn, new GenericListener()); + } + + protected void createAttachmentLayout() { + Section section = toolkit.createSection(form.getBody(), ExpandableComposite.TITLE_BAR | Section.TWISTIE); + section.setText(LABEL_SECTION_ATTACHMENTS); + section.setExpanded(getRepositoryTaskData().getAttachments().size() > 0); + section.setLayout(new GridLayout()); + section.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + + final Composite attachmentsComposite = toolkit.createComposite(section); + attachmentsComposite.setLayout(new GridLayout()); + attachmentsComposite.setLayoutData(new GridData(GridData.FILL_BOTH)); + section.setClient(attachmentsComposite); + + if (getRepositoryTaskData().getAttachments().size() > 0) { + + attachmentsTable = toolkit.createTable(attachmentsComposite, SWT.SINGLE | SWT.BORDER | SWT.FULL_SELECTION); + attachmentsTable.setLinesVisible(true); + attachmentsTable.setHeaderVisible(true); + attachmentsTable.setLayout(new GridLayout()); + GridData tableGridData = new GridData(GridData.FILL_BOTH); + // tableGridData.heightHint = 100; + tableGridData.widthHint = DESCRIPTION_WIDTH; + 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]); + } + + TableViewer attachmentsTableViewer = new TableViewer(attachmentsTable); + attachmentsTableViewer.setUseHashlookup(true); + attachmentsTableViewer.setColumnProperties(attachmentsColumns); + + attachmentsTableViewer.setSorter(new ViewerSorter() { + public int compare(Viewer viewer, Object e1, Object e2) { + RepositoryAttachment attachment1 = (RepositoryAttachment) e1; + RepositoryAttachment attachment2 = (RepositoryAttachment) e2; + Date created1 = attachment1.getDateCreated(); + Date created2 = attachment2.getDateCreated(); + if (created1 != null && created2 != null) { + return attachment1.getDateCreated().compareTo(attachment2.getDateCreated()); + } else { + return 0; + } + } + }); + + attachmentsTableViewer.setContentProvider(new IStructuredContentProvider() { + public Object[] getElements(Object inputElement) { + return getRepositoryTaskData().getAttachments().toArray(); + } + + public void dispose() { + // ignore + + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + // ignore + + } + }); + + attachmentsTableViewer.setLabelProvider(new ITableLabelProvider() { + + public Image getColumnImage(Object element, int columnIndex) { + // RepositoryAttachment attachment = (RepositoryAttachment) + // element; + return null; + } + + public String getColumnText(Object element, int columnIndex) { + RepositoryAttachment attachment = (RepositoryAttachment) element; + switch (columnIndex) { + case 0: + return attachment.getDescription(); + case 1: + return attachment.getContentType(); + case 2: + return attachment.getCreator(); + case 3: + return attachment.getDateCreated().toString(); + } + return "unrecognized column"; + } + + public void addListener(ILabelProviderListener listener) { + // ignore + + } + + public void dispose() { + // ignore + + } + + public boolean isLabelProperty(Object element, String property) { + // ignore + return false; + } + + public void removeListener(ILabelProviderListener listener) { + // ignore + + } + + }); + + attachmentsTableViewer.addDoubleClickListener(new IDoubleClickListener() { + public void doubleClick(DoubleClickEvent event) { + String address = repository.getUrl() + "/attachment.cgi?id="; + if (!event.getSelection().isEmpty()) { + StructuredSelection selection = (StructuredSelection) event.getSelection(); + RepositoryAttachment attachment = (RepositoryAttachment) selection.getFirstElement(); + address += attachment.getId() + "&action=view"; + ; + TaskUiUtil.openUrl(address); + } + } + }); + + attachmentsTableViewer.setInput(getRepositoryTaskData()); + + } else { + toolkit.createLabel(attachmentsComposite, "No attachments"); + } + + /* Add a file chooser to add new attachments */ + Composite addAttachmentComposite = toolkit.createComposite(attachmentsComposite); + addAttachmentComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + addAttachmentComposite.setLayout(new GridLayout(2, false)); + + Button addAttachmentButton = toolkit.createButton(addAttachmentComposite, "Add an Attachment...", SWT.PUSH); + final Text fname = new Text(addAttachmentComposite, SWT.LEFT);// toolkit.createText(addAttachmentComposite, + // ""); + fname.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + + final Composite addAttachmentInfo = toolkit.createComposite(addAttachmentComposite); + addAttachmentInfo.setSize(2, 1); + addAttachmentInfo.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1)); + addAttachmentInfo.setLayout(new GridLayout(3, false)); + + toolkit.createLabel(addAttachmentInfo, "Description: "); + attachmentDesc = toolkit.createText(addAttachmentInfo, ""); + attachmentDesc.setEnabled(false); + attachmentDesc.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1)); + + toolkit.createLabel(addAttachmentInfo, "Comment: "); + attachmentComment = toolkit.createText(addAttachmentInfo, ""); + attachmentComment.setEnabled(false); + attachmentComment.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1)); + + final Button isPatchButton = toolkit.createButton(addAttachmentInfo, "Patch", SWT.CHECK); + + addAttachmentInfo.setVisible(false); + + /* File Chooser listener */ + addAttachmentButton.addSelectionListener(new SelectionListener() { + public void widgetDefaultSelected(SelectionEvent e) { + // ignore + } + + public void widgetSelected(SelectionEvent e) { + FileDialog fileChooser = new FileDialog(attachmentsComposite.getShell(), SWT.OPEN); + String file = fileChooser.open(); + + // Check if the dialog was canceled or an error occured + if (file == null) { + return; + } + // update UI + fname.setText(file); + } + }); + + /* + * Attachment file name listener, update the local attachment + * accordingly + */ + fname.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + LocalAttachment att = getRepositoryTaskData().getNewAttachment(); + if (att == null) { + att = new LocalAttachment(); + att.setReport(getRepositoryTaskData()); + } + if ("".equals(fname.getText())) { + attachmentDesc.setEnabled(false); + attachmentComment.setEnabled(false); + } else { + addAttachmentInfo.setVisible(true); + attachmentDesc.setEnabled(true); + attachmentComment.setEnabled(true); + } + att.setFilePath(fname.getText()); + getRepositoryTaskData().setNewAttachment(att); + + /* TODO jpound - UI for content type */ + // Determine type by extension + int index = fname.getText().lastIndexOf("."); + if (index < 0) { + att.setContentType("text/plain"); + } else { + String ext = fname.getText().substring(index + 1); + String type = extensions2Types.get(ext.toLowerCase()); + if (type != null) { + att.setContentType(type); + } else { + att.setContentType("text/plain"); + } + } + } + }); + + /* Listener for isPatch */ + isPatchButton.addSelectionListener(new SelectionListener() { + public void widgetDefaultSelected(SelectionEvent e) { + // ignore + } + + public void widgetSelected(SelectionEvent e) { + LocalAttachment att = getRepositoryTaskData().getNewAttachment(); + att.setPatch(isPatchButton.getSelection()); + } + }); + + } + + /** + * Creates the description layout, which displays and possibly edits the + * bug's description. + */ + protected abstract void createCustomAttributeLayout(FormToolkit toolkit, final ScrolledForm form); + + protected void createCommentLayout(FormToolkit toolkit, final ScrolledForm form) { + + Section section = toolkit.createSection(form.getBody(), ExpandableComposite.TITLE_BAR | Section.TWISTIE); + section.setText(LABEL_SECTION_COMMENTS); + section.setExpanded(true); + section.setLayout(new GridLayout()); + section.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + section.addExpansionListener(new IExpansionListener() { + public void expansionStateChanging(ExpansionEvent e) { + form.reflow(true); + } + + public void expansionStateChanged(ExpansionEvent e) { + form.reflow(true); + } + }); + + ImageHyperlink hyperlink = toolkit.createImageHyperlink(section, SWT.NONE); + hyperlink.setBackgroundMode(SWT.INHERIT_NONE); + hyperlink.setBackground(section.getTitleBarBackground()); + hyperlink.setImage(TaskListImages.getImage(TaskListImages.EXPAND_ALL)); + hyperlink.addHyperlinkListener(new HyperlinkAdapter() { + public void linkActivated(HyperlinkEvent e) { + revealAllComments(); + } + }); + + section.setTextClient(hyperlink); + + // Additional (read-only) Comments Area + Composite addCommentsComposite = toolkit.createComposite(section); + section.setClient(addCommentsComposite); + GridLayout addCommentsLayout = new GridLayout(); + addCommentsLayout.numColumns = 1; + addCommentsComposite.setLayout(addCommentsLayout); + // addCommentsComposite.setBackground(background); + GridDataFactory.fillDefaults().grab(true, false).applyTo(addCommentsComposite); + // End Additional (read-only) Comments Area + + StyledText styledText = null; + for (Iterator<Comment> it = getRepositoryTaskData().getComments().iterator(); it.hasNext();) { + final Comment comment = it.next(); + + // skip comment 0 as it is the description + if (comment.getNumber() == 0) + continue; + + ExpandableComposite expandableComposite = toolkit.createExpandableComposite(addCommentsComposite, + ExpandableComposite.TREE_NODE); + + if (!it.hasNext()) { + expandableComposite.setExpanded(true); + } + + expandableComposite.setText(comment.getNumber() + ": " + comment.getAuthorName() + ", " + + simpleDateFormat.format(comment.getCreated())); + + expandableComposite.addExpansionListener(new ExpansionAdapter() { + public void expansionStateChanged(ExpansionEvent e) { + form.reflow(true); + } + }); + + expandableComposite.setLayout(new GridLayout()); + expandableComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + + Composite ecComposite = toolkit.createComposite(expandableComposite); + GridLayout ecLayout = new GridLayout(); + ecLayout.marginHeight = 0; + ecLayout.marginBottom = 10; + ecLayout.marginLeft = 10; + ecComposite.setLayout(ecLayout); + ecComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + expandableComposite.setClient(ecComposite); + // toolkit.paintBordersFor(expandableComposite); + + // TODO: Attachments are no longer 'attached' to Comments + + // if (comment.hasAttachment()) { + // + // Link attachmentLink = new Link(ecComposite, SWT.NONE); + // + // String attachmentHeader; + // + // if (!comment.isObsolete()) { + // attachmentHeader = " Attached: " + + // comment.getAttachmentDescription() + " [<a>view</a>]"; + // } else { + // attachmentHeader = " Deprecated: " + + // comment.getAttachmentDescription(); + // } + // // String result = MessageFormat.format(attachmentHeader, new + // // String[] { node + // // .getLabelText() }); + // + // attachmentLink.addSelectionListener(new SelectionAdapter() { + // /* + // * (non-Javadoc) + // * + // * @see + // org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent) + // */ + // public void widgetSelected(SelectionEvent e) { + // String address = repository.getUrl() + "/attachment.cgi?id=" + + // comment.getAttachmentId() + // + "&action=view"; + // TaskUiUtil.openUrl(address, address, address); + // + // } + // }); + // + // attachmentLink.setText(attachmentHeader); + // + // } + + TextViewer viewer = addRepositoryText(repository, ecComposite, comment.getText()); + styledText = viewer.getTextWidget(); + GridDataFactory.fillDefaults().hint(DESCRIPTION_WIDTH, SWT.DEFAULT).applyTo(styledText); + + + // code for outline + commentStyleText.add(styledText); + texts.add(textsindex, styledText); + textHash.put(comment, styledText); + textsindex++; + } + + Section sectionAdditionalComments = toolkit.createSection(form.getBody(), ExpandableComposite.TITLE_BAR + | Section.TWISTIE); + sectionAdditionalComments.setText(LABEL_SECTION_NEW_COMMENT); + sectionAdditionalComments.setExpanded(true); + + sectionAdditionalComments.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + sectionAdditionalComments.addExpansionListener(new IExpansionListener() { + public void expansionStateChanging(ExpansionEvent e) { + form.reflow(true); + } + + public void expansionStateChanged(ExpansionEvent e) { + form.reflow(true); + } + }); + + Composite newCommentsComposite = toolkit.createComposite(sectionAdditionalComments); + newCommentsComposite.setLayout(new GridLayout()); + newCommentsComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + addCommentsText = toolkit.createText(newCommentsComposite, getRepositoryTaskData().getNewComment(), SWT.MULTI | SWT.V_SCROLL + | SWT.WRAP); + + IThemeManager themeManager = getSite().getWorkbenchWindow().getWorkbench().getThemeManager(); + Font newCommnetFont = themeManager.getCurrentTheme().getFontRegistry().get(REPOSITORY_TEXT_ID); + addCommentsText.setFont(newCommnetFont); + toolkit.paintBordersFor(newCommentsComposite); + GridData addCommentsTextData = new GridData(GridData.FILL_HORIZONTAL); + addCommentsTextData.widthHint = DESCRIPTION_WIDTH; + addCommentsTextData.heightHint = DESCRIPTION_HEIGHT; + addCommentsTextData.grabExcessHorizontalSpace = true; + + addCommentsText.setLayoutData(addCommentsTextData); + + addCommentsText.addListener(SWT.KeyUp, new Listener() { + + public void handleEvent(Event event) { + String sel = addCommentsText.getText(); + if (!(getRepositoryTaskData().getNewComment().equals(sel))) { + getRepositoryTaskData().setNewComment(sel); + changeDirtyStatus(true); + } + validateInput(); + } + }); + addCommentsText.addListener(SWT.FocusIn, new NewCommentListener()); + addCommentsTextBox = addCommentsText; + + sectionAdditionalComments.setClient(newCommentsComposite); + +// TODO: move into ExistingBugEditor commands section +// // if they aren't already on the cc list create an add self check box +// +// RepositoryTaskAttribute owner = getReport().getAttribute(RepositoryTaskAttribute.USER_ASSIGNED); +// +// // Don't add addselfcc check box if the user is the bug owner +// if (owner != null && owner.getValue().indexOf(repository.getUserName()) != -1) { +// return; +// } +// // Don't add addselfcc if already there +// RepositoryTaskAttribute ccAttribute = getReport().getAttribute(RepositoryTaskAttribute.USER_CC); +// if (ccAttribute != null && ccAttribute.getValues().contains(repository.getUserName())) { +// return; +// } +// RepositoryTaskAttribute addselfcc = getReport().getAttribute(BugzillaReportElement.ADDSELFCC.getKeyString()); +// if (addselfcc == null) { +// // addselfcc = +// // BugzillaRepositoryUtil.makeNewAttribute(BugzillaReportElement.ADDSELFCC); +// getReport().setAttributeValue(BugzillaReportElement.ADDSELFCC.getKeyString(), "0"); +// } else { +// addselfcc.setValue("0"); +// } +// +// final Button addSelfButton = toolkit.createButton(newCommentsComposite, "Add " + repository.getUserName() +// + " to CC list", SWT.CHECK); +// +// addSelfButton.addSelectionListener(new SelectionAdapter() { +// +// @Override +// public void widgetSelected(SelectionEvent e) { +// if (addSelfButton.getSelection()) { +// getReport().setAttributeValue(BugzillaReportElement.ADDSELFCC.getKeyString(), "1"); +// // connector.getAttributeFactory().setAttributeValue(getReport(), +// // BugzillaReportElement.ADDSELFCC.getKeyString(), "1"); +// } else { +// getReport().setAttributeValue(BugzillaReportElement.ADDSELFCC.getKeyString(), "0"); +// } +// } +// }); + } + + protected abstract void validateInput(); + + + /** + * 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 createButtonLayouts(FormToolkit toolkit, Composite formComposite) { + + Section section = toolkit.createSection(form.getBody(), ExpandableComposite.TITLE_BAR | Section.TWISTIE); + section.setText(LABEL_SECTION_ACTIONS); + section.setExpanded(true); + section.setLayout(new GridLayout()); + section.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + + section.addExpansionListener(new IExpansionListener() { + public void expansionStateChanging(ExpansionEvent e) { + form.reflow(true); + } + + public void expansionStateChanged(ExpansionEvent e) { + form.reflow(true); + } + }); + + Composite buttonComposite = toolkit.createComposite(section); + GridLayout buttonLayout = new GridLayout(); + buttonLayout.numColumns = 4; + buttonComposite.setLayout(buttonLayout); + // buttonComposite.setBackground(background); + GridData buttonData = new GridData(GridData.FILL_BOTH); + buttonData.horizontalSpan = 1; + buttonData.grabExcessVerticalSpace = false; + buttonComposite.setLayoutData(buttonData); + section.setClient(buttonComposite); + addRadioButtons(buttonComposite); + addActionButtons(buttonComposite); + } + + /** + * Adds radio buttons to this composite. + * + * @param buttonComposite + * Composite to add the radio buttons to. + */ + abstract protected void addRadioButtons(Composite buttonComposite); + + /** + * 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); + // submitButton.setFont(TEXT_FONT); + GridData submitButtonData = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); + // submitButtonData.widthHint = + // AbstractRepositoryTaskEditor.WRAP_LENGTH; + // submitButtonData.heightHint = 20; + + submitButton.setLayoutData(submitButtonData); + submitButton.addListener(SWT.Selection, new Listener() { + public void handleEvent(Event e) { + submitBug(); + } + }); + submitButton.addListener(SWT.FocusIn, new GenericListener()); + + // This is not needed anymore since we have the save working properly + // with ctrl-s and file->save + // saveButton = new Button(buttonComposite, SWT.NONE); + // saveButton.setFont(TEXT_FONT); + // GridData saveButtonData = new + // GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); + // saveButtonData.widthHint = 100; + // saveButtonData.heightHint = 20; + // saveButton.setText("Save Offline"); + // saveButton.setLayoutData(saveButtonData); + // saveButton.addListener(SWT.Selection, new Listener() { + // public void handleEvent(Event e) { + // saveBug(); + // updateEditor(); + // } + // }); + // saveButton.addListener(SWT.FocusIn, new GenericListener()); + } + + /** + * Make sure that a String that is <code>null</code> is changed to a null + * string + * + * @param text + * The text to check if it is null or not + * @return If the text is <code>null</code>, then return the null string (<code>""</code>). + * Otherwise, return the text. + */ + public static String checkText(String text) { + if (text == null) + return ""; + else + return text; + } + + /** + * @return A string to use as a title for this editor. + */ + protected abstract String getTitleString(); + + /** + * Creates an uneditable text field for displaying data. + */ + protected StyledText newLayout(Composite composite, int colSpan, String text, String style) { + GridData data = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); + data.horizontalSpan = colSpan; + + StyledText resultText; + if (style.equalsIgnoreCase(VALUE)) { + resultText = new StyledText(composite, SWT.READ_ONLY); + resultText.setText(checkText(text)); + resultText.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + StyledText c = (StyledText) e.widget; + if (c != null && !c.getSelectionText().equals("")) { + if (currentSelectedText != null && !currentSelectedText.equals(c)) { + currentSelectedText.setSelectionRange(0, 0); + } + currentSelectedText = c; + } + + } + }); + resultText.setLayoutData(data); + } else if (style.equalsIgnoreCase(PROPERTY)) { + resultText = new StyledText(composite, SWT.READ_ONLY); + resultText.setText(checkText(text)); + resultText.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + StyledText c = (StyledText) e.widget; + if (c != null && !c.getSelectionText().equals("")) { + if (currentSelectedText != null && !currentSelectedText.equals(c)) { + currentSelectedText.setSelectionRange(0, 0); + } + currentSelectedText = c; + } + + } + }); + resultText.setLayoutData(data); + } else { + resultText = new StyledText(composite, SWT.READ_ONLY); + resultText.setText(checkText(text)); + resultText.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + StyledText c = (StyledText) e.widget; + if (c != null && !c.getSelectionText().equals("")) { + if (currentSelectedText != null && !currentSelectedText.equals(c)) { + currentSelectedText.setSelectionRange(0, 0); + } + currentSelectedText = c; + } + + } + }); + resultText.setLayoutData(data); + } + + // composite.setMenu(contextMenuManager.createContextMenu(composite)); + return resultText; + } + + protected TextViewer addRepositoryText(TaskRepository repository, Composite composite, String text) { + RepositoryTextViewer commentViewer = new RepositoryTextViewer(repository, composite, SWT.MULTI | SWT.WRAP); + + IThemeManager themeManager = getSite().getWorkbenchWindow().getWorkbench().getThemeManager(); + + commentViewer.getTextWidget().setFont(themeManager.getCurrentTheme().getFontRegistry().get(REPOSITORY_TEXT_ID)); + + commentViewer.setEditable(false); + commentViewer.getTextWidget().addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + StyledText c = (StyledText) e.widget; + if (c != null && !c.getSelectionText().equals("")) { + if (currentSelectedText != null && !currentSelectedText.equals(c)) { + currentSelectedText.setSelectionRange(0, 0); + } + currentSelectedText = c; + } + + } + }); + + commentViewer.getTextWidget().setMenu(contextMenuManager.createContextMenu(commentViewer.getTextWidget())); + + // textViewer.getControl().setFont(COMMENT_FONT); + commentViewer.setDocument(new Document(text)); + // commentViewer.activatePlugins(); + // textViewer.refresh(); + return commentViewer; + } + + /** + * This refreshes the text in the title label of the info area (it contains + * elements which can change). + */ + protected void setGeneralTitleText() { + // String text = "[Open in Internal Browser]"; + // linkToBug.setText(text); + // linkToBug.setFont(TEXT_FONT); + // if (this instanceof ExistingBugEditor) { + // linkToBug.setUnderlined(true); + // linkToBug.setForeground(JFaceColors.getHyperlinkText(Display.getCurrent())); + // linkToBug.addMouseListener(new MouseListener() { + // + // public void mouseDoubleClick(MouseEvent e) { + // } + // + // public void mouseUp(MouseEvent e) { + // } + // + // public void mouseDown(MouseEvent e) { + // TaskListUiUtil.openUrl(getTitle(), getTitleToolTip(), + // BugzillaRepositoryUtil.getBugUrlWithoutLogin( + // bugzillaInput.getBug().getRepositoryUrl(), + // bugzillaInput.getBug().getId())); + // if (e.stateMask == SWT.MOD3) { + // // XXX come back to look at this ui + // close(); + // } + // + // } + // }); + // } else { + // linkToBug.setEnabled(false); + // } + // linkToBug.addListener(SWT.FocusIn, new GenericListener()); + // + // // Resize the composite, in case the new summary is longer than the + // // previous one. + // // Then redraw it to show the changes. + // linkToBug.getParent().pack(true); + // linkToBug.redraw(); + + // String text = getTitleString(); + // generalTitleText.setText(text); + // StyleRange sr = new StyleRange(generalTitleText.getOffsetAtLine(0), + // text.length(), foreground, background, + // SWT.BOLD); + // generalTitleText.setStyleRange(sr); + // generalTitleText.addListener(SWT.FocusIn, new GenericListener()); + // + // // Resize the composite, in case the new summary is longer than the + // // previous one. + // // Then redraw it to show the changes. + // generalTitleText.getParent().pack(true); + // generalTitleText.redraw(); + } + + /** + * Creates some blank space underneath the supplied composite. + * + * @param parent + * The composite to add the blank space to. + */ + protected void createSeparatorSpace(Composite parent) { + GridData separatorData = new GridData(GridData.HORIZONTAL_ALIGN_FILL); + separatorData.verticalSpan = 1; + separatorData.grabExcessVerticalSpace = false; + + Composite separatorComposite = new Composite(parent, SWT.NONE); + GridLayout separatorLayout = new GridLayout(); + separatorLayout.marginHeight = 0; + separatorLayout.verticalSpacing = 0; + separatorComposite.setLayout(separatorLayout); + // separatorComposite.setBackground(background); + separatorComposite.setLayoutData(separatorData); + newLayout(separatorComposite, 1, "", VALUE); + } + + /** + * Submit the changes to the bug to the bugzilla server. (Public for testing + * purposes) + */ + protected abstract void submitBug(); + + /** + * If there is no locally saved copy of the current bug, then it saved + * offline. Otherwise, any changes are updated in the file. + */ + public void saveBug() { + try { + updateBug(); + + final AbstractRepositoryConnector bugzillaRepositoryClient = (AbstractRepositoryConnector) MylarTaskListPlugin + .getRepositoryManager().getRepositoryConnector(getRepositoryTaskData().getRepositoryKind()); + + IEditorInput input = this.getEditorInput(); + if (input instanceof ExistingBugEditorInput) { + ExistingBugEditorInput existingInput = (ExistingBugEditorInput) input; + AbstractRepositoryTask repositoryTask = existingInput.getRepositoryTask(); + // AbstractRepositoryTask repositoryTask = getRepositoryTask(); + if (repositoryTask != null) { + if (getRepositoryTaskData().hasChanges()) { + repositoryTask.setSyncState(RepositoryTaskSyncState.OUTGOING); + } else { + repositoryTask.setSyncState(RepositoryTaskSyncState.SYNCHRONIZED); + } + MylarTaskListPlugin.getTaskListManager().getTaskList().notifyRepositoryInfoChanged(repositoryTask); + } + } + + bugzillaRepositoryClient.saveOffline(getRepositoryTaskData()); + changeDirtyStatus(false); + if (parentEditor != null) { + parentEditor.notifyTaskChanged(); + } + } catch (Exception e) { + MylarStatusHandler.fail(e, "bug save offline failed", true); + } + + } + + /** + * Updates the <code>IBugzillaBug</code> object to contain the latest data + * entered in the data fields. + */ + protected abstract void updateBug(); + +// /** +// * Resets the data fields to contain the data currently in the +// * <code>IBugzillaBug</code> object. +// */ +// protected abstract void restoreBug(); + + /** + * Refreshes any text labels in the editor that contain information that + * might change. + */ + protected void updateEditor() { + // Reset all summary occurrences, since it might have + // been edited. + // String title = getTitleString(); + // titleLabel.setText(title); + setGeneralTitleText(); + } + + @Override + public void setFocus() { + form.setFocus(); + } + + @Override + public boolean isDirty() { + return isDirty; + } + + /** + * Updates the dirty status of this editor page. The dirty status is true if + * the bug report has been modified but not saved. The title of the editor + * is also updated to reflect the status. + * + * @param newDirtyStatus + * is true when the bug report has been modified but not saved + */ + public void changeDirtyStatus(boolean newDirtyStatus) { + isDirty = newDirtyStatus; + if (parentEditor == null) { + firePropertyChange(PROP_DIRTY); + } else { + parentEditor.markDirty(); + } + + } + + /** + * Updates the title of the editor to reflect dirty status. If the bug + * report has been modified but not saved, then an indicator will appear in + * the title. + */ + protected void updateEditorTitle() { + setPartName(editorInput.getName()); + } + + @Override + public boolean isSaveAsAllowed() { + return false; + } + + @Override + public void doSave(IProgressMonitor monitor) { + saveBug(); + updateEditor(); + } + + @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() { + super.dispose(); + isDisposed = true; + getSite().getPage().removeSelectionListener(selectionListener); + } + + // public void handleEvent(Event event) { + // if (event.widget instanceof CCombo) { + // CCombo combo = (CCombo) event.widget; + // if (comboListenerMap.containsKey(combo)) { + // if (combo.getSelectionIndex() > -1) { + // String sel = combo.getItem(combo.getSelectionIndex()); + // Attribute attribute = getBug().getAttribute(comboListenerMap.get(combo)); + // if (sel != null && !(sel.equals(attribute.getNewValue()))) { + // attribute.setNewValue(sel); + // for (IRepositoryTaskAttributeListener client : attributesListeners) { + // client.attributeChanged(attribute.getName(), sel); + // } + // changeDirtyStatus(true); + // } + // } + // } + // } + // } + + /** + * 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 (int i = 0; i < listeners.length; i++) { + final ISelectionChangedListener l = (ISelectionChangedListener) listeners[i]; + SafeRunnable.run(new SafeRunnable() { + public void run() { + l.selectionChanged(event); + } + }); + } + } + + /** + * A generic listener for selection of unimportant items. The default + * selection item sent out is the entire bug object. + */ + public class GenericListener implements Listener { + public void handleEvent(Event event) { + RepositoryTaskData bug = (RepositoryTaskData) getRepositoryTaskData(); + fireSelectionChanged(new SelectionChangedEvent(selectionProvider, new StructuredSelection( + new RepositoryTaskSelection(bug.getId(), bug.getRepositoryUrl(), bug.getLabel(), false, bug + .getSummary())))); + } + } + + /** + * A listener to check if the summary field was modified. + */ + protected class SummaryListener implements Listener { + public void handleEvent(Event event) { + handleSummaryEvent(); + } + } + + /** + * Check if the summary field was modified, and update it if necessary. + */ + public void handleSummaryEvent() { + String sel = summaryText.getText(); + RepositoryTaskAttribute a = getRepositoryTaskData().getAttribute(RepositoryTaskAttribute.SUMMARY); + if (!(a.getValue().equals(sel))) { + a.setValue(sel); + changeDirtyStatus(true); + } + } + + /*----------------------------------------------------------* + * CODE TO SCROLL TO A COMMENT OR OTHER PIECE OF TEXT + *----------------------------------------------------------*/ + + /** List of the StyledText's so that we can get the previous and the next */ + protected ArrayList<StyledText> texts = new ArrayList<StyledText>(); + + protected HashMap<Object, StyledText> textHash = new HashMap<Object, StyledText>(); + + protected List<StyledText> commentStyleText = new ArrayList<StyledText>(); + + /** Index into the styled texts */ + protected int textsindex = 0; + + protected Text addCommentsTextBox = null; + + protected Text descriptionTextBox = null; + + // private FormText previousText = null; + + /** + * Selects the given object in the editor. + * + * @param commentNumber + * The comment number to be selected + */ + public void select(int commentNumber) { + if (commentNumber == -1) + return; + + for (Object o : textHash.keySet()) { + if (o instanceof Comment) { + if (((Comment) o).getNumber() == commentNumber) { + select(o, true); + } + } + } + } + + public void revealAllComments() { + for (StyledText text : commentStyleText) { + Composite comp = text.getParent(); + while (comp != null) { + if (comp instanceof ExpandableComposite) { + ExpandableComposite ex = (ExpandableComposite) comp; + ex.setExpanded(true); + } + comp = comp.getParent(); + } + } + } + + /** + * 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 void select(Object o, boolean highlight) { + if (textHash.containsKey(o)) { + StyledText t = textHash.get(o); + if (t != null) { + Composite comp = t.getParent(); + while (comp != null) { + if (comp instanceof ExpandableComposite) { + ExpandableComposite ex = (ExpandableComposite) comp; + ex.setExpanded(true); + } + comp = comp.getParent(); + } + focusOn(t, highlight); + } + } else if (o instanceof RepositoryTaskData) { + focusOn(null, highlight); + } + } + + public void selectDescription() { + for (Object o : textHash.keySet()) { + if (o.equals(editorInput.getRepositoryTaskData().getDescription())) { + select(o, true); + } + } + } + + public void selectNewComment() { + focusOn(addCommentsTextBox, false); + } + + public void selectNewDescription() { + focusOn(descriptionTextBox, 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; + + @Override + public Object getAdapter(Class adapter) { + if (IContentOutlinePage.class.equals(adapter)) { + if (outlinePage == null && editorInput != null) { + outlinePage = new RepositoryTaskOutlinePage(bugzillaOutlineModel); + } + return outlinePage; + } + return super.getAdapter(adapter); + } + + public RepositoryTaskOutlineNode getOutlineModel() { + return bugzillaOutlineModel; + } + + public RepositoryTaskOutlinePage getOutline() { + return outlinePage; + } + + private boolean isDisposed = false; + + public boolean isDisposed() { + return isDisposed; + } + + public void close() { + Display activeDisplay = getSite().getShell().getDisplay(); + activeDisplay.asyncExec(new Runnable() { + public void run() { + if (getSite() != null && getSite().getPage() != null && !AbstractRepositoryTaskEditor.this.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(MylarTaskEditor parentEditor) { + this.parentEditor = parentEditor; + } + + public RepositoryTaskOutlineNode getBugzillaOutlineModel() { + return bugzillaOutlineModel; + } + + public void setBugzillaOutlineModel(RepositoryTaskOutlineNode bugzillaOutlineModel) { + this.bugzillaOutlineModel = bugzillaOutlineModel; + } + + /** + * A listener for selection of the textbox where a new comment is entered + * in. + */ + protected class NewCommentListener implements Listener { + public void handleEvent(Event event) { + fireSelectionChanged(new SelectionChangedEvent(selectionProvider, new StructuredSelection( + new RepositoryTaskSelection(getRepositoryTaskData().getId(), getRepositoryTaskData().getRepositoryUrl(), "New Comment", false, + getRepositoryTaskData().getSummary())))); + } + } + + // private void addHyperlinks(final StyledText styledText, Composite + // composite) { + // + // StringMatcher javaElementMatcher = new StringMatcher("*(*.java:*)", true, + // false); + // String[] lines = styledText.getText().split("\r\n|\n"); + // + // int totalLength = 0; + // for (int x = 0; x < lines.length; x++) { + // + // String line = lines[x]; + // Position position = javaElementMatcher.find(line, 0, line.length()); + // if (position != null) { + // String linkText = line.substring(position.getStart() + 1, + // position.getEnd() - 1); + // // Link hyperlink = new Link(styledText, SWT.NONE); + // IRegion region = new Region(styledText.getText().indexOf(line) + + // position.getStart(), position.getEnd() + // - position.getStart()); + // addControl(styledText, region, linkText, line, HYPERLINK_TYPE_JAVA); + // } + // + // IHyperlink[] bugHyperlinks = BugzillaUITools.findBugHyperlinks(0, + // line.length(), line, 0); + // if (bugHyperlinks != null) { + // for (IHyperlink hyperlink : bugHyperlinks) { + // String linkText = hyperlink.getHyperlinkText(); + // int index = linkText.lastIndexOf('='); + // if (index >= 0) { + // String taskId = linkText.substring(index + 1); + // String href = repository.getUrl() + hyperlink.getHyperlinkText(); + // addControl(styledText, hyperlink.getHyperlinkRegion(), "bug# " + taskId, + // href, + // HYPERLINK_TYPE_TASK); + // } + // + // } + // } + // + // totalLength = totalLength + line.length(); + // + // } // bottom of for loop + // + // // reposition widgets on paint event + // styledText.addPaintObjectListener(new PaintObjectListener() { + // public void paintObject(PaintObjectEvent event) { + // StyleRange style = event.style; + // int start = style.start; + // Map<Integer, Control> controlMap = controls.get(styledText); + // Control control = controlMap.get(start); + // if (control != null) { + // Point pt = control.getSize(); + // int x = event.x + MARGIN; + // int y = event.y + event.ascent - 2 * pt.y / 3; + // control.setLocation(x, y); + // } + // } + // }); + // } + + // private void addControl(final StyledText styledText, IRegion region, + // String linkText, String href, + // final String listenerType) { + // Hyperlink hyperlink = toolkit.createHyperlink(styledText, linkText, + // SWT.NONE); + // hyperlink.setText(linkText); + // hyperlink.setFont(COMMENT_FONT); + // hyperlink.setHref(href); + // IHyperlinkListener hyperlinkListener = + // MylarTaskListPlugin.getDefault().getTaskHyperlinkListeners().get( + // listenerType); + // if (hyperlinkListener != null) { + // hyperlink.addHyperlinkListener(hyperlinkListener); + // } + // Map<Integer, Control> controlMap = controls.get(styledText); + // if (controlMap == null) { + // controlMap = new HashMap<Integer, Control>(); + // controls.put(styledText, controlMap); + // } + // controlMap.put(new Integer(region.getOffset()), hyperlink); + // StyleRange style = new StyleRange(); + // style.start = region.getOffset(); + // style.length = region.getLength(); + // hyperlink.pack(); + // Rectangle rect = hyperlink.getBounds(); + // int ascent = 2 * rect.height / 3; + // int descent = rect.height - ascent; + // style.metrics = new GlyphMetrics(ascent + MARGIN, descent + MARGIN, + // rect.width + 2 * MARGIN); + // styledText.setStyleRange(style); + // } +} diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/ExistingBugEditorInput.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/ExistingBugEditorInput.java new file mode 100644 index 000000000..3c4a57ca9 --- /dev/null +++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/ExistingBugEditorInput.java @@ -0,0 +1,195 @@ +/******************************************************************************* + * Copyright (c) 2003 - 2006 University Of British Columbia 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: + * University Of British Columbia - initial API and implementation + *******************************************************************************/ +package org.eclipse.mylar.internal.tasklist.ui.editors; + +import java.io.IOException; +import java.net.Proxy; +import java.security.GeneralSecurityException; + +import org.eclipse.mylar.internal.core.util.MylarStatusHandler; +import org.eclipse.mylar.internal.tasklist.OfflineTaskManager; +import org.eclipse.mylar.internal.tasklist.RepositoryTaskData; +import org.eclipse.mylar.provisional.tasklist.AbstractRepositoryTask; +import org.eclipse.mylar.provisional.tasklist.ITask; +import org.eclipse.mylar.provisional.tasklist.MylarTaskListPlugin; +import org.eclipse.mylar.provisional.tasklist.TaskRepository; + +/** + * The <code>IEditorInput</code> implementation for + * <code>ExistingBugEditor</code>. + * + * @author Mik Kersten + * @author Rob Elves + */ +public class ExistingBugEditorInput extends AbstractBugEditorInput { + + private TaskRepository repository; + + protected int bugId; + protected AbstractRepositoryTask repositoryTask; + protected RepositoryTaskData repositoryTaskData; + + // Called for new bug reports + public ExistingBugEditorInput(TaskRepository repository, RepositoryTaskData bug) { + this.repositoryTaskData = bug; + this.bugId = bug.getId(); + this.repository = repository; + } + + +// public ExistingBugEditorInput(TaskRepository repository, AbstractRepositoryTask task) throws IOException, GeneralSecurityException { +// this.repositoryTask = task; +// this.repository = repository; +// this.repositoryTaskData = task.getTaskData(); +// //bug = BugzillaRepositoryUtil.getBug(repository.getUrl(), repository.getUserName(), repository.getPassword(), proxySettings, repository.getCharacterEncoding(), bugId); +// } + + public ExistingBugEditorInput(TaskRepository repository, int bugId) throws IOException, GeneralSecurityException { + this.bugId = bugId; + this.repository = repository; + this.repositoryTaskData = getOfflineTaskData(repository, proxySettings, bugId); + + + String handle = AbstractRepositoryTask.getHandle(repository.getUrl(), bugId); + ITask task = MylarTaskListPlugin.getTaskListManager().getTaskList().getTask(handle); + if(task != null && task instanceof AbstractRepositoryTask) { + this.repositoryTask = (AbstractRepositoryTask)task; + } else { + MylarStatusHandler.log("Could not locate repository task", this); + } + + + + //bug = BugzillaRepositoryUtil.getBug(repository.getUrl(), repository.getUserName(), repository.getPassword(), proxySettings, repository.getCharacterEncoding(), bugId); + } + + public AbstractRepositoryTask getRepositoryTask() { + return repositoryTask; + } + + @Override + public RepositoryTaskData getRepositoryTaskData() { + return repositoryTaskData; + } +// public ExistingBugEditorInput(TaskRepository repository, int bugId, boolean offline) throws IOException, GeneralSecurityException { +// this.bugId = bugId; +// this.repository = repository; +// this.repositoryTaskData = getOfflineTaskData(repository, proxySettings, bugId); +// // if (!offline) { +// // try { +// // bug = BugzillaRepositoryUtil.getBug(repository.getUrl(), +// // repository.getUserName(), repository.getPassword(), proxySettings, +// // repository.getCharacterEncoding(), bugId); +// // } catch (IOException e) { +// // bug = getCurrentBug(repository, proxySettings, bugId); +// // // IWorkbench workbench = PlatformUI.getWorkbench(); +// // // workbench.getDisplay().asyncExec(new Runnable() { +// // // public void run() { +// // // MessageDialog.openInformation( +// // // Display.getDefault().getActiveShell(), +// // // "Mylar Bugzilla Client", +// // // "Unable to download bug report, using offline copy."); +// // // +// // // } +// // // }); +// // } +// // } else { +// // bug = getCurrentBug(repository, proxySettings, bugId); +// // } +// } + + // TODO: move? + private RepositoryTaskData getOfflineTaskData(final TaskRepository repository, Proxy proxySettings, final int id) + throws IOException, GeneralSecurityException { + RepositoryTaskData result = null; + // Look among the offline reports for a bug with the given id. + OfflineTaskManager reportsFile = MylarTaskListPlugin.getDefault().getOfflineReportsFile(); + if (reportsFile != null) { + int offlineId = reportsFile.find(repository.getUrl(), id); + // If an offline bug was found, return it if possible. + if (offlineId != -1) { + RepositoryTaskData bug = reportsFile.elements().get(offlineId); + if (bug instanceof RepositoryTaskData) { + result = (RepositoryTaskData) bug; + } + } + } + + // If a suitable offline report was not found, get it from the server +// if(result == null) { +// try { +// result = BugzillaRepositoryUtil.getBug(repository.getUrl(), repository.getUserName(), repository.getPassword(), proxySettings, repository.getCharacterEncoding(), id); +// } catch (final LoginException e) { +// PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { +// public void run() { +// MessageDialog.openError(Display.getDefault().getActiveShell(), "Report Download Failed", +// "Ensure proper repository configuration of " + repository.getUrl() + " in " +// + TaskRepositoriesView.NAME + "."); +// } +// }); +// } catch (final UnrecognizedBugzillaError e) { +// PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { +// public void run() { +// WebBrowserDialog.openAcceptAgreement(null, "Report Download Failed", "Unrecognized response from " +// + repository.getUrl(), e.getMessage()); +// } +// }); +// } catch (final Exception e) { +// if (PlatformUI.getWorkbench() != null && !PlatformUI.getWorkbench().isClosing()) { +// PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { +// public void run() { +// if (e instanceof FileNotFoundException) { +// MessageDialog.openError(PlatformUI.getWorkbench().getDisplay().getActiveShell(), "Report Download Failed", +// "Resource not found: " + e.getMessage()); +// +// } else { +// MessageDialog.openError(PlatformUI.getWorkbench().getDisplay().getActiveShell(), "Report Download Failed", +// "Report "+id+" did not download correctly from " + repository.getUrl()); +// +// } +// } +// }); +// } +// } +// } + return result; + } + + public String getName() { + return repositoryTaskData.getLabel(); + } + + /** + * @return The id of the bug for this editor input. + */ + public int getBugId() { + return bugId; + } + + + /** + * @return <code>true</code> if the argument is a bug report editor input + * on the same bug id. + */ + @Override + public boolean equals(Object o) { + if (o instanceof ExistingBugEditorInput) { + ExistingBugEditorInput input = (ExistingBugEditorInput) o; + return getBugId() == input.getBugId(); + } + return false; + } + + public TaskRepository getRepository() { + return repository; + } + +} diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/IRepositoryTaskAttributeListener.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/IRepositoryTaskAttributeListener.java new file mode 100644 index 000000000..199926120 --- /dev/null +++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/IRepositoryTaskAttributeListener.java @@ -0,0 +1,19 @@ +/******************************************************************************* + * Copyright (c) 2004 - 2006 University Of British Columbia 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: + * University Of British Columbia - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylar.internal.tasklist.ui.editors; + +/** + * @author Ken Sueda + */ +public interface IRepositoryTaskAttributeListener { + public abstract void attributeChanged(String attribute, String value); +} diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/IRepositoryTaskSelection.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/IRepositoryTaskSelection.java new file mode 100644 index 000000000..09825e830 --- /dev/null +++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/IRepositoryTaskSelection.java @@ -0,0 +1,89 @@ +/******************************************************************************* + * Copyright (c) 2004 - 2006 University Of British Columbia 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: + * University Of British Columbia - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylar.internal.tasklist.ui.editors; + +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.mylar.internal.tasklist.Comment; + +/** + * Interface for a selection of a Bugzilla element in a view. + */ +public interface IRepositoryTaskSelection extends ISelection { + + /** + * @return <code>true</code> if a comment was selected. + */ + public boolean hasComment(); + + /** + * @return the <code>Comment</code> object for this selection, or + * <code>null</code> if a comment was not selected. + */ + public Comment getComment(); + + /** + * Sets the <code>Comment</code> object for this selection. If a comment + * was not selected, then this should be <code>null</code>. + * + * @param comment + * The selection's comment, or <code>null</code> if not + * applicable. + */ + public void setComment(Comment comment); + + /** + * @return The contents of the selection. This can be <code>null</code>. + */ + public String getContents(); + + /** + * Sets the contents of the selection. + * + * @param contents + * The selection. + */ + public void setContents(String contents); + + /** + * @return The id of the Bugzilla object that the selection was on. + */ + public int getId(); + + /** + * Sets the id of the Bugzilla object that the selection was on. + * + * @param id + * The id of the bug. + */ + public void setId(int id); + + /** + * @return The server of the Bugzilla object that the selection was on, or + * <code>null</code> if no server is supplied. + */ + public String getServer(); + + /** + * Sets the server of the Bugzilla object that the selection was on. + * + * @param server + * The server of the bug. + */ + public void setServer(String server); + + public boolean isCommentHeader(); + + public boolean isDescription(); + + public String getBugSummary(); + +} diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/OutlineTools.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/OutlineTools.java new file mode 100644 index 000000000..55fdeb523 --- /dev/null +++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/OutlineTools.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2003 - 2006 University Of British Columbia 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: + * University Of British Columbia - initial API and implementation + *******************************************************************************/ +package org.eclipse.mylar.internal.tasklist.ui.editors; + +import org.eclipse.mylar.internal.tasklist.RepositoryTaskData; + +public class OutlineTools { + + /** The default string used for locally created bugs. */ + public static final String OFFLINE_SERVER_DEFAULT = "[local]"; + + /** + * Returns a unique handle for the bugzilla selection. Contains the bug id, + * the bug server, and (if applicable) the comment number. + * + * @param bugSel + * The bugzilla selection. + * @return The handle for the bugzilla selection. + */ + public static String getHandle(IRepositoryTaskSelection bugSel) { + String handle = bugSel.getServer() + ";" + bugSel.getId(); + if (bugSel.hasComment()) { + int number = bugSel.getComment().getNumber() + 1; + handle += ";" + number; + } else if (bugSel.isCommentHeader()) { + handle += ";1"; + } else if (bugSel.isDescription()) { + handle += ";0"; + } + return handle; + } + + public static String getName(IRepositoryTaskSelection bugSel) { + String name = bugSel.getServer() + ": Bug#: " + bugSel.getId() + ": " + bugSel.getBugSummary(); + if (bugSel.hasComment()) { + name += " : Comment#: " + bugSel.getComment().getNumber(); + } else if (bugSel.isCommentHeader()) { + name += " : Comment Header"; + } else if (bugSel.isDescription()) { + name += ": Description"; + } + return name; + } + + public static String getHandle(RepositoryTaskData bug) { + return getHandle(bug.getRepositoryUrl(), bug.getId()); + } + + public static String getHandle(String server, int id) { + return server + ";" + id; + } + + public static String getName(RepositoryTaskData bug) { + return bug.getRepositoryUrl() + ": Bug#: " + bug.getId() + ": " + bug.getSummary(); + } + +} diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/RepositoryTaskEditorCopyAction.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/RepositoryTaskEditorCopyAction.java new file mode 100644 index 000000000..322ac2af4 --- /dev/null +++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/RepositoryTaskEditorCopyAction.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2004 - 2006 University Of British Columbia 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: + * University Of British Columbia - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylar.internal.tasklist.ui.editors; + +import org.eclipse.jface.action.Action; + +/** + * Action used to copy selected text from a bug editor to the clipboard. + */ +public class RepositoryTaskEditorCopyAction extends Action { + /** The editor to copy text selections from. */ + private AbstractRepositoryTaskEditor bugEditor; + + /** + * Creates a new <code>RepositoryTaskEditorCopyAction</code>. + * + * @param editor + * The editor that this action is copying text selections from. + */ + public RepositoryTaskEditorCopyAction(AbstractRepositoryTaskEditor editor) { + bugEditor = editor; + setText("AbstractRepositoryTaskEditor.copy.text"); + } + + @Override + public void run() { + bugEditor.getCurrentText().copy(); + } + +} diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/RepositoryTaskOutlineComparer.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/RepositoryTaskOutlineComparer.java new file mode 100644 index 000000000..b9f6405e2 --- /dev/null +++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/RepositoryTaskOutlineComparer.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2004 - 2006 University Of British Columbia 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: + * University Of British Columbia - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylar.internal.tasklist.ui.editors; + +import org.eclipse.jface.viewers.IElementComparer; + +/** + * This class is used to compare two <code>IRepositoryTaskSelection</code> + * objects. + * + * @see IElementComparer + * @see IRepositoryTaskSelection + */ +public class RepositoryTaskOutlineComparer implements IElementComparer { + + public boolean equals(Object a, Object b) { + if ((a instanceof IRepositoryTaskSelection) && (b instanceof IRepositoryTaskSelection)) { + IRepositoryTaskSelection s1 = (IRepositoryTaskSelection) a; + IRepositoryTaskSelection s2 = (IRepositoryTaskSelection) b; + + // An IRepositoryTaskSelection is uniquely defined by its handle and + // its contents + return ((OutlineTools.getHandle(s1).equals(OutlineTools.getHandle(s2))) && ((s1.getContents() == null) ? (s2 + .getContents() == null) + : s1.getContents().equals(s2.getContents()))); + } + return a.equals(b); + } + + public int hashCode(Object element) { + if (element instanceof IRepositoryTaskSelection) { + IRepositoryTaskSelection sel = (IRepositoryTaskSelection) element; + + // An IRepositoryTaskSelection is uniquely defined by its handle and + // its contents + return (OutlineTools.getHandle(sel) + sel.getContents()).hashCode(); + } + return element.hashCode(); + } +} diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/RepositoryTaskOutlineNode.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/RepositoryTaskOutlineNode.java new file mode 100644 index 000000000..562e4b923 --- /dev/null +++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/RepositoryTaskOutlineNode.java @@ -0,0 +1,356 @@ +/******************************************************************************* + * Copyright (c) 2003 - 2006 University Of British Columbia 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: + * University Of British Columbia - initial API and implementation + *******************************************************************************/ +package org.eclipse.mylar.internal.tasklist.ui.editors; + +import java.util.ArrayList; +import java.util.Iterator; + +import org.eclipse.mylar.internal.tasklist.Comment; +import org.eclipse.mylar.internal.tasklist.RepositoryTaskData; +import org.eclipse.mylar.internal.tasklist.ui.TaskListImages; +import org.eclipse.swt.graphics.Image; + +/** + * A node for the tree in the <code>RepositoryTaskOutlinePage</code>. + * + * @author Mik Kersten (hardening of prototype) + */ +public class RepositoryTaskOutlineNode implements IRepositoryTaskSelection { + + /** The id of the Bugzilla object that the selection was on. */ + protected int id; + + /** The server of the Bugzilla object that the selection was on. */ + protected String server; + + /** The label for this piece of data. */ + private String key; + + /** The children of this node. */ + private ArrayList<RepositoryTaskOutlineNode> nodeChildren; + + /** The parent of this node or null if it is the bug report */ + private RepositoryTaskOutlineNode parent; + + /** This node's image. */ + private Image image; + + private Object data = null; + + private String bugSummary; + + private boolean fromEditor = false; + + private boolean isCommentHeader = false; + + private boolean isDescription = false; + + /** + * Creates a new <code>RepositoryTaskOutlineNode</code>. + * + * @param id + * The id of the bug this outline is for. + * @param server + * The server of the bug this outline is for. + * @param key + * The label for this node. + * @param image + * The image that will be displayed by this node in the tree. + * @param data + * The data, if necessary, this node represents. + * @param parent + * The parent of this node + */ + public RepositoryTaskOutlineNode(int id, String server, String key, Image image, Object data, String summary) { + this.id = id; + this.server = server; + this.key = key; + this.nodeChildren = null; + this.image = image; + this.data = data; + this.parent = null; + this.bugSummary = summary; + } + + public boolean isFromEditor() { + return fromEditor; + } + + /** + * @return The children of this node, represented as an <code>Object</code> + * array. + */ + public RepositoryTaskOutlineNode[] getChildren() { + return (nodeChildren == null) ? new RepositoryTaskOutlineNode[0] : nodeChildren + .toArray(new RepositoryTaskOutlineNode[nodeChildren.size()]); + } + + /** + * Adds a node to this node's list of children. + * + * @param bugNode + * The new child. + */ + public void addChild(RepositoryTaskOutlineNode bugNode) { + if (nodeChildren == null) { + nodeChildren = new ArrayList<RepositoryTaskOutlineNode>(); + } + bugNode.setParent(this); + nodeChildren.add(bugNode); + } + + /** + * @return The label of this node. + */ + public String getKey() { + return key; + } + + // /** + // * Set the label of this node. + // * @param key The new label. + // */ + // public void setKey(String key) { + // this.key = key; + // } + + /** + * TODO: remove, nodes don't need to know about image decorator + */ + public Image getImage() { + return image; + } + + /** + * Sets the decorator image for this node. + * + * @param newImage + * The new image. + */ + public void setImage(Image newImage) { + this.image = newImage; + } + + /** + * @return <code>true</code> if the given object is another node + * representing the same piece of data in the editor. + */ + @Override + public boolean equals(Object arg0) { + if (arg0 instanceof RepositoryTaskOutlineNode) { + RepositoryTaskOutlineNode bugNode = (RepositoryTaskOutlineNode) arg0; + return getKey().equals(bugNode.getKey()); + } + return super.equals(arg0); + } + + @Override + public int hashCode() { + return getKey().hashCode(); + } + + /** + * @return The name of this node. + */ + public String getName() { + return getKey(); + } + + /** + * @return The data (where applicable) this node represents. + */ + public Object getData() { + return data; + } + + /** + * Sets the data that this node represents. + * + * @param data + * The new piece of data. + */ + public void setData(Object data) { + this.data = data; + } + + /** + * Parses the given <code>IBugzillaBug</code> into a tree of + * <code>RepositoryTaskOutlineNode</code>'s suitable for use in the + * <code>RepositoryTaskOutlinePage</code> view. + * + * @param bug + * The bug that needs parsing. + * @return The tree of <code>RepositoryTaskOutlineNode</code>'s. + */ + public static RepositoryTaskOutlineNode parseBugReport(RepositoryTaskData bug) { + // Choose the appropriate parsing function based on + // the type of IBugzillaBug. +// if (bug instanceof NewBugzillaReport) { +// return parseNewBugReport((NewBugzillaReport) bug); +// } else + if (bug instanceof RepositoryTaskData) { + return parseExistingBugReport((RepositoryTaskData) bug); + } else { + return null; + } + } + +// /** +// * Parses the given <code>NewBugModel</code> into a tree of +// * <code>RepositoryTaskOutlineNode</code>'s suitable for use in the +// * <code>RepositoryTaskOutlinePage</code> view. +// * +// * @param bug +// * The <code>NewBugModel</code> that needs parsing. +// * @return The tree of <code>RepositoryTaskOutlineNode</code>'s. +// */ +// protected static RepositoryTaskOutlineNode parseNewBugReport(NewBugzillaReport bug) { +// int bugId = bug.getId(); +// String bugServer = bug.getRepositoryUrl(); +// Image bugImage = BugzillaImages.getImage(BugzillaImages.BUG); +// Image defaultImage = BugzillaImages.getImage(BugzillaImages.BUG_COMMENT); +// RepositoryTaskOutlineNode topNode = new RepositoryTaskOutlineNode(bugId, bugServer, bug.getLabel(), bugImage, bug, bug +// .getSummary()); +// +// topNode.addChild(new RepositoryTaskOutlineNode(bugId, bugServer, "New Description", defaultImage, null, bug +// .getSummary())); +// +// RepositoryTaskOutlineNode titleNode = new RepositoryTaskOutlineNode(bugId, bugServer, "NewBugModel Object", defaultImage, +// null, bug.getSummary()); +// titleNode.addChild(topNode); +// +// return titleNode; +// } + + /** + * Parses the given <code>BugReport</code> into a tree of + * <code>RepositoryTaskOutlineNode</code>'s suitable for use in the + * <code>RepositoryTaskOutlinePage</code> view. + * + * @param bug + * The <code>BugReport</code> that needs parsing. + * @return The tree of <code>RepositoryTaskOutlineNode</code>'s. + */ + protected static RepositoryTaskOutlineNode parseExistingBugReport(RepositoryTaskData bug) { + + int bugId = bug.getId(); + String bugServer = bug.getRepositoryUrl(); + Image bugImage = TaskListImages.getImage(TaskListImages.TASK_REMOTE); + //MylarTaskListPlugin.getDefault().BugzillaImages.getImage(BugzillaImages.BUG); + Image defaultImage = TaskListImages.getImage(TaskListImages.TASK_NOTES); + //BugzillaImages.getImage(BugzillaImages.BUG_COMMENT); + RepositoryTaskOutlineNode topNode = new RepositoryTaskOutlineNode(bugId, bugServer, bug.getLabel(), bugImage, bug, bug + .getSummary()); + + RepositoryTaskOutlineNode desc = new RepositoryTaskOutlineNode(bugId, bugServer, "Description", defaultImage, bug + .getDescription(), bug.getSummary()); + desc.setIsDescription(true); + + topNode.addChild(desc); + + RepositoryTaskOutlineNode comments = null; + for (Iterator<Comment> iter = bug.getComments().iterator(); iter.hasNext();) { + Comment comment = iter.next(); + // first comment is the bug description + if(comment.getNumber() == 0) continue; + if (comments == null) { + comments = new RepositoryTaskOutlineNode(bugId, bugServer, "Comments", defaultImage, comment, bug + .getSummary()); + comments.setIsCommentHeader(true); + } + comments.addChild(new RepositoryTaskOutlineNode(bugId, bugServer, comment.getCreated().toString(), defaultImage, + comment, bug.getSummary())); + } + if (comments != null) { + topNode.addChild(comments); + } + + topNode + .addChild(new RepositoryTaskOutlineNode(bugId, bugServer, "New Comment", defaultImage, null, bug.getSummary())); + + RepositoryTaskOutlineNode titleNode = new RepositoryTaskOutlineNode(bugId, bugServer, "BugReport Object", defaultImage, + null, bug.getSummary()); + titleNode.addChild(topNode); + + return titleNode; + } + + public boolean hasComment() { + // If the comment category was selected, then the comment object is + // not the intended selection (it is just used to help find the correct + // location in the editor). + return (data instanceof Comment) && !(key.toLowerCase().equals("comments")); + } + + public Comment getComment() { + return (hasComment()) ? (Comment) data : null; + } + + public void setComment(Comment comment) { + data = comment; + } + + public String getContents() { + return key; + } + + public void setContents(String contents) { + key = contents; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getServer() { + return server; + } + + public void setServer(String server) { + this.server = server; + } + + public boolean isEmpty() { + return (server == null) || ((getContents() == null) && (getComment() == null)); + } + + public RepositoryTaskOutlineNode getParent() { + return parent; + } + + public void setParent(RepositoryTaskOutlineNode parent) { + this.parent = parent; + } + + public boolean isCommentHeader() { + return isCommentHeader; + } + + public boolean isDescription() { + return isDescription; + } + + public void setIsCommentHeader(boolean isCommentHeader) { + this.isCommentHeader = isCommentHeader; + } + + public void setIsDescription(boolean isDescription) { + this.isDescription = isDescription; + } + + public String getBugSummary() { + return bugSummary; + } +} diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/RepositoryTaskOutlinePage.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/RepositoryTaskOutlinePage.java new file mode 100644 index 000000000..351941349 --- /dev/null +++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/RepositoryTaskOutlinePage.java @@ -0,0 +1,171 @@ +/******************************************************************************* + * Copyright (c) 2003 - 2006 University Of British Columbia 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: + * University Of British Columbia - initial API and implementation + *******************************************************************************/ +package org.eclipse.mylar.internal.tasklist.ui.editors; + +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.mylar.internal.core.util.MylarStatusHandler; +import org.eclipse.mylar.internal.tasklist.ui.TaskListImages; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.ui.ISelectionListener; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.views.contentoutline.ContentOutlinePage; + +/** + * An outline page for a <code>BugEditor</code>. + */ +public class RepositoryTaskOutlinePage extends ContentOutlinePage { + + private RepositoryTaskOutlineNode topTreeNode; + + protected final ISelectionListener selectionListener = new ISelectionListener() { + public void selectionChanged(IWorkbenchPart part, ISelection selection) { + if ((part instanceof AbstractRepositoryTaskEditor) && (selection instanceof IStructuredSelection)) { + if (((IStructuredSelection) selection).getFirstElement() instanceof IRepositoryTaskSelection) { + if (((IStructuredSelection) getSelection()).getFirstElement() instanceof IRepositoryTaskSelection) { + IRepositoryTaskSelection brs1 = (IRepositoryTaskSelection) ((IStructuredSelection) getSelection()) + .getFirstElement(); + IRepositoryTaskSelection brs2 = ((IRepositoryTaskSelection) ((IStructuredSelection) selection) + .getFirstElement()); + if (OutlineTools.getHandle(brs1).compareTo(OutlineTools.getHandle(brs2)) == 0) { + // don't need to make a selection for the same + // element + return; + } + } + getTreeViewer().setSelection(selection, true); + } + } + } + }; + + private TreeViewer viewer; + + /** + * Creates a new <code>RepositoryTaskOutlinePage</code>. + * + * @param topTreeNode + * The top data node of the tree for this view. + * @param editor + * The editor this outline page is for. + */ + public RepositoryTaskOutlinePage(RepositoryTaskOutlineNode topTreeNode) { + super(); + this.topTreeNode = topTreeNode; + } + + @Override + public void createControl(Composite parent) { + super.createControl(parent); + viewer = getTreeViewer(); + viewer.setContentProvider(new BugTaskOutlineContentProvider()); + viewer.setLabelProvider(new LabelProvider() { + @Override + public Image getImage(Object element) { + if (element instanceof RepositoryTaskOutlineNode) { + RepositoryTaskOutlineNode node = (RepositoryTaskOutlineNode) element; + if (node.getComment() != null) { + return node.getImage(); + } else { + return TaskListImages.getImage(TaskListImages.TASK); + //BugzillaImages.getImage(BugzillaImages.BUG); + } + } else { + return super.getImage(element); + } + } + + @Override + public String getText(Object element) { + if (element instanceof RepositoryTaskOutlineNode) { + RepositoryTaskOutlineNode node = (RepositoryTaskOutlineNode) element; + if (node.getComment() != null) { + return node.getComment().getAuthorName() + " (" + node.getName() + ")"; + } else { + return node.getName(); + } + } + return super.getText(element); + } + }); + try { + viewer.setInput(topTreeNode); + viewer.setComparer(new RepositoryTaskOutlineComparer()); + viewer.expandAll(); + } catch (Exception e) { + MylarStatusHandler.fail(e, "could not create bugzilla outline", true); + } + getSite().getPage().addSelectionListener(selectionListener); + } + + @Override + public void dispose() { + super.dispose(); + getSite().getPage().removeSelectionListener(selectionListener); + } + + public TreeViewer getOutlineTreeViewer() { + return viewer; + } + + /** + * A content provider for the tree for this view. + * + * @see ITreeContentProvider + */ + protected class BugTaskOutlineContentProvider implements ITreeContentProvider { + + public Object[] getChildren(Object parentElement) { + if (parentElement instanceof RepositoryTaskOutlineNode) { + Object[] children = ((RepositoryTaskOutlineNode) parentElement).getChildren(); + if (children.length > 0) { + return children; + } + } + return new Object[0]; + } + + public Object getParent(Object element) { + return null; + } + + public boolean hasChildren(Object element) { + if (element instanceof RepositoryTaskOutlineNode) { + return ((RepositoryTaskOutlineNode) element).getChildren().length > 0; + } + return false; + } + + public Object[] getElements(Object inputElement) { + if (inputElement instanceof RepositoryTaskOutlineNode) { + Object[] children = ((RepositoryTaskOutlineNode) inputElement).getChildren(); + if (children.length > 0) { + return children; + } + } + return new Object[0]; + } + + public void dispose() { + // don't care when we are disposed + } + + public void inputChanged(Viewer viewerChanged, Object oldInput, Object newInput) { + // don't care when the input changes + } + } + +} diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/RepositoryTaskSelection.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/RepositoryTaskSelection.java new file mode 100644 index 000000000..a9d48f708 --- /dev/null +++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/RepositoryTaskSelection.java @@ -0,0 +1,171 @@ +/******************************************************************************* + * Copyright (c) 2004 - 2006 University Of British Columbia 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: + * University Of British Columbia - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylar.internal.tasklist.ui.editors; + +import org.eclipse.mylar.internal.tasklist.Comment; + +/** + * A selection of an element in a view. + */ +public class RepositoryTaskSelection implements IRepositoryTaskSelection { + + /** + * The id of the Bugzilla object that the selection was on. + */ + protected int id; + + /** The server of the Bugzilla object that the selection was on. */ + protected String server; + + /** The contents of the selection. */ + protected String contents; + + protected String bugSummary; + + /** + * The comment, if a comment was selected. If the selection was not a + * comment, then this is <code>null</code>. + */ + protected Comment comment; + + /** + * Creates a new <code>RepositoryTaskSelection</code> with no supplied + * contents or comment. + * + * @param id + * The id of the Bugzilla object that the selection was on. + * @param server + * The server of the Bugzilla object that the selection was on. + */ + public RepositoryTaskSelection(int id, String server, String summary) { + this(id, server, null, null, summary); + } + + /** + * Creates a new <code>RepositoryTaskSelection</code> with no supplied + * comment. + * + * @param id + * The id of the Bugzilla object that the selection was on. + * @param server + * The server of the Bugzilla object that the selection was on. + * @param contents + * The contents of the selection. + */ + public RepositoryTaskSelection(int id, String server, String contents, boolean isDescription, String summary) { + this(id, server, contents, null, summary); + this.isDescription = isDescription; + } + + /** + * Creates a new <code>RepositoryTaskSelection</code> with no supplied + * contents. + * + * @param id + * The id of the Bugzilla object that the selection was on. + * @param server + * The server of the Bugzilla object that the selection was on. + * @param comment + * The <code>Comment</code> object for this selection. If a + * comment was not selected, then this should be + * <code>null</code>. + */ + public RepositoryTaskSelection(int id, String server, Comment comment, String summary) { + this(id, server, null, comment, summary); + } + + /** + * Creates a new <code>RepositoryTaskSelection</code>. + * + * @param id + * The id of the Bugzilla object that the selection was on. + * @param server + * The server of the Bugzilla object that the selection was on. + * @param contents + * The contents of the selection. + * @param comment + * The <code>Comment</code> object for this selection. If a + * comment was not selected, then this should be + * <code>null</code>. + */ + public RepositoryTaskSelection(int id, String server, String contents, Comment comment, String summary) { + this.id = id; + this.server = server; + this.contents = contents; + this.comment = comment; + this.bugSummary = summary; + } + + public boolean hasComment() { + return comment != null; + } + + public Comment getComment() { + return comment; + } + + public void setComment(Comment comment) { + this.comment = comment; + } + + public String getContents() { + return contents; + } + + public void setContents(String contents) { + this.contents = contents; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getServer() { + return server; + } + + public void setServer(String server) { + this.server = server; + } + + public boolean isEmpty() { + return (server == null) || ((contents == null) && (comment == null)); + } + + private boolean isCommentHeader = false; + + private boolean isDescription = false; + + public boolean isCommentHeader() { + return isCommentHeader; + } + + public boolean isDescription() { + return isDescription; + } + + public void setIsCommentHeader(boolean isCommentHeader) { + this.isCommentHeader = isCommentHeader; + } + + public void setIsDescription(boolean isDescription) { + this.isDescription = isDescription; + } + + public String getBugSummary() { + return bugSummary; + } +} diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/RepositoryTextViewer.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/RepositoryTextViewer.java new file mode 100644 index 000000000..795e116ad --- /dev/null +++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/RepositoryTextViewer.java @@ -0,0 +1,125 @@ +/******************************************************************************* + * Copyright (c) 2004 - 2006 University Of British Columbia 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: + * University Of British Columbia - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylar.internal.tasklist.ui.editors; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.TextAttribute; +import org.eclipse.jface.text.hyperlink.DefaultHyperlinkPresenter; +import org.eclipse.jface.text.hyperlink.IHyperlinkDetector; +import org.eclipse.jface.text.hyperlink.IHyperlinkPresenter; +import org.eclipse.jface.text.presentation.IPresentationReconciler; +import org.eclipse.jface.text.presentation.PresentationReconciler; +import org.eclipse.jface.text.rules.DefaultDamagerRepairer; +import org.eclipse.jface.text.rules.IRule; +import org.eclipse.jface.text.rules.IToken; +import org.eclipse.jface.text.rules.MultiLineRule; +import org.eclipse.jface.text.rules.RuleBasedScanner; +import org.eclipse.jface.text.rules.SingleLineRule; +import org.eclipse.jface.text.rules.Token; +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.jface.text.source.SourceViewer; +import org.eclipse.mylar.provisional.tasklist.MylarTaskListPlugin; +import org.eclipse.mylar.provisional.tasklist.TaskRepository; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.editors.text.TextSourceViewerConfiguration; +import org.eclipse.ui.internal.editors.text.URLHyperlinkDetector; + +/** + * @author Rob Elves + */ +public class RepositoryTextViewer extends SourceViewer { + + private TaskRepository repository; + + public RepositoryTextViewer(TaskRepository repository, Composite composite, int style) { + super(composite, null, style); + this.configure(new RepositoryViewerConfig()); + this.repository = repository; + } + + public TaskRepository getRepository() { + return repository; + } + + public void setRepository(TaskRepository repository) { + this.repository = repository; + } + + class RepositoryViewerConfig extends TextSourceViewerConfiguration { + + private RepositoryTextScanner scanner = null; + + public IPresentationReconciler getPresentationReconciler(ISourceViewer sourceViewer) { + PresentationReconciler reconciler = new PresentationReconciler(); + reconciler.setDocumentPartitioning(getConfiguredDocumentPartitioning(sourceViewer)); + + DefaultDamagerRepairer dr = new DefaultDamagerRepairer(getDefaultScanner()); + reconciler.setDamager(dr, IDocument.DEFAULT_CONTENT_TYPE); + reconciler.setRepairer(dr, IDocument.DEFAULT_CONTENT_TYPE); + + return reconciler; + } + + private RepositoryTextScanner getDefaultScanner() { + if (scanner == null) { + scanner = new RepositoryTextScanner(); + } + return scanner; + } + + public IHyperlinkDetector[] getHyperlinkDetectors(ISourceViewer sourceViewer) { + URLHyperlinkDetector hyperlinkDetector = new URLHyperlinkDetector(); + List<IHyperlinkDetector> detectors = new ArrayList<IHyperlinkDetector>(); + detectors.add(hyperlinkDetector); + detectors.addAll(Arrays.asList(MylarTaskListPlugin.getDefault().getTaskHyperlinkDetectors())); + return detectors.toArray(new IHyperlinkDetector[detectors.size()]); +// return MylarTaskListPlugin.getDefault().getTaskHyperlinkDetectors(); + } + + public IHyperlinkPresenter getHyperlinkPresenter(ISourceViewer sourceViewer) { + return new DefaultHyperlinkPresenter(new RGB(0, 0, 200)); + } + + public int getHyperlinkStateMask(ISourceViewer sourceViewer) { + return SWT.NONE; + } + } + + class RepositoryTextScanner extends RuleBasedScanner { + private Color URL_COLOR = new Color(Display.getCurrent(), new RGB(0, 0, 200)); + + public RepositoryTextScanner() { + IToken bugToken = new Token(new TextAttribute(URL_COLOR)); + IRule[] rules = new IRule[7]; + rules[0] = (new SingleLineRule("http://", " ", bugToken)); + rules[1] = (new SingleLineRule("https://", " ", bugToken)); + rules[2] = (new MultiLineRule("bug#", " ", bugToken)); + rules[3] = (new MultiLineRule("bug #", " ", bugToken)); + rules[4] = (new SingleLineRule("bug #", "\n", bugToken)); + rules[5] = (new SingleLineRule("http://", "\n", bugToken)); + rules[6] = (new SingleLineRule("https://", "\n", bugToken)); +// rules[7] = (new MultiLineRule(" bug ", " ", bugToken)); +// rules[8] = (new SingleLineRule(" at ", ")", bugToken)); + setRules(rules); + } + + } + +} diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/SpellingDialog.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/SpellingDialog.java new file mode 100644 index 000000000..82ec9311e --- /dev/null +++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasklist/ui/editors/SpellingDialog.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * Copyright (c) 2004 - 2006 University Of British Columbia 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: + * University Of British Columbia - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylar.internal.tasklist.ui.editors; + +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.contentassist.ICompletionProposal; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.List; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + +/** + * TODO this is used only for spell checking which is not yet implemented, + * therefore this is not properly tested + * + * @author Shawn Minto + */ +public class SpellingDialog extends Dialog { + + private String title; + + private Text wordToFix; + + private List suggestions; + + private IDocument document; + + private ICompletionProposal[] proposals; + + protected SpellingDialog(Shell parentShell, String title, IDocument document) { + super(parentShell); + this.title = title; + this.document = document; + } + + @Override + protected Control createDialogArea(Composite parent) { + Control c = super.createDialogArea(parent); + + Composite spellingComposite = new Composite(parent, SWT.NONE); + + GridLayout spellingLayout = new GridLayout(); + spellingLayout.numColumns = 1; + spellingComposite.setLayout(spellingLayout); + + wordToFix = new Text(spellingComposite, SWT.BORDER | SWT.READ_ONLY); + GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); + gd.widthHint = 150; + wordToFix.setLayoutData(gd); + + suggestions = new List(spellingComposite, SWT.BORDER); + gd = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); + gd.widthHint = 150; + gd.heightHint = AbstractRepositoryTaskEditor.WRAP_LENGTH; + suggestions.setLayoutData(gd); + + return c; + } + + @Override + protected void configureShell(Shell newShell) { + super.configureShell(newShell); + newShell.setText(title); + } + + public void open(String word, ICompletionProposal[] proposals) { + create(); + + this.proposals = proposals; + + wordToFix.setText(word); + suggestions.removeAll(); + + for (int i = 0; i < proposals.length; i++) { + suggestions.setItem(i, proposals[i].getDisplayString()); + } + + super.open(); + } + + @Override + protected void handleShellCloseEvent() { + if (getReturnCode() == Dialog.OK) { + int i = suggestions.getSelectionIndex(); + if (i > 0 && i < proposals.length) + proposals[i].apply(document); + } + super.handleShellCloseEvent(); + } + +} diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/provisional/tasklist/AbstractRepositoryConnector.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/provisional/tasklist/AbstractRepositoryConnector.java index ab1904162..8b40e1a58 100644 --- a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/provisional/tasklist/AbstractRepositoryConnector.java +++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/provisional/tasklist/AbstractRepositoryConnector.java @@ -27,6 +27,7 @@ import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.jobs.IJobChangeListener; import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.wizard.IWizard; import org.eclipse.mylar.internal.core.util.DateUtil; @@ -117,11 +118,31 @@ public abstract class AbstractRepositoryConnector { public abstract List<AbstractQueryHit> performQuery(AbstractRepositoryQuery query, IProgressMonitor monitor, MultiStatus queryStatus); + // Precondition of note: offline file is removed upon submit to repository resulting in a synchronized state. - protected void updateOfflineState(AbstractRepositoryTask repositoryTask, boolean forceSync) { + protected void updateOfflineState(final AbstractRepositoryTask repositoryTask, boolean forceSync) { RepositoryTaskSyncState status = repositoryTask.getSyncState(); + RepositoryTaskData downloadedTaskData; + + final TaskRepository repository = MylarTaskListPlugin.getRepositoryManager().getRepository( + repositoryTask.getRepositoryKind(), repositoryTask.getRepositoryUrl()); - final RepositoryTaskData downloadedTaskData = downloadTaskData(repositoryTask); + if (repository == null) { + MylarStatusHandler + .log("No repository associated with task "+repositoryTask.getDescription()+". Unable to retrieve timezone information.", this); + return; + } + + try { + downloadedTaskData = downloadTaskData(repositoryTask); + } catch (final CoreException e) { + PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { + public void run() { + ErrorDialog.openError(PlatformUI.getWorkbench().getDisplay().getActiveShell(), "Error Downloading Report", "Unable to synchronize "+repositoryTask.getDescription()+" on "+repository.getUrl(), e.getStatus()); + } + }); + return; + } if (downloadedTaskData == null) { MylarStatusHandler.log("Download of " + repositoryTask.getDescription() + " from " @@ -132,14 +153,6 @@ public abstract class AbstractRepositoryConnector { RepositoryTaskData offlineTaskData = OfflineTaskManager.findBug(downloadedTaskData.getRepositoryUrl(), downloadedTaskData.getId()); - TaskRepository repository = MylarTaskListPlugin.getRepositoryManager().getRepository( - repositoryTask.getRepositoryKind(), repositoryTask.getRepositoryUrl()); - - if (repository == null) { - MylarStatusHandler - .log("No repository associated with task. Unable to retrieve timezone information.", this); - return; - } TimeZone repositoryTimeZone = DateUtil.getTimeZone(repository.getTimeZoneId()); if (offlineTaskData != null) { @@ -155,9 +168,9 @@ public abstract class AbstractRepositoryConnector { null, "Update Local Copy", "Local copy of Report " - + downloadedTaskData.getId() + + repositoryTask.getDescription() + " on " - + downloadedTaskData.getRepositoryUrl() + + repositoryTask.getRepositoryUrl() + " has changes.\nWould you like to override local changes? \n\nNote: if you select No, only the new comment will be saved with the updated bug, all other changes will be lost."); } }); @@ -186,12 +199,12 @@ public abstract class AbstractRepositoryConnector { status = RepositoryTaskSyncState.SYNCHRONIZED; } repositoryTask.setLastSynchronized(downloadedTaskData.getLastModified(repositoryTimeZone)); - repositoryTask.setTaskData(downloadedTaskData); + repositoryTask.setTaskData(downloadedTaskData); repositoryTask.setSyncState(status); - saveOffline(downloadedTaskData, forceSync); + saveOffline(downloadedTaskData); } - protected abstract RepositoryTaskData downloadTaskData(AbstractRepositoryTask bugzillaTask); + protected abstract RepositoryTaskData downloadTaskData(AbstractRepositoryTask bugzillaTask) throws CoreException; public abstract String getLabel(); @@ -387,11 +400,10 @@ public abstract class AbstractRepositoryConnector { public AbstractAttributeFactory getAttributeFactory() { return attributeFactory; } - - public void saveOffline(final RepositoryTaskData report, final boolean forceSync) { - try { - MylarTaskListPlugin.getDefault().getOfflineReportsFile().add(report, forceSync); - // report.setOfflineState(true); + + public void saveOffline(RepositoryTaskData taskData) { + try { + MylarTaskListPlugin.getDefault().getOfflineReportsFile().add(taskData); } catch (CoreException e) { MylarStatusHandler.fail(e, e.getMessage(), false); } |