Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormkersten2005-06-17 16:51:27 -0400
committermkersten2005-06-17 16:51:27 -0400
commit7320e6f5ba8114f927eeeed9f0b11ecd2041f414 (patch)
treee9a9d5d9e0ffb37dc8e575c8402a2008cea563d3 /org.eclipse.mylyn.tasks.ui
downloadorg.eclipse.mylyn.tasks-7320e6f5ba8114f927eeeed9f0b11ecd2041f414.tar.gz
org.eclipse.mylyn.tasks-7320e6f5ba8114f927eeeed9f0b11ecd2041f414.tar.xz
org.eclipse.mylyn.tasks-7320e6f5ba8114f927eeeed9f0b11ecd2041f414.zip
Initial mylar contribution
Diffstat (limited to 'org.eclipse.mylyn.tasks.ui')
-rw-r--r--org.eclipse.mylyn.tasks.ui/.classpath11
-rw-r--r--org.eclipse.mylyn.tasks.ui/.cvsignore1
-rw-r--r--org.eclipse.mylyn.tasks.ui/.project30
-rw-r--r--org.eclipse.mylyn.tasks.ui/META-INF/MANIFEST.MF33
-rw-r--r--org.eclipse.mylyn.tasks.ui/about.html22
-rw-r--r--org.eclipse.mylyn.tasks.ui/build.properties24
-rw-r--r--org.eclipse.mylyn.tasks.ui/icons/elcl16/delete.gifbin0 -> 351 bytes
-rw-r--r--org.eclipse.mylyn.tasks.ui/icons/elcl16/refresh.gifbin0 -> 216 bytes
-rw-r--r--org.eclipse.mylyn.tasks.ui/icons/elcl16/remove.gifbin0 -> 163 bytes
-rw-r--r--org.eclipse.mylyn.tasks.ui/icons/elcl16/synched.gifbin0 -> 160 bytes
-rw-r--r--org.eclipse.mylyn.tasks.ui/icons/elcl16/task-bugzilla.gifbin0 -> 358 bytes
-rw-r--r--org.eclipse.mylyn.tasks.ui/icons/elcl16/task-category-new.gifbin0 -> 239 bytes
-rw-r--r--org.eclipse.mylyn.tasks.ui/icons/elcl16/task-category.gifbin0 -> 161 bytes
-rw-r--r--org.eclipse.mylyn.tasks.ui/icons/elcl16/task.gifbin0 -> 375 bytes
-rw-r--r--org.eclipse.mylyn.tasks.ui/icons/eview16/tasklist.gifbin0 -> 594 bytes
-rw-r--r--org.eclipse.mylyn.tasks.ui/plugin.xml47
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/BugzillaTask.java450
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/Category.java67
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ITask.java97
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ITaskActivityListener.java29
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ITaskInfo.java23
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/MylarTasksPlugin.java224
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/Task.java306
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/TaskList.java138
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/TaskListManager.java159
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/BugzillaContentProvider.java59
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/BugzillaEditingMonitor.java46
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/BugzillaMylarBridge.java142
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/BugzillaReferencesProvider.java106
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/BugzillaReportNode.java184
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/BugzillaStructureBridge.java169
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/StackTrace.java375
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/Util.java226
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/search/BugzillaMylarSearch.java149
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/search/BugzillaMylarSearchJob.java109
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/search/BugzillaMylarSearchOperation.java526
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/search/BugzillaResultCollector.java182
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/ui/BugzillaNodeLabelProvider.java58
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/ui/BugzillaUiBridge.java141
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/BugzillaTaskEditor.java274
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/BugzillaTaskEditorInput.java135
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/TaskEditor.java163
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/TaskEditorCopyAction.java33
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/TaskEditorInput.java113
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/actions/ToggleIntersectionModeAction.java42
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/views/ActiveTaskscapeView.java207
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/views/TaskListLabelProvider.java213
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/views/TaskListView.java1156
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/views/ToolTipHandler.java240
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/util/RelativePathUtil.java31
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/util/TaskTest.java148
51 files changed, 6888 insertions, 0 deletions
diff --git a/org.eclipse.mylyn.tasks.ui/.classpath b/org.eclipse.mylyn.tasks.ui/.classpath
new file mode 100644
index 000000000..003e39624
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/.classpath
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins">
+ <accessrules>
+ <accessrule kind="accessible" pattern="**/internal/**"/>
+ </accessrules>
+ </classpathentry>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.mylyn.tasks.ui/.cvsignore b/org.eclipse.mylyn.tasks.ui/.cvsignore
new file mode 100644
index 000000000..ba077a403
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/.cvsignore
@@ -0,0 +1 @@
+bin
diff --git a/org.eclipse.mylyn.tasks.ui/.project b/org.eclipse.mylyn.tasks.ui/.project
new file mode 100644
index 000000000..2537de5d3
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/.project
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.mylar.tasks</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jem.beaninfo.BeanInfoNature</nature>
+ </natures>
+</projectDescription>
diff --git a/org.eclipse.mylyn.tasks.ui/META-INF/MANIFEST.MF b/org.eclipse.mylyn.tasks.ui/META-INF/MANIFEST.MF
new file mode 100644
index 000000000..9d39d0b85
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/META-INF/MANIFEST.MF
@@ -0,0 +1,33 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Mylar Task Plug-in
+Bundle-SymbolicName: org.eclipse.mylar.tasks; singleton:=true
+Bundle-Version: 0.2.3
+Bundle-Activator: org.eclipse.mylar.tasks.MylarTasksPlugin
+Bundle-Localization: plugin
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime,
+ org.eclipse.core.resources,
+ org.eclipse.ui.views,
+ org.eclipse.ui.forms,
+ org.eclipse.ui.ide,
+ org.eclipse.osgi.util,
+ org.eclipse.osgi.services,
+ org.eclipse.search,
+ org.eclipse.jdt.ui,
+ org.eclipse.pde.ui,
+ org.eclipse.mylar.core,
+ org.eclipse.mylar.ui,
+ org.eclipse.mylar.bugzilla,
+ org.eclipse.jdt.core
+Eclipse-AutoStart: true
+Bundle-Vendor: University of British Columbia
+Bundle-ClassPath: mylar-tasklist.jar
+Export-Package: org.eclipse.mylar.tasks,
+ org.eclipse.mylar.tasks.bugzilla,
+ org.eclipse.mylar.tasks.bugzilla.search,
+ org.eclipse.mylar.tasks.bugzilla.ui,
+ org.eclipse.mylar.tasks.ui,
+ org.eclipse.mylar.tasks.ui.actions,
+ org.eclipse.mylar.tasks.ui.views,
+ org.eclipse.mylar.tasks.util
diff --git a/org.eclipse.mylyn.tasks.ui/about.html b/org.eclipse.mylyn.tasks.ui/about.html
new file mode 100644
index 000000000..60ca57b4b
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/about.html
@@ -0,0 +1,22 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3c.org/TR/1999/REC-html401-19991224/loose.dtd">
+<!-- saved from url=(0043)http://www.eclipse.org/legal/epl/about.html -->
+<HTML><HEAD><TITLE>About</TITLE>
+<META http-equiv=Content-Type content="text/html; charset=ISO-8859-1">
+<META content="MSHTML 6.00.2900.2627" name=GENERATOR></HEAD>
+<BODY lang=EN-US>
+<H2>About This Content</H2>
+<P>February 24, 2005</P>
+<H3>License</H3>
+<P>The Eclipse Foundation makes available all content in this plug-in
+("Content"). Unless otherwise indicated below, the Content is provided to you
+under the terms and conditions of the Eclipse Public License Version 1.0
+("EPL"). A copy of the EPL is available at <A
+href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</A>.
+For purposes of the EPL, "Program" will mean the Content.</P>
+<P>If you did not receive this Content directly from the Eclipse Foundation, the
+Content is being redistributed by another party ("Redistributor") and different
+terms and conditions may apply to your use of any object code in the Content.
+Check the Redistributor's license that was provided with the Content. If no such
+license exists, contact the Redistributor. Unless otherwise indicated below, the
+terms and conditions of the EPL still apply to any source code in the
+Content.</P></BODY></HTML>
diff --git a/org.eclipse.mylyn.tasks.ui/build.properties b/org.eclipse.mylyn.tasks.ui/build.properties
new file mode 100644
index 000000000..c9d4ccc4a
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/build.properties
@@ -0,0 +1,24 @@
+###############################################################################
+# Copyright (c) 2004 - 2005 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
+###############################################################################
+bin.includes = plugin.xml,\
+ icons/,\
+ lib/,\
+ mylar-tasklist.jar,\
+ META-INF/
+src.includes = icons/,\
+ plugin.xml,\
+ tasklist.jar,\
+ src/,\
+ lib/,\
+ META-INF/
+jars.compile.order = mylar-tasklist.jar
+source.mylar-tasklist.jar = src/
+output.mylar-tasklist.jar = bin/
diff --git a/org.eclipse.mylyn.tasks.ui/icons/elcl16/delete.gif b/org.eclipse.mylyn.tasks.ui/icons/elcl16/delete.gif
new file mode 100644
index 000000000..b6922ac11
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/icons/elcl16/delete.gif
Binary files differ
diff --git a/org.eclipse.mylyn.tasks.ui/icons/elcl16/refresh.gif b/org.eclipse.mylyn.tasks.ui/icons/elcl16/refresh.gif
new file mode 100644
index 000000000..634306d4c
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/icons/elcl16/refresh.gif
Binary files differ
diff --git a/org.eclipse.mylyn.tasks.ui/icons/elcl16/remove.gif b/org.eclipse.mylyn.tasks.ui/icons/elcl16/remove.gif
new file mode 100644
index 000000000..2cd9c5444
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/icons/elcl16/remove.gif
Binary files differ
diff --git a/org.eclipse.mylyn.tasks.ui/icons/elcl16/synched.gif b/org.eclipse.mylyn.tasks.ui/icons/elcl16/synched.gif
new file mode 100644
index 000000000..870934b69
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/icons/elcl16/synched.gif
Binary files differ
diff --git a/org.eclipse.mylyn.tasks.ui/icons/elcl16/task-bugzilla.gif b/org.eclipse.mylyn.tasks.ui/icons/elcl16/task-bugzilla.gif
new file mode 100644
index 000000000..8704e4df2
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/icons/elcl16/task-bugzilla.gif
Binary files differ
diff --git a/org.eclipse.mylyn.tasks.ui/icons/elcl16/task-category-new.gif b/org.eclipse.mylyn.tasks.ui/icons/elcl16/task-category-new.gif
new file mode 100644
index 000000000..bcae2d487
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/icons/elcl16/task-category-new.gif
Binary files differ
diff --git a/org.eclipse.mylyn.tasks.ui/icons/elcl16/task-category.gif b/org.eclipse.mylyn.tasks.ui/icons/elcl16/task-category.gif
new file mode 100644
index 000000000..112cb65b4
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/icons/elcl16/task-category.gif
Binary files differ
diff --git a/org.eclipse.mylyn.tasks.ui/icons/elcl16/task.gif b/org.eclipse.mylyn.tasks.ui/icons/elcl16/task.gif
new file mode 100644
index 000000000..a2948dff6
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/icons/elcl16/task.gif
Binary files differ
diff --git a/org.eclipse.mylyn.tasks.ui/icons/eview16/tasklist.gif b/org.eclipse.mylyn.tasks.ui/icons/eview16/tasklist.gif
new file mode 100644
index 000000000..3efb0536a
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/icons/eview16/tasklist.gif
Binary files differ
diff --git a/org.eclipse.mylyn.tasks.ui/plugin.xml b/org.eclipse.mylyn.tasks.ui/plugin.xml
new file mode 100644
index 000000000..2061a3803
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/plugin.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.0"?>
+<plugin>
+
+ <extension
+ name="Mylar Tasks startup"
+ point="org.eclipse.ui.startup">
+ </extension>
+
+ <extension point="org.eclipse.ui.views">
+ <category name="Mylar Tools" id="org.eclipse.mylar"/>
+
+ <view name="Mylar Tasks"
+ icon="icons/eview16/tasklist.gif"
+ category="org.eclipse.mylar"
+ class="org.eclipse.mylar.tasks.ui.views.TaskListView"
+ id="org.eclipse.mylar.tasks.ui.views.TaskListView" />
+ </extension>
+ <extension
+ point="org.eclipse.ui.editors">
+ <editor
+ icon="icons/eview16/tasklist.gif"
+ class="org.eclipse.mylar.tasks.ui.TaskEditor"
+ name="Task Viewer"
+ id="org.eclipse.mylar.tasks.ui.taskEditor"/>
+ <editor
+ icon="icons/eview16/tasklist.gif"
+ name="Bugzilla task viewer"
+ class="org.eclipse.mylar.tasks.ui.BugzillaTaskEditor"
+ id="org.eclipse.mylar.tasks.ui.bugzillaTaskEditor"/>
+ </extension>
+
+ <extension point="org.eclipse.ui.commands">
+ <command
+ name="Interest filtering"
+ description="Interest filtering"
+ id="org.eclipse.mylar.ui.interest.filtering"
+ categoryId="org.eclipse.mylar.ui">
+ </command>
+ <command
+ name="Intersection mode"
+ description="Intersection mode"
+ id="org.eclipse.mylar.ui.interest.intersection"
+ categoryId="org.eclipse.mylar.ui">
+ </command>
+ </extension>
+</plugin>
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/BugzillaTask.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/BugzillaTask.java
new file mode 100644
index 000000000..9ea3a2914
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/BugzillaTask.java
@@ -0,0 +1,450 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2005 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
+ *******************************************************************************/
+/*
+ * Created on 14-Jan-2005
+ */
+package org.eclipse.mylar.tasks;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import javax.security.auth.login.LoginException;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.IJobChangeEvent;
+import org.eclipse.core.runtime.jobs.IJobChangeListener;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.mylar.bugzilla.BugzillaPlugin;
+import org.eclipse.mylar.bugzilla.core.BugReport;
+import org.eclipse.mylar.bugzilla.core.BugzillaRepository;
+import org.eclipse.mylar.bugzilla.core.IBugzillaBug;
+import org.eclipse.mylar.bugzilla.offlineReports.OfflineReportsFile;
+import org.eclipse.mylar.bugzilla.ui.OfflineView;
+import org.eclipse.mylar.core.MylarPlugin;
+import org.eclipse.mylar.tasks.ui.BugzillaTaskEditorInput;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.internal.Workbench;
+
+
+/**
+ * @author ebooth
+ */
+public class BugzillaTask extends Task {
+
+ /**
+ * Comment for <code>serialVersionUID</code>
+ */
+ private static final long serialVersionUID = 3257007648544469815L;
+
+ public static final String FILE_EXTENSION = ".bug_reports";
+
+ public enum BugTaskState {FREE, WAITING, DOWNLOADING, COMPARING, OPENING}
+ private transient BugTaskState state;
+
+ /**
+ * The bug report for this BugzillaTask. This is <code>null</code> if the
+ * bug report with the specified ID was unable to download.
+ */
+ protected transient BugReport bugReport = null;
+
+ /**
+ * Value is <code>true</code> if the bug report has saved changes that
+ * need synchronizing with the Bugzilla server.
+ */
+ private boolean isDirty;
+
+ /** The last time this task's bug report was downloaded from the server. */
+ protected Date lastRefresh;
+
+ public static final ISchedulingRule rule = new ISchedulingRule() {
+ public boolean isConflicting(ISchedulingRule schedulingRule) {
+ return schedulingRule == this;
+ }
+ public boolean contains(ISchedulingRule schedulingRule) {
+ return schedulingRule == this;
+ }
+ };
+
+ public BugzillaTask(String id, String label) {
+ super(id, label);
+ isDirty = false;
+ GetBugReportJob job = new GetBugReportJob("Downloading from Bugzilla server...");
+ job.schedule();
+ }
+
+ public BugzillaTask(String id, String label, boolean noDownload) {
+ super(id, label);
+ isDirty = false;
+ if (!noDownload) {
+ GetBugReportJob job = new GetBugReportJob("Downloading from Bugzilla server...");
+ job.schedule();
+ }
+ }
+
+ @Override
+ public String getLabel() {
+ return MylarTasksPlugin.getDefault().getBugzillaProvider().getBugzillaDescription(this);
+ }
+
+ /**
+ * @return Returns the bugReport.
+ */
+ public BugReport getBugReport() {
+ return bugReport;
+ }
+
+ /**
+ * @param bugReport The bugReport to set.
+ */
+ public void setBugReport(BugReport bugReport) {
+ this.bugReport = bugReport;
+ }
+
+ /**
+ * @return Returns the serialVersionUID.
+ */
+ public static long getSerialVersionUID() {
+ return serialVersionUID;
+ }
+ /**
+ * @return Returns the lastRefresh.
+ */
+ public Date getLastRefresh() {
+ return lastRefresh;
+ }
+ /**
+ * @param lastRefresh The lastRefresh to set.
+ */
+ public void setLastRefresh(Date lastRefresh) {
+ this.lastRefresh = lastRefresh;
+ }
+ /**
+ * @param state The state to set.
+ */
+ public void setState(BugTaskState state) {
+ this.state = state;
+ }
+ /**
+ * @return Returns <code>true</code> if the bug report has saved changes
+ * that need synchronizing with the Bugzilla server.
+ */
+ public boolean isDirty() {
+ return isDirty;
+ }
+
+ /**
+ * @param isDirty The isDirty to set.
+ */
+ public void setDirty(boolean isDirty) {
+ this.isDirty = isDirty;
+ notifyTaskDataChange();
+ }
+
+ /**
+ * @return Returns the state of the Bugzilla task.
+ */
+ public BugTaskState getState() {
+ return state;
+ }
+
+ /**
+ * Try to download the bug from the server.
+ * @param bugId The ID of the bug report to download.
+ *
+ * @return The bug report, or <code>null</code> if it was unsuccessfully
+ * downloaded.
+ */
+ public BugReport downloadReport() {
+// BugzillaTaskEditorInput input = new BugzillaTaskEditorInput(this);
+ try {
+ // XXX make sure to send in the server name if there are multiple repositories
+ return BugzillaRepository.getInstance().getBug(getBugId(getHandle()));
+ } catch (LoginException e) {
+ MylarPlugin.log(this.getClass().toString(), e);
+ } catch (IOException e) {
+ MylarPlugin.log(this.getClass().toString(), e);
+ }
+ return null;
+ }
+
+ @Override
+ public void openTaskInEditor(){
+ openTask(-1);
+ }
+
+ /**
+ * Opens this task's bug report in an editor revealing the selected comment.
+ * @param commentNumber The comment number to reveal
+ */
+ public void openTask(int commentNumber) {
+ if (state != BugTaskState.FREE) {
+ return;
+ }
+
+ state = BugTaskState.OPENING;
+ notifyTaskDataChange();
+ OpenBugTaskJob job = new OpenBugTaskJob("Opening Bugzilla task in editor...", this);
+ job.schedule();
+ job.addJobChangeListener(new IJobChangeListener(){
+
+ public void aboutToRun(IJobChangeEvent event) {
+ // don't care about this event
+ }
+
+ public void awake(IJobChangeEvent event) {
+ // don't care about this event
+ }
+
+ public void done(IJobChangeEvent event) {
+ state = BugTaskState.FREE;
+ notifyTaskDataChange();
+ }
+
+ public void running(IJobChangeEvent event) {
+ // don't care about this event
+ }
+
+ public void scheduled(IJobChangeEvent event) {
+ // don't care about this event
+ }
+
+ public void sleeping(IJobChangeEvent event) {
+ // don't care about this event
+ }
+ });
+ }
+
+ /**
+ * @return <code>true</code> if the bug report for this BugzillaTask was
+ * successfully downloaded.
+ */
+ public boolean isBugDownloaded() {
+ return bugReport != null;
+ }
+
+ @Override
+ public String toString() {
+ return "bugzilla report id: " + getHandle();
+ }
+
+ protected void openTaskEditor(final IEditorInput input) {
+ if (isBugDownloaded()) {
+
+ Workbench.getInstance().getDisplay().asyncExec(new Runnable() {
+ public void run() {
+ // get the active workbench page
+ IWorkbenchPage page = MylarTasksPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getActivePage();
+
+ // if we couldn't get the page, get out of here
+ if (page == null)
+ return;
+
+ try {
+ // try to open an editor on the input bug
+ //page.openEditor(input, IBugzillaConstants.EXISTING_BUG_EDITOR_ID);
+ page.openEditor(input, "org.eclipse.mylar.tasks.ui.bugzillaTaskEditor");
+ }
+ catch (PartInitException ex) {
+ MylarPlugin.log(this.getClass().toString(), ex);
+ return;
+ }
+ }
+ });
+ }
+ else {
+ Workbench.getInstance().getDisplay().asyncExec(new Runnable() {
+ public void run() {
+ MessageDialog.openInformation(Workbench.getInstance().getActiveWorkbenchWindow().getShell(),
+ "Could not open bug.", "Bug #" + getHandle()
+ + " could not be read from the server. Try refreshing the bug task.");
+ }
+ });
+ }
+ }
+
+ /**
+ * @return Returns the last time this task's bug report was downloaded from
+ * the server.
+ */
+ public Date getLastRefreshTime() {
+ return lastRefresh;
+ }
+
+ /**
+ * @return The number of seconds ago that this task's bug report was
+ * downloaded from the server.
+ */
+ public long getTimeSinceLastRefresh() {
+ Date timeNow = new Date();
+ return (timeNow.getTime() - lastRefresh.getTime())/1000;
+ }
+
+ private class GetBugReportJob extends Job {
+ public GetBugReportJob(String name) {
+ super(name);
+ setRule(rule);
+ state = BugTaskState.WAITING;
+ notifyTaskDataChange();
+ }
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ state = BugTaskState.DOWNLOADING;
+ notifyTaskDataChange();
+ // Update time this bugtask was last downloaded.
+ lastRefresh = new Date();
+ bugReport = downloadReport();
+ state = BugTaskState.FREE;
+ notifyTaskDataChange();
+ saveBugReport(true);
+ return new Status(IStatus.OK, MylarPlugin.IDENTIFIER, IStatus.OK, "", null);
+ }
+ }
+
+ private class OpenBugTaskJob extends Job {
+
+ protected BugzillaTask bugTask;
+
+ public OpenBugTaskJob(String name, BugzillaTask bugTask) {
+ super(name);
+ this.bugTask = bugTask;
+ }
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ try{
+ final IEditorInput input = new BugzillaTaskEditorInput(bugTask);
+ state = BugTaskState.OPENING;
+ notifyTaskDataChange();
+ openTaskEditor(input);
+
+ state = BugTaskState.FREE;
+ notifyTaskDataChange();
+ return new Status(IStatus.OK, MylarPlugin.IDENTIFIER, IStatus.OK, "", null);
+ }catch(Exception e){
+ MylarPlugin.log(this.getClass().toString(), e);
+ }
+ return Status.CANCEL_STATUS;
+ }
+ }
+
+ /**
+ * Refreshes the bug report with the Bugzilla server.
+ */
+ public void refresh() {
+ // The bug report must be untouched, and this task must not be busy.
+ if (isDirty() || (state != BugTaskState.FREE)) {
+ return;
+ }
+ GetBugReportJob job = new GetBugReportJob("Refreshing with Bugzilla server...");
+ job.schedule();
+ }
+
+ @Override
+ public String getToolTipText() {
+ // Get the current time.
+ Date timeNow = new Date();
+
+ // Get the number of minutes between the current time
+ // and the last time the bug report was downloaded
+ long timeDifference = (timeNow.getTime() - lastRefresh.getTime())/60000;
+
+ // Calculate the number of minutes and hours.
+ // The amount left in "timeDifference" is the
+ // days' difference.
+ long minutes = timeDifference % 60;
+ timeDifference /= 60;
+ long hours = timeDifference % 24;
+ timeDifference /= 24;
+
+ // Gradually generate the tooltip string...
+ String toolTip;
+ if (bugReport == null) {
+ toolTip = "Last attempted download ";
+ }
+ else {
+ toolTip = "Last downloaded ";
+ }
+
+ if (timeDifference > 0) {
+ toolTip += timeDifference + ((timeDifference == 1) ? " day " : " days ");
+ }
+ if (hours > 0 || timeDifference > 0) {
+ toolTip += hours + ((hours == 1) ? " hour " : " hours ");
+ }
+ toolTip += minutes + ((minutes == 1) ? " minute " : " minutes ") + "ago";
+
+ return toolTip;
+ }
+
+ public boolean readBugReport() {
+ // XXX server name needs to be with the bug report
+ int location = BugzillaPlugin.getDefault().getOfflineReports().find(getBugId(getHandle()));
+ if(location == -1){
+ bugReport = null;
+ return true;
+ }
+ bugReport = (BugReport)BugzillaPlugin.getDefault().getOfflineReports().elements().get(location);
+ return true;
+ }
+
+ public void saveBugReport(boolean refresh) {
+ if(bugReport == null)
+ return;
+
+ // XXX use the server name for multiple repositories
+ OfflineReportsFile offlineReports = BugzillaPlugin.getDefault().getOfflineReports();
+ int location = offlineReports.find(getBugId(getHandle()));
+ if(location != -1){
+ IBugzillaBug tmpBugReport = offlineReports.elements().get(location);
+ List<IBugzillaBug> l = new ArrayList<IBugzillaBug>(1);
+ l.add(tmpBugReport);
+ offlineReports.remove(l);
+ }
+ offlineReports.add(bugReport);
+
+ if(refresh){
+ final IWorkbench workbench = PlatformUI.getWorkbench();
+ workbench.getDisplay().asyncExec(new Runnable() {
+ public void run() {
+ OfflineView.refresh();
+ }
+ });
+ }
+ }
+
+ public static String getServerName(String handle) {
+ int index = handle.lastIndexOf('-');
+ if(index != -1){
+ return handle.substring(0, index);
+ }
+ return null;
+ }
+
+ public static int getBugId(String handle) {
+ int index = handle.lastIndexOf('-');
+ if(index != -1){
+ String id = handle.substring(index+1);
+ return Integer.parseInt(id);
+ }
+ return -1;
+ }
+}
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/Category.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/Category.java
new file mode 100644
index 000000000..2d1db62f9
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/Category.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2005 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
+ *******************************************************************************/
+/*
+ * Created on Dec 26, 2004
+ */
+package org.eclipse.mylar.tasks;
+
+import java.io.Serializable;
+
+
+/**
+ * @author Mik Kersten
+ */
+public class Category implements Serializable {
+
+ private static final long serialVersionUID = 3834024740813027380L;
+
+// private List<ITask> tasks = new ArrayList<ITask>();
+ private String name = "";
+
+ public Category(String name) {
+ this.name = name;
+ }
+
+// public void addTask(ITask task) {
+// tasks.add(task);
+// }
+//
+// public void removeTask(Task task) {
+// tasks.remove(task);
+// }
+//
+// public List<ITask> getTasks() {
+// return tasks;
+// }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ public String getName() {
+ return name;
+ }
+ public void setName(String label) {
+ this.name = label;
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (object == null) return false;
+ if (object instanceof Category) {
+ Category compare = (Category)object;
+ return this.getName().equals(compare.getName());
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ITask.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ITask.java
new file mode 100644
index 000000000..1ade911a3
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ITask.java
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2005 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
+ *******************************************************************************/
+/*
+ * Created on Jan 13, 2005
+ */
+package org.eclipse.mylar.tasks;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @author Mik Kersten
+ *
+ * TODO: make IDs be handles
+ */
+public interface ITask extends Serializable {
+
+ @Override
+ public abstract String toString();
+
+ public abstract String getPath();
+
+ public abstract void setPath(String path);
+
+ public abstract List<ITask> getChildren();
+
+ public abstract String getHandle();
+
+ public abstract void setHandle(String id);
+
+ public abstract String getLabel();
+
+ public abstract void setLabel(String label);
+
+ public abstract ITask getParent();
+
+ public abstract void setParent(ITask parent);
+
+ public abstract void removeSubtask(ITask task);
+
+ public abstract void addSubtask(ITask task);
+
+ public abstract boolean isActive();
+
+ public abstract void setActive(boolean active);
+
+ public abstract boolean isCompleted();
+
+ public abstract void setCompleted(boolean completed);
+
+ public abstract RelatedLinks getRelatedLinks();
+
+ public abstract void setRelatedLinks(RelatedLinks relatedLinks);
+
+ public abstract void addLink(String url);
+
+ public abstract void removeLink(String url);
+
+ public abstract String getNotes();
+
+ public abstract void setNotes(String notes);
+
+ public abstract String getElapsedTime();
+
+ public abstract void setElapsedTime(String elapsed);
+
+ public abstract String getEstimatedTime();
+
+ public abstract void setEstimatedTime(String estimated);
+
+ public abstract List<ITask> getSubTasksInProgress();
+
+ /**
+ * Opens this task in an editor
+ */
+ public abstract void openTaskInEditor();
+
+ public abstract String getToolTipText();
+
+ public abstract List<Category> getCategories();
+
+ public abstract String getPriority();
+
+ public abstract void setPriority(String priority);
+
+ public abstract boolean isCategory();
+
+ public abstract void setIsCategory(boolean b);
+} \ No newline at end of file
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ITaskActivityListener.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ITaskActivityListener.java
new file mode 100644
index 000000000..f9bdc4df8
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ITaskActivityListener.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2005 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
+ *******************************************************************************/
+/*
+ * Created on Jan 13, 2005
+ */
+package org.eclipse.mylar.tasks;
+
+import java.util.List;
+
+/**
+ * @author Mik Kersten
+ */
+public interface ITaskActivityListener {
+
+ public abstract void taskActivated(ITask task);
+
+ public abstract void tasksActivated(List<ITask> tasks);
+
+ public abstract void taskDeactivated(ITask task);
+
+}
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ITaskInfo.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ITaskInfo.java
new file mode 100644
index 000000000..e8fbc8de8
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ITaskInfo.java
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2005 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
+ *******************************************************************************/
+/*
+ * Created on Jan 12, 2005
+ */
+package org.eclipse.mylar.tasks;
+
+/**
+ * @author Mik Kersten
+ */
+public interface ITaskInfo {
+
+ public String getHandle();
+
+}
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/MylarTasksPlugin.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/MylarTasksPlugin.java
new file mode 100644
index 000000000..14e024a9d
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/MylarTasksPlugin.java
@@ -0,0 +1,224 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2005 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.tasks;
+
+import java.io.File;
+import java.util.List;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import org.eclipse.core.runtime.Preferences.IPropertyChangeListener;
+import org.eclipse.core.runtime.Preferences.PropertyChangeEvent;
+import org.eclipse.mylar.core.MylarPlugin;
+import org.eclipse.mylar.tasks.bugzilla.BugzillaContentProvider;
+import org.eclipse.mylar.tasks.bugzilla.BugzillaEditingMonitor;
+import org.eclipse.mylar.tasks.bugzilla.BugzillaMylarBridge;
+import org.eclipse.mylar.tasks.bugzilla.BugzillaReferencesProvider;
+import org.eclipse.mylar.tasks.bugzilla.BugzillaStructureBridge;
+import org.eclipse.mylar.tasks.bugzilla.ui.BugzillaUiBridge;
+import org.eclipse.mylar.ui.MylarUiPlugin;
+import org.eclipse.swt.events.ShellEvent;
+import org.eclipse.swt.events.ShellListener;
+import org.eclipse.ui.IStartup;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.internal.Workbench;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * @author Mik Kersten
+ */
+public class MylarTasksPlugin extends AbstractUIPlugin implements IStartup {
+
+ private static MylarTasksPlugin plugin;
+ private static TaskListManager taskListManager;
+ private BugzillaContentProvider bugzillaProvider;
+
+ public static final String FILE_EXTENSION = ".xml";
+ public static final String TASK_ID = "org.eclipse.mylar.tasks.userid";
+ public static final String DEFAULT_TASK_LIST_FILE = "tasklist" + FILE_EXTENSION;
+ public static final String TASK_EDITOR_ID = "org.eclipse.mylar.tasks.ui.taskEditor";
+ private ResourceBundle resourceBundle;
+
+ /** The bridge between Bugzilla and mylar */
+ private static BugzillaMylarBridge bridge = null;
+
+ private static BugzillaStructureBridge structureBridge = new BugzillaStructureBridge();
+
+ private static ITaskActivityListener TASK_LIST_LISTENER = new ITaskActivityListener() {
+
+ public void taskActivated(ITask task) {
+ MylarPlugin.getTaskscapeManager().taskActivated(task.getHandle(), task.getPath());
+ }
+
+ public void tasksActivated(List<ITask> tasks) {
+ for (ITask task : tasks) {
+ MylarPlugin.getTaskscapeManager().taskActivated(task.getHandle(), task.getPath());
+ }
+ }
+
+ public void taskDeactivated(ITask task) {
+ MylarPlugin.getTaskscapeManager().taskDeactivated(task.getHandle(), task.getPath());
+ }
+
+ };
+
+ private static ShellListener SHELL_LISTENER = new ShellListener() {
+ private void saveState() {
+ taskListManager.saveTaskList();
+ for(ITask task : taskListManager.getTaskList().getActiveTasks()) {
+ MylarPlugin.getTaskscapeManager().saveTaskscape(task.getHandle(), task.getPath());
+ }
+ }
+ public void shellClosed(ShellEvent arg0) {
+ saveState();
+ }
+ public void shellDeactivated(ShellEvent arg0) {
+ saveState();
+ }
+ public void shellActivated(ShellEvent arg0) { }
+ public void shellDeiconified(ShellEvent arg0) { }
+ public void shellIconified(ShellEvent arg0) { }
+ };
+
+ private static IPropertyChangeListener PREFERENCE_LISTENER = new IPropertyChangeListener() {
+
+ public void propertyChange(PropertyChangeEvent event) {
+ // TODO Auto-generated method stub
+ if (event.getProperty().equals(MylarPlugin.MYLAR_DIR)) {
+ if (event.getOldValue() instanceof String) {
+ String prevDir = (String) event.getOldValue();
+ MylarPlugin.getTaskscapeManager().updateMylarDirContents(prevDir);
+ getTaskListManager().updateTaskscapeReference(prevDir);
+
+ String path = MylarPlugin.getDefault().getUserDataDirectory() + File.separator + DEFAULT_TASK_LIST_FILE;
+ getTaskListManager().setFile(new File(path));
+ }
+ } else {
+ }
+ }
+ };
+
+ public MylarTasksPlugin() {
+ super();
+ plugin = this;
+ }
+
+ public void earlyStartup() {
+ final IWorkbench workbench = PlatformUI.getWorkbench();
+ workbench.getDisplay().asyncExec(new Runnable() {
+ public void run() {
+
+ MylarPlugin.getDefault().addBridge(structureBridge);
+ MylarPlugin.getTaskscapeManager().addListener(new BugzillaReferencesProvider());
+ MylarUiPlugin.getDefault().addAdapter(BugzillaStructureBridge.EXTENSION, new BugzillaUiBridge());
+ MylarPlugin.getDefault().getSelectionMonitors().add(new BugzillaEditingMonitor());
+
+ IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
+
+ Workbench.getInstance().getActiveWorkbenchWindow().getShell().addShellListener(SHELL_LISTENER);
+ MylarPlugin.getDefault().getPluginPreferences().addPropertyChangeListener(PREFERENCE_LISTENER);
+
+ if (window != null) {
+ // create a new bridge and initialize it
+ bridge = new BugzillaMylarBridge();
+ }
+ }
+ });
+ }
+
+ @Override
+ public void start(BundleContext context) throws Exception {
+ bugzillaProvider = new BugzillaContentProvider();
+ String path = MylarPlugin.getDefault().getUserDataDirectory() + File.separator + DEFAULT_TASK_LIST_FILE;
+ File taskListFile = new File(path);
+ taskListManager = new TaskListManager(taskListFile);
+ taskListManager.addListener(TASK_LIST_LISTENER);
+ taskListManager.readTaskList();
+ if (taskListManager.getTaskList() == null) taskListManager.createNewTaskList();
+
+ super.start(context);
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ super.stop(context);
+ plugin = null;
+ resourceBundle = null;
+ }
+
+ /**
+ * Get the bridge for this plugin
+ *
+ * @return The bugzilla mylar bridge
+ */
+ public static BugzillaMylarBridge getBridge() {
+ // make sure that the bridge initialized, if not, make a new one
+ if (bridge == null) {
+ bridge = new BugzillaMylarBridge();
+// MylarPlugin.getTaskscapeManager().addRelationshipProvider(new BugzillaRelationshipProvider());
+// MylarUiPlugin.getDefault().getAdapters().put(ITaskscapeNode.Kind.Bugzilla, new BugzillaUiAdapter());
+ }
+ return bridge;
+ }
+
+
+ public static TaskListManager getTaskListManager() {
+ return taskListManager;
+ }
+
+ /**
+ * Returns the shared instance.
+ */
+ public static MylarTasksPlugin getDefault() {
+ return plugin;
+ }
+
+ /**
+ * Returns the string from the plugin's resource bundle,
+ * or 'key' if not found.
+ */
+ public static String getResourceString(String key) {
+ ResourceBundle bundle = MylarTasksPlugin.getDefault().getResourceBundle();
+ try {
+ return (bundle != null) ? bundle.getString(key) : key;
+ } catch (MissingResourceException e) {
+ return key;
+ }
+ }
+
+ /**
+ * Returns the plugin's resource bundle,
+ */
+ public ResourceBundle getResourceBundle() {
+ try {
+ if (resourceBundle == null)
+ resourceBundle = ResourceBundle.getBundle("taskListPlugin.TaskListPluginPluginResources");
+ } catch (MissingResourceException x) {
+ resourceBundle = null;
+ }
+ return resourceBundle;
+ }
+
+ public BugzillaContentProvider getBugzillaProvider() {
+ return bugzillaProvider;
+ }
+
+ public void setBugzillaProvider(BugzillaContentProvider bugzillaProvider) {
+ this.bugzillaProvider = bugzillaProvider;
+ }
+
+ public static BugzillaStructureBridge getStructureBridge() {
+ return structureBridge;
+ }
+}
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/Task.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/Task.java
new file mode 100644
index 000000000..7702f8e97
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/Task.java
@@ -0,0 +1,306 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2005 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
+ *******************************************************************************/
+/*
+ * Created on Dec 22, 2004
+ */
+package org.eclipse.mylar.tasks;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.mylar.core.MylarPlugin;
+import org.eclipse.mylar.tasks.ui.TaskEditorInput;
+import org.eclipse.mylar.tasks.ui.views.TaskListView;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.internal.Workbench;
+
+
+/**
+ * @author Mik Kersten
+ */
+public class Task implements ITask {
+
+ private static final long serialVersionUID = 3545518391537382197L;
+ private boolean active = false;
+ protected String handle = "-1";
+ private boolean category = false;
+
+
+ /**
+ * Either to local resource or URL.
+ * TODO: consider changing to java.net.URL
+ */
+ private String path;
+ private String label;
+ private String priority = "P3";
+ private String notes = "";
+ private String estimatedTime = "";
+ private String elapsedTime = "";
+ private boolean completed;
+ private transient List<Category> categories = new ArrayList<Category>();
+ private RelatedLinks links = new RelatedLinks();
+
+ /**
+ * null if root
+ */
+ private transient ITask parent;
+
+ private List<ITask> children = new ArrayList<ITask>();
+
+ @Override
+ public String toString() {
+ return label;
+ }
+
+ public String getPath() {
+ // returns relative path Mylar Directory
+ return path;
+ }
+
+// public String getRelativePath() {
+// //returns relative path from Mylar Directory
+//
+// if (path.startsWith("..")) {
+// return "../" + path;
+// } else {
+// return path.substring(path.indexOf('/')+1, path.length());
+// }
+// }
+
+ public void setPath(String path) {
+ if (path.startsWith(".mylar")) {
+ this.path = path.substring(path.lastIndexOf('/')+1, path.length());
+ } else if (!path.equals("")) {
+ this.path = path;
+ }
+ }
+
+ public Task(String handle, String label) {
+ this.handle = handle;
+ this.label = label;
+ this.path = handle;
+ }
+
+ public List<ITask> getChildren() {
+ return children;
+ }
+
+ public String getHandle() {
+ return handle;
+ }
+ public void setHandle(String id) {
+ this.handle = id;
+ }
+ public String getLabel() {
+ return label;
+ }
+ public void setLabel(String label) {
+ this.label = label;
+ }
+ public ITask getParent() {
+ return parent;
+ }
+ public void setParent(ITask parent) {
+ this.parent = parent;
+ }
+ public Object getAdapter(Class adapter) {
+ return null;
+ }
+
+ public void removeSubtask(ITask task) {
+ children.remove(task);
+ task.setParent(null); // HACK
+ }
+
+ public void addSubtask(ITask task) {
+ children.add(task);
+ task.setParent(this);
+ }
+
+ /**
+ * Package visible in order to prevent sets that don't update the index.
+ */
+ public void setActive(boolean active) {
+ this.active = active;
+ }
+
+ public boolean isActive() {
+ return active;
+ }
+
+ public void openTaskInEditor() {
+ Workbench.getInstance().getDisplay().asyncExec(new Runnable() {
+ public void run() {
+ openTaskEditor();
+ }
+ });
+ }
+
+ /**
+ * Opens the task in an editor.
+ * @return Resulting <code>IStatus</code> of the operation
+ */
+ protected void openTaskEditor() {
+
+ // get the active page so that we can reuse it
+ IWorkbenchPage page = MylarTasksPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getActivePage();
+
+ // if we couldn't get a page, get out
+ if (page == null) {
+ return;
+ }
+
+ IEditorInput input = new TaskEditorInput(this);
+ try
+ {
+ // try to open an editor on the input task
+ page.openEditor(input, MylarTasksPlugin.TASK_EDITOR_ID);
+
+ }
+ catch (PartInitException ex)
+ {
+ MylarPlugin.log(this.getClass().toString(), ex);
+ }
+ }
+
+ /**
+ * Refreshes the tasklist viewer.
+ */
+ public void notifyTaskDataChange() {
+ Workbench.getInstance().getDisplay().asyncExec(new Runnable() {
+ public void run() {
+ if (TaskListView.getDefault() != null) TaskListView.getDefault().notifyTaskDataChanged();
+ }
+ });
+ }
+
+ public String getToolTipText() {
+ // No tool-tip used for a general task as of yet.
+ return null;
+ }
+ public List<Category> getCategories() {
+ return categories;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof Task && obj != null) {
+ return this.getHandle() == ((Task)obj).getHandle();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return this.getHandle().hashCode();
+ }
+ public boolean isCompleted() {
+ return completed;
+ }
+ public void setCompleted(boolean completed) {
+ this.completed = completed;
+ }
+
+ public boolean isCategory() {
+ return category;
+ }
+
+ public void setIsCategory(boolean category) {
+ this.category = category;
+ }
+
+ public String getPriority() {
+ return priority;
+ }
+
+ public void setPriority(String priority) {
+ this.priority = priority;
+ }
+
+ public RelatedLinks getRelatedLinks() {
+ // TODO: removed check for null once xml updated.
+ if (links == null) {
+ links = new RelatedLinks();
+ }
+ return links;
+ }
+
+ public void setRelatedLinks(RelatedLinks relatedLinks) {
+ this.links = relatedLinks;
+ }
+
+ public void addLink(String url) {
+ links.add(url);
+ }
+
+ public void removeLink(String url) {
+ links.remove(url);
+ }
+
+ public String getNotes() {
+ // TODO: removed check for null once xml updated.
+ if (notes == null) {
+ notes = "";
+ }
+ return notes;
+ }
+
+ public void setNotes(String notes) {
+ this.notes = notes;
+ }
+
+ public String getElapsedTime() {
+ // TODO: removed check for null once xml updated.
+ if (elapsedTime == null) {
+ elapsedTime = "";
+ }
+ return elapsedTime;
+ }
+
+ public void setElapsedTime(String elapsed) {
+ this.elapsedTime = elapsed;
+ }
+
+ public String getEstimatedTime() {
+ // TODO: removed check for null once xml updated.
+ if (estimatedTime == null) {
+ estimatedTime = "";
+ }
+ return estimatedTime;
+ }
+
+ public void setEstimatedTime(String estimated) {
+ this.estimatedTime = estimated;
+ }
+
+ public List<ITask> getSubTasksInProgress() {
+ List<ITask> inprogress = new ArrayList<ITask>();
+ for (ITask task : children) {
+ if (!task.isCompleted()) {
+ inprogress.add(task);
+ }
+ }
+ return inprogress;
+ }
+
+ public List<ITask> getCompletedSubTasks() {
+ List<ITask> complete = new ArrayList<ITask>();
+ for (ITask task : children) {
+ if (task.isCompleted()) {
+ complete.add(task);
+ }
+ }
+ return complete;
+ }
+}
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/TaskList.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/TaskList.java
new file mode 100644
index 000000000..cd3529701
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/TaskList.java
@@ -0,0 +1,138 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2005 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
+ *******************************************************************************/
+/*
+ * Created on Dec 22, 2004
+ */
+package org.eclipse.mylar.tasks;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.mylar.tasks.BugzillaTask.BugTaskState;
+
+
+/**
+ * @author Mik Kersten
+ */
+public class TaskList implements Serializable {
+
+ private static final long serialVersionUID = 3618984485791021105L;
+
+ private List<ITask> rootTasks = new ArrayList<ITask>();
+ private transient List<ITask> activeTasks = new ArrayList<ITask>();
+
+ public void addRootTask(ITask task) {
+ rootTasks.add(task);
+ }
+
+ public void setActive(ITask task, boolean active) {
+ task.setActive(active);
+ if (active) {
+ activeTasks.add(task);
+ } else {
+ activeTasks.remove(task);
+ }
+ }
+
+ /**
+ * TODO: make data structure handle this traversal
+ */
+ public ITask getTaskForId(String id) {
+ return setActiveHelper(rootTasks, id);
+ }
+
+ private ITask setActiveHelper(List<ITask> tasks, String id) {
+ for (ITask task : tasks) {
+ if (task.getHandle() == id) {
+ return task;
+ } else {
+ ITask child = setActiveHelper(task.getChildren(), id);
+ if (child != null) return child;
+ }
+ }
+ return null;
+ }
+
+ public List<ITask> getActiveTasks() {
+ return activeTasks;
+ }
+
+ public List<ITask> getTaskFor(Category category) {
+ List<ITask> categoryTasks = new ArrayList<ITask>();
+ for (ITask task : rootTasks) {
+ if (task.getCategories().contains(category)) categoryTasks.add(task);
+ }
+ return categoryTasks;
+ }
+
+ public List<ITask> getRootTasks() {
+ return rootTasks;
+ }
+
+ public Set<Category> getCategories() {
+ Set<Category> categories = new HashSet<Category>();
+ for (ITask task : rootTasks) {
+ categories.addAll(task.getCategories());
+ }
+ return categories;
+ }
+
+ public void refreshRestoredTasks() {
+ activeTasks = new ArrayList<ITask>();
+ activateRestoredTasks(rootTasks);
+ restoreParents(rootTasks, null);
+ refreshBugReports(rootTasks);
+ }
+ private void activateRestoredTasks(List<ITask> tasks) {
+ for (ITask task : tasks) {
+ if (task.isActive()) {
+ setActive(task, true);
+ }
+ activateRestoredTasks(task.getChildren());
+ }
+ }
+ private void restoreParents(List<ITask> tasks, ITask parent) {
+ for (ITask task : tasks) {
+ task.setParent(parent);
+ restoreParents(task.getChildren(), task);
+ }
+ }
+ private void refreshBugReports(List<ITask> tasks) {
+ for (ITask task : tasks) {
+ if (task instanceof BugzillaTask) {
+ ((BugzillaTask)task).readBugReport();
+ ((BugzillaTask)task).setState(BugTaskState.FREE);
+ }
+ refreshBugReports(task.getChildren());
+ }
+ }
+ public List<ITask> getTasksInProgress() {
+ List<ITask> inprogress = new ArrayList<ITask>();
+ for (ITask task : rootTasks) {
+ if (!task.isCompleted()) {
+ inprogress.add(task);
+ }
+ }
+ return inprogress;
+ }
+ public List<ITask> getCompletedTasks() {
+ List <ITask> complete = new ArrayList<ITask>();
+ for (ITask task : rootTasks) {
+ if (task.isCompleted()) {
+ complete.add(task);
+ }
+ }
+ return complete;
+ }
+}
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/TaskListManager.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/TaskListManager.java
new file mode 100644
index 000000000..93ace114e
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/TaskListManager.java
@@ -0,0 +1,159 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2005 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
+ *******************************************************************************/
+/*
+ * Created on Dec 26, 2004
+ */
+package org.eclipse.mylar.tasks;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.mylar.core.MylarPlugin;
+import org.eclipse.mylar.tasks.util.RelativePathUtil;
+import org.eclipse.mylar.tasks.util.XmlUtil;
+
+
+/**
+ * @author Mik Kersten
+ */
+public class TaskListManager {
+
+ private File file;
+ private TaskList taskList = new TaskList();
+ private List<ITaskActivityListener> listeners = new ArrayList<ITaskActivityListener>();
+ private int nextTaskId;
+
+ public TaskListManager(File file) {
+ this.file = file;
+ if (MylarPlugin.getDefault().getPreferenceStore().contains(MylarTasksPlugin.TASK_ID)) { // TODO: fix to MylarTasksPlugin
+ nextTaskId = MylarPlugin.getDefault().getPreferenceStore().getInt(MylarTasksPlugin.TASK_ID);
+ } else {
+ nextTaskId = 1;
+ }
+ }
+
+ public TaskList createNewTaskList() {
+ return taskList;
+ }
+
+ public String genUniqueTaskId() {
+ return "task-" + nextTaskId++;
+ }
+
+ public boolean readTaskList() {
+ try {
+ if (file.exists()) {
+ XmlUtil.readTaskList(taskList, file);
+ for (ITaskActivityListener listener : listeners) listener.tasksActivated(taskList.getActiveTasks());
+ }
+ return true;
+ } catch (Exception e) {
+ MylarPlugin.log(this.getClass().toString(), e);
+ return false;
+ }
+ }
+
+ public void saveTaskList() {
+ try {
+ XmlUtil.writeTaskList(taskList, file);
+ MylarPlugin.getDefault().getPreferenceStore().setValue(MylarTasksPlugin.TASK_ID, nextTaskId);
+ } catch (Exception e) {
+ e.printStackTrace(); // TODO: fix
+// MylarPlugin.fail(e, "Could not save task list", true);
+ }
+ }
+
+ public TaskList getTaskList() {
+ return taskList;
+ }
+
+ public void setTaskList(TaskList taskList) {
+ this.taskList = taskList;
+ }
+
+ public void deleteTask(ITask task) {
+ taskList.setActive(task, false);
+ if (taskList.getRootTasks().contains(task)) {
+ taskList.getRootTasks().remove(task);
+ } else {
+ task.getParent().getChildren().remove(task);
+ }
+ }
+
+ public void addListener(ITaskActivityListener listener) {
+ listeners.add(listener);
+ }
+
+ public void removeListener(ITaskActivityListener listener) {
+ listeners.remove(listener);
+ }
+
+ public void activateTask(ITask task) {
+ if (task.isCategory()) {
+ for (ITask childTask : task.getChildren()) {
+ taskList.setActive(childTask, true);
+ for (ITaskActivityListener listener : listeners) listener.taskActivated(childTask);
+ }
+ } else {
+ taskList.setActive(task, true);
+ for (ITaskActivityListener listener : listeners) listener.taskActivated(task);
+ }
+ }
+
+ public void deactivateTask(ITask task) {
+ if (task.isCategory()) {
+ for (ITask childTask : task.getChildren()) {
+ taskList.setActive(childTask, false);
+ for (ITaskActivityListener listener : listeners) listener.taskDeactivated(childTask);
+ }
+ } else {
+ taskList.setActive(task, false);
+ for (ITaskActivityListener listener : listeners) listener.taskDeactivated(task);
+ }
+ }
+
+ public void updateTaskscapeReference(String prevDir) {
+ List<ITask> rootTasks = this.getTaskList().getRootTasks();
+ updateTaskscapeReferenceHelper(rootTasks, prevDir);
+ }
+ public void updateTaskscapeReferenceHelper(List<ITask> list, String prevDir) {
+ for (ITask task : list) {
+ if (!task.getPath().startsWith("task-")) {
+ if (task.getPath().startsWith("..")) {
+ String path = task.getPath();
+ File d = new File(prevDir);
+ while (path.startsWith("..")) {
+ d = d.getParentFile();
+ path = path.substring(3, path.length());
+ }
+
+ String absPath = d.getPath() + "/" + path + MylarTasksPlugin.FILE_EXTENSION;
+ absPath = absPath.replaceAll("\\\\", "/");
+ String rel = RelativePathUtil.findRelativePath(MylarPlugin.getDefault().getUserDataDirectory() + "/", absPath);
+ task.setPath(rel);
+ } else {
+ String absPath = prevDir + "/" + task.getPath() + MylarTasksPlugin.FILE_EXTENSION;
+ absPath = absPath.replaceAll("\\\\", "/");
+ String rel = RelativePathUtil.findRelativePath(MylarPlugin.getDefault().getUserDataDirectory(), absPath);
+ task.setPath(rel);
+ }
+ }
+ updateTaskscapeReferenceHelper(task.getChildren(), prevDir);
+ }
+ }
+ public void setFile(File f) {
+ if (this.file.exists()) {
+ this.file.delete();
+ }
+ this.file = f;
+ }
+}
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/BugzillaContentProvider.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/BugzillaContentProvider.java
new file mode 100644
index 000000000..f8e62a5f0
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/BugzillaContentProvider.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2005 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
+ *******************************************************************************/
+/*
+ * Created on Jan 13, 2005
+ */
+package org.eclipse.mylar.tasks.bugzilla;
+
+import org.eclipse.mylar.bugzilla.core.BugReport;
+import org.eclipse.mylar.tasks.BugzillaTask;
+
+
+/**
+ * @author Mik Kersten
+ * @author Eric Booth
+ */
+public class BugzillaContentProvider {
+
+ public BugzillaContentProvider() {
+ // don't have any initialization to do
+ }
+
+ /**
+ * @return String containing bug priority and label, e.g. "[12345] P2: fix failing test"
+ */
+ public String getBugzillaDescription(BugzillaTask bugTask) {
+ if (bugTask == null) return "<no info>";
+
+ String prefix = //((bugTask.isDirty()) ? ">" : "") +
+ "<" + BugzillaTask.getBugId(bugTask.getHandle()) + ">: ";
+
+ if (bugTask.getState() == BugzillaTask.BugTaskState.DOWNLOADING) {
+ return prefix + ": <Downloading bug report from server...>";
+ } else if (bugTask.getState() == BugzillaTask.BugTaskState.OPENING) {
+ return prefix + ": <Opening bug report in editor...>";
+ } else if (bugTask.getState() == BugzillaTask.BugTaskState.COMPARING) {
+ return prefix + ": <Comparing bug report with server...>";
+ } else if (bugTask.getState() == BugzillaTask.BugTaskState.WAITING) {
+ return prefix + ": <Waiting to check server...>";
+ }
+
+ // generate the label
+ if (bugTask.isBugDownloaded()) {
+ BugReport report = bugTask.getBugReport();
+ return prefix + report.getSummary();
+ }
+ else {
+ return prefix + ": <could not find bug>";
+ }
+ }
+
+}
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/BugzillaEditingMonitor.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/BugzillaEditingMonitor.java
new file mode 100644
index 000000000..8eadb4e55
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/BugzillaEditingMonitor.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2005 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
+ *******************************************************************************/
+/*
+ * Created on Apr 27, 2005
+ */
+package org.eclipse.mylar.tasks.bugzilla;
+
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.mylar.bugzilla.ui.editor.AbstractBugEditor;
+import org.eclipse.mylar.bugzilla.ui.outline.BugzillaReportSelection;
+import org.eclipse.mylar.core.AbstractSelectionMonitor;
+import org.eclipse.mylar.tasks.ui.BugzillaTaskEditor;
+import org.eclipse.ui.IWorkbenchPart;
+
+
+/**
+ * @author Mik Kersten
+ */
+public class BugzillaEditingMonitor extends AbstractSelectionMonitor {
+
+ public BugzillaEditingMonitor() {
+ super();
+ }
+
+ @Override
+ protected void handleWorkbenchPartSelection(IWorkbenchPart part, ISelection selection) {
+ if(!(part instanceof AbstractBugEditor) && !(part instanceof BugzillaTaskEditor))
+ return;
+
+ if(selection instanceof StructuredSelection){
+ StructuredSelection ss = (StructuredSelection)selection;
+ Object object = ss.getFirstElement();
+ if(object instanceof BugzillaReportSelection) super.handleElementSelection(part, object);
+ }
+ }
+
+}
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/BugzillaMylarBridge.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/BugzillaMylarBridge.java
new file mode 100644
index 000000000..7d2265e84
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/BugzillaMylarBridge.java
@@ -0,0 +1,142 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2005 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
+ *******************************************************************************/
+/*
+ * Created on Oct 1, 2004
+ */
+package org.eclipse.mylar.tasks.bugzilla;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IMember;
+
+
+/**
+ * Class to handle the bridge between mylar and bugzilla
+ *
+ * @author sminto
+ */
+public class BugzillaMylarBridge {
+
+ /** The hash of all of the landmarks and their related search hits */
+ private HashMap<String, Map<Integer, List<BugzillaReportNode>>> landmarksHash;
+ /**
+ * The currently running search jobs so that we can cancel it if necessary <br> KEY: IMember VALUE: Job
+ */
+ public static HashMap<String, Job> runningJobs = new HashMap<String, Job>();
+
+ /**
+ * Constructor
+ */
+ public BugzillaMylarBridge() {
+ landmarksHash = new HashMap<String, Map<Integer, List<BugzillaReportNode>>>();
+ }
+
+
+ /**
+ * Remove a landmark from the hash
+ *
+ * @param removed
+ * This landmark to remove (IJavaElement)
+ */
+ public void removeFromLandmarksHash(IJavaElement removed) {
+ landmarksHash.remove(removed.getHandleIdentifier());
+ }
+
+ /**
+ * Remove all of the landmarks from the hash that are in the list
+ *
+ * @param removed
+ * This list of landmarks to remove (IJavaElements)
+ */
+ public void removeFromLandmarksHash(List<IJavaElement> removed) {
+
+ for(IJavaElement je : removed) {
+ landmarksHash.remove(je.getHandleIdentifier());
+ }
+ }
+
+ /**
+ * Add data to the landmarks hash
+ *
+ * @param doiList
+ * The list of BugzillaSearchHitDoiInfo
+ * @param m
+ * The member that this list is for
+ */
+ public void addToLandmarksHash(List<BugzillaReportNode> doiList, IMember m, int scope) {
+ Map<Integer, List<BugzillaReportNode>> searches = landmarksHash.get(m.getHandleIdentifier());
+
+ if(searches == null){
+ searches = new HashMap<Integer, List<BugzillaReportNode>>();
+ }
+ searches.put(scope, doiList);
+ landmarksHash.put(m.getHandleIdentifier(), searches);
+ }
+
+ /**
+ * Get the doiList for the given IMember from the landmarks hash
+ *
+ * @param m
+ * The member to get the doiList for
+ * @return The doiList or null if it doesn't exist
+ */
+ public List<BugzillaReportNode> getFromLandmarksHash(IMember m, int scope) {
+ Map<Integer, List<BugzillaReportNode>> scopes = landmarksHash.get(m.getHandleIdentifier());
+ if(scopes == null)
+ return null;
+ else
+ return scopes.get(scope);
+ }
+
+ /**
+ * Determine whether the current element has a search job running for it
+ *
+ * @param e
+ * The element that we want to know whether there is a search job
+ * or not
+ * @return <code>true</code> if it does else <code>false</code>
+ */
+ public static boolean doesJobExist(String handle) {
+ return runningJobs.containsKey(handle);
+ }
+
+ /**
+ * Remove search job for the given element
+ *
+ * @param m
+ * The element that we want to make sure that the search is
+ * canceled for
+ */
+ public static void removeSearchJob(String handle) {
+
+ // make sure that there wasn't a previous search job that we know
+ // of. If there was, cancel it
+ if (doesJobExist(handle)) {
+ // get the search job and wait until it is cancelled
+ Job prevJob = runningJobs.get(handle);
+ prevJob.cancel();
+ runningJobs.remove(handle);
+ }
+ }
+
+ /**
+ * Add a search job to our list
+ * @param handle The handle of the element that we are searching for
+ * @param searchJob The job that represents the search
+ */
+ public static void addJob(String handle, Job searchJob) {
+ runningJobs.put(handle, searchJob);
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/BugzillaReferencesProvider.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/BugzillaReferencesProvider.java
new file mode 100644
index 000000000..219bf1b74
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/BugzillaReferencesProvider.java
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2005 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
+ *******************************************************************************/
+/*
+ * Created on Feb 2, 2005
+ */
+package org.eclipse.mylar.tasks.bugzilla;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IMember;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.mylar.core.model.ITaskscapeNode;
+import org.eclipse.mylar.core.search.IActiveSearchListener;
+import org.eclipse.mylar.core.search.IMylarSearchOperation;
+import org.eclipse.mylar.core.search.RelationshipProvider;
+import org.eclipse.mylar.tasks.bugzilla.search.BugzillaMylarSearch;
+
+
+/**
+ * @author sminto
+ */
+public class BugzillaReferencesProvider extends RelationshipProvider {
+
+ public static final String ID = "org.eclipse.mylar.bugzilla.search.references";
+ public static final String NAME = "Bugilla report references";
+
+ public BugzillaReferencesProvider() {
+ super(BugzillaStructureBridge.EXTENSION, ID);
+ }
+
+ protected boolean acceptElement(IJavaElement javaElement) {
+ return javaElement != null
+ && (javaElement instanceof IMember || javaElement instanceof IType);
+ }
+
+ /**
+ * HACK: checking kind as string - don't want the dependancy to mylar.java
+ */
+ @Override
+ protected void findRelated(final ITaskscapeNode node, int degreeOfSeparation) {
+ if (!node.getStructureKind().equals("java")) return;
+ IJavaElement javaElement = JavaCore.create(node.getElementHandle());
+ if (!acceptElement(javaElement)) {
+ return;
+ }
+ runJob(node, degreeOfSeparation);
+
+ //XXX what if degreeOfSeparation is 5?
+ }
+
+ @Override
+ public IMylarSearchOperation getSearchOperation(ITaskscapeNode node, int limitTo, int degreeOfSepatation) {
+ IJavaElement javaElement = JavaCore.create(node.getElementHandle());
+ return new BugzillaMylarSearch(degreeOfSepatation, javaElement);
+ }
+
+ private void runJob(final ITaskscapeNode node, final int degreeOfSeparation) {
+ BugzillaMylarSearch search = (BugzillaMylarSearch)getSearchOperation(node, 0, degreeOfSeparation);
+
+ search.addListener(new IActiveSearchListener(){
+
+ private boolean gathered = false;
+
+ public void searchCompleted(List<?> nodes) {
+ Iterator<?> itr = nodes.iterator();
+
+ while(itr.hasNext()) {
+ Object o = itr.next();
+ if(o instanceof BugzillaReportNode){
+ BugzillaReportNode bugzillaNode = (BugzillaReportNode)o;
+ incrementInterest(degreeOfSeparation, BugzillaStructureBridge.EXTENSION, bugzillaNode.getElementHandle());
+ }
+ }
+ gathered = true;
+ }
+
+ public boolean resultsGathered() {
+ return gathered;
+ }
+
+ });
+ search.run(new NullProgressMonitor());
+ }
+
+ @Override
+ protected String getSourceId() {
+ return ID;
+ }
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+}
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/BugzillaReportNode.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/BugzillaReportNode.java
new file mode 100644
index 000000000..23fd6c080
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/BugzillaReportNode.java
@@ -0,0 +1,184 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2005 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
+ *******************************************************************************/
+/*
+ * Created on Oct 21, 2004
+ */
+package org.eclipse.mylar.tasks.bugzilla;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.security.auth.login.LoginException;
+
+import org.eclipse.mylar.bugzilla.core.BugReport;
+import org.eclipse.mylar.bugzilla.core.BugzillaRepository;
+import org.eclipse.mylar.bugzilla.search.BugzillaSearchHit;
+
+
+/**
+ * Class to store the DoiInfo of a BugzillaSearchHit
+ *
+ * @author sminto
+ */
+public class BugzillaReportNode {
+
+ private static final long serialVersionUID = 3257004367222419506L;
+
+ /** The BugzillaSearchHit associated with this DoiInfo */
+ private BugzillaSearchHit hit;
+
+ /** Whether this search hit was from an exact search like a stack trace */
+ private boolean isExact = false;
+
+ /** List of all of the StackTrace's in the given bug */
+ private List<StackTrace> stackTraces;
+
+ /** The bug report associated with this DoiInfo */
+ private BugReport bug;
+
+ /**
+ * Constructor
+ *
+ * @param initialValue
+ * The initial Doi value
+ * @param hit
+ * The BugzillaSearchHit associated with this DoiInfo
+ * @param isExact
+ * Whether the search was exact or not
+ */
+ public BugzillaReportNode(float initialValue, BugzillaSearchHit hit,
+ boolean isExact) {
+ this.hit = hit;
+ this.isExact = isExact;
+ bug = null;
+ stackTraces = new ArrayList<StackTrace>();
+ }
+
+ /**
+ * Get the bugzilla search hit relating to this DoiInfo
+ *
+ * @return The BugzillaSearchHit related to this DoiInfo
+ */
+ public BugzillaSearchHit getHit() {
+ return hit;
+ }
+
+ @Override
+ public String toString() {
+ return hit.toString();
+ }
+
+ /**
+ * Determine if the search hit this represents is exact or not
+ *
+ * @return <code>true</code> if the search was exact otherwise
+ * <code>false</code>
+ */
+ public boolean isExact() {
+ return isExact;
+ }
+
+ /**
+ * Set whether this bug has any exact elements in it - the search used was fully qualified
+ *
+ * @param isExact -
+ * Whether there are any exact element matches in it
+ */
+ public void setExact(boolean isExact) {
+ this.isExact = isExact;
+ }
+
+ /**
+ * Get the bug report associated with this DoiInfo<br>
+ * The bug is downloaded if it was not previously
+ *
+ * @return Returns the BugReport
+ *
+ * @throws IOException
+ * @throws LoginException
+ * @throws MalformedURLException
+ */
+ public BugReport getBug() throws MalformedURLException, LoginException, IOException {
+ if(bug == null){
+
+ // get the bug report
+ bug = BugzillaRepository.getInstance().getBug(
+ hit.getId());
+ }
+ return bug;
+ }
+
+ /**
+ * Set the bug report associated with this DoiInfo
+ *
+ * @param bug -
+ * BugReport that this is associated with
+ */
+ public void setBug(BugReport bug) {
+ this.bug = bug;
+ }
+
+ /**
+ * Get all of the stack traces contained in the bug
+ *
+ * @return Returns a list of StackTrace's
+ */
+ public List<StackTrace> getStackTraces() {
+ return stackTraces;
+ }
+
+ /**
+ * Determine whether the doi info has any stack traces associated with it
+ * @return <code>true</code> if there are some stack traces else <code>false</code>
+ */
+ public boolean hasStackTraces(){
+ return !stackTraces.isEmpty();
+ }
+
+ /**
+ * Add a stack trace to this DoiInfo
+ *
+ * @param stackTrace -
+ * The StackTrace to add
+ */
+ public void addStackTrace(StackTrace stackTrace) {
+ this.stackTraces.add(stackTrace);
+ }
+
+ /**
+ * Add an array of stack traces to this DoiInfo
+ *
+ * @param stackTracesToAdd -
+ * The StackTraces to add
+ */
+ public void addStackTraces(StackTrace[] stackTracesToAdd) {
+ for (int i = 0; i < stackTracesToAdd.length; i++)
+ this.stackTraces.add(stackTracesToAdd[i]);
+ }
+
+ /**
+ * Get the name of the bug report
+ * @return The name of the bug report, max 20 characters
+ */
+ public String getName() {
+ final int MAX_LENGTH = 100;
+ String description = hit.getDescription();
+ int length = description.length();
+ if (length > MAX_LENGTH) description = description.substring(0, MAX_LENGTH) + "..";
+ return "bug " + hit.getId() + ": " + description;
+ }
+
+ public String getElementHandle() {
+ return "<server-id>;" + hit.getId();
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/BugzillaStructureBridge.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/BugzillaStructureBridge.java
new file mode 100644
index 000000000..fecb66c14
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/BugzillaStructureBridge.java
@@ -0,0 +1,169 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2005 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
+ *******************************************************************************/
+/*
+ * Created on May 2, 2005
+ */
+package org.eclipse.mylar.tasks.bugzilla;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.mylar.bugzilla.core.BugzillaRepository;
+import org.eclipse.mylar.bugzilla.ui.editor.AbstractBugEditor;
+import org.eclipse.mylar.bugzilla.ui.outline.BugzillaOutlineNode;
+import org.eclipse.mylar.bugzilla.ui.outline.BugzillaReportSelection;
+import org.eclipse.mylar.bugzilla.ui.outline.BugzillaTools;
+import org.eclipse.mylar.core.IMylarStructureBridge;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.views.markers.internal.ProblemMarker;
+
+
+public class BugzillaStructureBridge implements IMylarStructureBridge {
+
+ public final static String EXTENSION = "bugzilla";
+
+ public String getResourceExtension() {
+ return EXTENSION;
+ }
+
+ public BugzillaStructureBridge() {
+ super();
+ }
+
+ /**
+ * Handle format: <server-name:port>;<bug-id>;<comment#>
+ *
+ * Use: BugzillaTools ???
+ */
+ public String getHandleIdentifier(Object object) {
+ if(object instanceof BugzillaOutlineNode){
+ BugzillaOutlineNode n = (BugzillaOutlineNode)object;
+ return BugzillaTools.getHandle(n);
+ }
+ else if(object instanceof BugzillaReportSelection){
+ BugzillaReportSelection n = (BugzillaReportSelection)object;
+ return BugzillaTools.getHandle(n);
+ }
+ return null;
+ }
+
+ public Object getObjectForHandle(String handle) {
+ String [] parts = handle.split(";");
+ if (parts.length >= 2){
+// String server = parts[0]; TODO add back in when we deal with multiple servers
+ int id = Integer.parseInt(parts[1]);
+ int commentNumber = -1;
+ if(parts.length == 3){
+ commentNumber = Integer.parseInt(parts[2]);
+ }
+ try{
+ // get the bugzillaOutlineNode for the element
+ IEditorPart editorPart = null;
+ try{
+ editorPart = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor();
+ }catch(NullPointerException e){
+ // do nothing, this just means that there is no active page
+ }
+ if(editorPart != null && editorPart instanceof AbstractBugEditor){
+ AbstractBugEditor abe = ((AbstractBugEditor)editorPart);
+ BugzillaOutlineNode node = abe.getModel();
+ return findNode(node, commentNumber);
+ }
+
+ // TODO There is a huge slowdown here always getting the object - maybe make bugzilla store the bug for a while in memory or as temp ofline?
+ BugzillaOutlineNode node = BugzillaOutlineNode.parseBugReport(BugzillaRepository.getInstance().getCurrentBug(id));
+ return findNode(node, commentNumber);
+ }catch(Exception e){
+ return null;
+ }
+ }
+ else{
+ return null;
+ }
+ }
+
+ private BugzillaOutlineNode findNode(BugzillaOutlineNode startNode, int commentNumber){
+
+ if(commentNumber == -1){
+ return startNode;
+ }else if(startNode.getComment() != null && startNode.getComment().getNumber() == commentNumber -1){
+ return startNode;
+ } else if(startNode.isCommentHeader() && commentNumber == 1){
+ return startNode;
+ }else if(startNode.isDescription() && commentNumber == 0){
+ return startNode;
+ }
+
+ BugzillaOutlineNode[] children = startNode.getChildren();
+ for(int i = 0; i < children.length; i++){
+ BugzillaOutlineNode n = findNode(children[i], commentNumber);
+ if(n != null)
+ return n;
+ }
+ return null;
+ }
+
+ public String getParentHandle(String handle) {
+ BugzillaOutlineNode bon = (BugzillaOutlineNode)getObjectForHandle(handle);
+ if(bon != null && bon.getParent() != null)
+ return BugzillaTools.getHandle(bon.getParent());
+ else
+ return null;
+// String [] parts = handle.split(";");
+// if (parts.length == 1){
+// return null;
+// }else if (parts.length > 2) {
+// String newHandle = "";
+// for(int i = 0; i < parts.length - 1; i++)
+// newHandle += parts[i] + ";";
+// System.out.println(handle + " " + newHandle.substring(0, newHandle.length() - 1));
+// return newHandle.substring(0, newHandle.length() - 1);
+//// return handle.substring(0, handle.lastIndexOf(";"));
+// }
+// return null;
+ }
+
+ public String getName(Object object) {
+ if(object instanceof BugzillaOutlineNode){
+ BugzillaOutlineNode b = (BugzillaOutlineNode)object;
+ return BugzillaTools.getName(b);
+ }
+ return "";
+ }
+
+ public boolean acceptAsLandmark(String handle) {
+ return false;
+ }
+
+ public boolean acceptsObject(Object object) {
+ return object instanceof BugzillaOutlineNode || object instanceof BugzillaReportSelection;
+ }
+
+ public boolean canFilter(Object element) {
+ return true;
+ }
+
+ public boolean isDocument(String handle) {
+ return (handle.indexOf(';') == handle.lastIndexOf(';') && handle.indexOf(";") != -1);
+ }
+
+ public String getHandleForMarker(ProblemMarker marker) {
+ return null;
+ }
+
+ public IProject getProjectForObject(Object object) {
+ // bugzilla objects do not yet sit in a project
+ return null;
+ }
+
+ public String getResourceExtension(String elementHandle) {
+ return getResourceExtension();
+ }
+}
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/StackTrace.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/StackTrace.java
new file mode 100644
index 000000000..ec9a10fc0
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/StackTrace.java
@@ -0,0 +1,375 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2005 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
+ *******************************************************************************/
+/*
+ * Created on Dec 8, 2004
+ */
+package org.eclipse.mylar.tasks.bugzilla;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class to hold all of the information about a stack trace
+ *
+ * @author sminto
+ */
+public class StackTrace {
+
+ /** The length of the stack trace in the original string */
+ private int length;
+
+ /** The offset of the stack trace in the orignal string */
+ private int offset;
+
+ /** The string of the stack trace */
+ private String stackTrace;
+
+ /** This is the comment that the stack trace appeared in. String if desciption else Comment */
+ private Object comment;
+
+ /**
+ * Constructor
+ *
+ * @param stackTrace
+ * The stack trace string
+ * @param offset
+ * The offset of the stack trace in the original string
+ * @param length
+ * The length of the stack trace in the original string
+ * @param comment
+ * The comment that the stack trace came from
+ */
+ public StackTrace(String stackTrace, int offset, int length, Object comment) {
+ this.stackTrace = stackTrace;
+ this.offset = offset;
+ this.length = length;
+ this.comment = comment;
+ }
+
+ /**
+ * Get the offset for the stack trace
+ *
+ * @return Returns the offset.
+ */
+ public int getOffset() {
+ return offset;
+ }
+
+ /**
+ * Get the stack trace for the bug
+ *
+ * @return Returns the stackTrace.
+ */
+ public String getStackTrace() {
+ return stackTrace;
+ }
+
+ /**
+ * Get the length of the bug
+ *
+ * @return Returns the length.
+ */
+ public int getLength() {
+ return length;
+ }
+
+ /**
+ * Get the Comment that this stack trace came from
+ *
+ * @return Returns the Comment if it was a comment else a String if it was the description
+ */
+ public Object getComment() {
+ return comment;
+ }
+
+ /**
+ * Find a standard java stack trace in the given string
+ *
+ *
+ * @param s
+ * The string to search for stack traces
+ * @param comment
+ * The comment that the text came from.<br>
+ * Comment if a comment else a String
+ * @return String[] of stack traces - each element is 1 trace
+ */
+ public static StackTrace[] getStackTrace(String s, Object comment) {
+
+ // setup the regex used to determine if it looks like we are at a
+ // stack trace and whether it is something that should be skipped
+ String regexExceptionType = "^(.*\\.)+.+(Exception|Error|Throwable).*";
+ String regexSkip = ".*\\.\\..*";
+
+ // get all of the individual lines for the string
+ String[] lines = s.split("\r\n");
+
+ // the character start of the current stack trace
+ int charStackStart = 0;
+
+ // the current character in the string - used for the start and the
+ // offset
+ int[] charPos = { 0 }; // array so pass by reference
+
+ boolean inStackTrace = false;
+ List<String> stackTrace = null;
+ List<StackTrace> stackTraces = new ArrayList<StackTrace>();
+
+ // go through each of the lines of the string
+ for (int i = 0; i < lines.length; i++) {
+
+ if (lines[i].matches(regexSkip)){
+
+ // update the current character position
+ charPos[0] += lines[i].length() + 2;
+
+ }else if (lines[i].trim().matches(regexExceptionType) && !inStackTrace) {
+
+ // we have matched the stack trace and we are not already in one
+
+ // add the old stack trace to the list of stack traces
+ if (stackTrace != null && stackTrace.size() > 1) {
+ stackTraces.add(getStackTrace(stackTrace, charStackStart,
+ charPos[0] - charStackStart, comment));
+ }
+
+ // prepare for a new stack trace
+ stackTrace = new ArrayList<String>();
+ inStackTrace = true;
+
+ // the current line is the start of our stack trace
+ stackTrace.add(lines[i]);
+ charStackStart = charPos[0];
+ charPos[0] += lines[i].length() + 2;
+ } else if (inStackTrace) {
+ // we are in a stack trace
+
+ int[] pos = { i }; // array so pass by reference
+
+ // get the next at clause of the stack trace
+ String stack = getNextAt(lines, pos, charPos);
+
+ // check if there was an at
+ if (stack == null) {
+
+ // there wasn't so we are done this stack trace
+ inStackTrace = false;
+ if (stackTrace != null && stackTrace.size() > 1) {
+ stackTraces.add(getStackTrace(stackTrace,
+ charStackStart, charPos[0] - charStackStart, comment));
+ }
+ stackTrace = null;
+ } else {
+
+ // we had one, so add it to this stack trace
+ stackTrace.add(stack);
+ }
+
+ // update the position
+ i = pos[0];
+ } else {
+ // update the current character position
+ charPos[0] += lines[i].length() + 2;
+ }
+ }
+
+ // make sure to add the stack trace if it was the last in the string
+ if (stackTrace != null && stackTrace.size() > 1) {
+ stackTraces.add(getStackTrace(stackTrace, charStackStart,
+ charPos[0] - charStackStart, comment));
+ }
+
+ if (stackTraces.size() == 0)
+ return null;
+
+ // get the string values of the stack traces and return it
+ return getTracesFromList(stackTraces);
+ }
+
+ /**
+ * Get the next at clause from a potential stack trace -- looks ahead 4
+ * lines
+ *
+ * @param lines
+ * The array of all of the lines in the bug
+ * @param i
+ * The current position to start at
+ * @param charPos
+ * The current character position in the original string
+ * @return The next at clause, or <code>null</code><br>
+ * If an at line is matched, but the end isn't within the 4 lines,
+ * only the first line is returned. Also, charPos is updated as well
+ * as i
+ */
+ private static String getNextAt(String[] lines, int[] i, int[] charPos) {
+ String regexAtString = "^at.*";
+ String regexEndString = ".*:\\d+\\)$";
+ int index = i[0];
+ String l1, l2, l3, l4;
+ l1 = l2 = l3 = l4 = null;
+ String res = null;
+
+ // get the first line to look at
+ if (lines.length > index) {
+ l1 = lines[index];
+ } else {
+ // if the first line doesn't exist, we are done and should
+ // return
+ return null;
+ }
+
+ // get the next 3 lines
+ if (lines.length > index + 1) {
+ l2 = lines[index + 1];
+ }
+ if (lines.length > index + 2) {
+ l3 = lines[index + 2];
+ }
+ if (lines.length > index + 3) {
+ l4 = lines[index + 3];
+ }
+
+ // make sure that the first line is the start of an at
+ // if not, return null
+ if (l1.trim().matches(regexAtString)) {
+ charPos[0] += l1.length() + 2;
+ res = l1;
+ } else
+ return null;
+
+ // now determine where the end is if it wasn't on 1 line
+ if (!res.trim().matches(regexEndString)) {
+
+ if (l2 != null && l2.trim().matches(regexEndString)) {
+
+ // it was on the second line
+ // update the current position and the result string
+ i[0] = index + 1;
+ charPos[0] += l2.length() + 2;
+ res += l2.trim();
+ } else if (l3 != null && l3.trim().matches(regexEndString)) {
+
+ // it was on the third line
+ // update the current position and the result string
+ i[0] = index + 2;
+ charPos[0] += l2.length() + l3.length() + 4;
+ res += l2.trim();
+ res += l3.trim();
+ } else if (l4 != null && l4.trim().matches(regexEndString)) {
+
+ // it was on the fourth line
+ // update the current position and the result string
+ i[0] = index + 3;
+ charPos[0] += l2.length() + l3.length() + l4.length() + 6;
+ res += l2.trim();
+ res += l3.trim();
+ res += l4.trim();
+ }
+ }
+
+ // return the result
+ return res;
+ }
+
+ /**
+ * Get the StackTrace
+ *
+ * @param l
+ * the list of lines that contain the trace
+ * @param start
+ * the start of the stack trace
+ * @param offset
+ * the offset of the stack trace
+ * @param comment
+ * The comment that the stack trace came from
+ * @return The StackTrace for the given data
+ */
+ private static StackTrace getStackTrace(List<String> l, int offset, int length, Object comment) {
+ String s = "";
+ for(String s2 : l) {
+ s += s2 + "\r\n";
+ }
+
+ return new StackTrace(s, offset, length, comment);
+ }
+
+ /**
+ * Convert a List StackTraces to a StackTrace[] <br>
+ *
+ * @param l
+ * The List of StackTraces
+ * @return StackTrace[] of the List
+ */
+ private static StackTrace[] getTracesFromList(List<StackTrace> l) {
+
+ // make sure that there is something to convert, else return null
+ if (l == null || l.size() == 0)
+ return null;
+
+ // convert the list of strings to an array of strings
+ int i = 0;
+ StackTrace[] s = new StackTrace[l.size()];
+
+ for(StackTrace st : l) {
+ s[i] = st;
+ i++;
+ }
+
+ // return the string array
+ return s;
+ }
+
+ /**
+ * Escape all of the special regex characters from the string
+ *
+ * @param s
+ * The string to escape the characters for
+ * @return A string with all of the special characters escaped <br>
+ * <code>
+ * . => \.<br>
+ * $ => \$<br>
+ * ? => \?<br>
+ * { => \{<br>
+ * } => \}<br>
+ * ( => \(<br>
+ * ) => \)<br>
+ * [ => \[<br>
+ * ] => \]<br>
+ * + => \+<br>
+ * * => \*<br>
+ * | => \|<br>
+ * ^ => \^<br>
+ * \ => \\<br>
+ * / => \/<br>
+ * </code>
+ */
+ public static String escapeForRegex(String s) {
+ String sFixed = s;
+
+ // replace all special regex characters
+ sFixed = sFixed.replaceAll("\\\\", "\\\\\\\\");
+ sFixed = sFixed.replaceAll("\\$", "\\\\\\$");
+ sFixed = sFixed.replaceAll("\\.", "\\\\.");
+ sFixed = sFixed.replaceAll("\\?", "\\\\?");
+ sFixed = sFixed.replaceAll("\\{", "\\\\{");
+ sFixed = sFixed.replaceAll("\\}", "\\\\}");
+ sFixed = sFixed.replaceAll("\\(", "\\\\(");
+ sFixed = sFixed.replaceAll("\\)", "\\\\)");
+ sFixed = sFixed.replaceAll("\\[", "\\\\[");
+ sFixed = sFixed.replaceAll("\\]", "\\\\]");
+ sFixed = sFixed.replaceAll("\\+", "\\\\+");
+ sFixed = sFixed.replaceAll("\\*", "\\\\*");
+ sFixed = sFixed.replaceAll("\\|", "\\\\|");
+ sFixed = sFixed.replaceAll("\\^", "\\\\^");
+ sFixed = sFixed.replaceAll("\\/", "\\\\/");
+
+ return sFixed;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/Util.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/Util.java
new file mode 100644
index 000000000..817ee6d36
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/Util.java
@@ -0,0 +1,226 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2005 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
+ *******************************************************************************/
+/*
+ * Created on Nov 19, 2004
+ */
+package org.eclipse.mylar.tasks.bugzilla;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IMember;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.mylar.bugzilla.BugzillaPlugin;
+import org.eclipse.mylar.bugzilla.BugzillaPreferences;
+import org.eclipse.mylar.bugzilla.IBugzillaConstants;
+import org.eclipse.mylar.core.model.InterestComparator;
+import org.eclipse.mylar.tasks.bugzilla.search.BugzillaMylarSearchOperation;
+
+
+/**
+ * Utilities methods for the BugzillaMylarBridge
+ *
+ * @author sminto
+ */
+public class Util {
+
+ /**
+ * List of all of the search operations that can be done <br> all words, any words, regex
+ */
+ private static final String[] patternOperationValues = { "allwordssubstr", "anywordssubstr", "regexp" };
+ /**
+ * Sugzilla preferences so that we can get the search params
+ */
+ private static IPreferenceStore prefs = BugzillaPlugin.getDefault().getPreferenceStore();
+ /**
+ * List of all of the resolutions that we can have <br> FIXED, INVALID, WONTFIX, LATER, REMIND, DUPLICATE, WORKSFORME, MOVED, ---
+ */
+ private static String[] resolutionValues = BugzillaPreferences.queryOptionsToArray(prefs.getString(IBugzillaConstants.RESOLUTION_VALUES));
+ /**
+ * List of all of the statuses that we can have <br> UNCONFIRMED, NEW, ASSIGNED, REOPENED, RESOLVED, VERIFIED, CLOSED
+ */
+ private static String[] statusValues = BugzillaPreferences.queryOptionsToArray(prefs.getString(IBugzillaConstants.STATUS_VALUES));
+
+ /**
+ * Get only the landmarks that are IMember and sort them according to their
+ * DOI value (highest to lowest)
+ *
+ * @param landmarks
+ * The landmarks to check
+ * @return List of IMember landmarks sorted by DOI value
+ */
+ public static List<IMember> getMemberLandmarks(List<IJavaElement> landmarks) {
+ List<IMember> memberLandmarks = new ArrayList<IMember>();
+
+ for(IJavaElement je : landmarks) {
+
+ // keep only the IMember landmarks
+ if (je instanceof IMember) {
+ memberLandmarks.add((IMember)je);
+ }
+ }
+
+ // sort the landmarks
+ Collections.sort(memberLandmarks, new InterestComparator<IMember>());
+
+ return memberLandmarks;
+ }
+
+ /**
+ * Get the bugzilla url used for searching for exact matches
+ *
+ * @param je
+ * The IMember to create the query string for
+ * @return A url string for the search
+ */
+ public static String getExactSearchURL(IMember je) {
+ StringBuffer sb = getQueryURLStart();
+
+ String long_desc = "";
+
+ // get the fully qualified name of the element
+ long_desc += BugzillaMylarSearchOperation.getFullyQualifiedName(je);
+
+ try{
+ // encode the string to be used as a url
+ sb.append(URLEncoder.encode(long_desc, Charset.defaultCharset().toString()));
+ } catch (UnsupportedEncodingException e)
+ {
+ // should never get here since we are using the default encoding
+ }
+ sb.append(getQueryURLEnd());
+
+ return sb.toString();
+ }
+
+ /**
+ * Get the bugzilla url used for searching for inexact matches
+ *
+ * @param je
+ * The IMember to create the query string for
+ * @return A url string for the search
+ */
+ public static String getInexactSearchURL(IMember je) {
+ StringBuffer sb = getQueryURLStart();
+
+
+ String long_desc = "";
+
+ // add the member, qualified with just its parents name
+ if (!(je instanceof IType))
+ long_desc += je.getParent().getElementName()+".";
+ long_desc += je.getElementName();
+
+ try{
+ // encode the string to be used as a url
+ sb.append(URLEncoder.encode(long_desc, Charset.defaultCharset().toString()));
+ } catch (UnsupportedEncodingException e)
+ {
+ // should never get here since we are using the default encoding
+ }
+ sb.append(getQueryURLEnd());
+
+ return sb.toString();
+ }
+
+ /**
+ * Create the end of the bugzilla query URL with all of the status' and resolutions that we want
+ * @return StringBuffer with the end of the query URL in it
+ */
+ public static StringBuffer getQueryURLEnd(){
+
+ StringBuffer sb = new StringBuffer();
+
+ // add the status and resolutions that we care about
+ sb.append("&bug_status=" + statusValues[0]); // UNCONFIRMED
+ sb.append("&bug_status=" + statusValues[1]); // NEW
+ sb.append("&bug_status=" + statusValues[2]); // ASSIGNED
+ sb.append("&bug_status=" + statusValues[3]); // REOPENED
+ sb.append("&bug_status=" + statusValues[4]); // RESOLVED
+ sb.append("&bug_status=" + statusValues[5]); // VERIFIED
+ sb.append("&bug_status=" + statusValues[6]); // CLOSED
+
+ sb.append("&resolution=" + resolutionValues[0]); // FIXED
+ sb.append("&resolution=" + resolutionValues[3]); // LATER
+ sb.append("&resolution=" + "---"); // ---
+ return sb;
+ }
+
+ /**
+ * Create the bugzilla query URL start.
+ *
+ * @return The start of the query url as a StringBuffer <br>
+ * Example: https://bugs.eclipse.org/bugs/buglist.cgi?long_desc_type=allwordssubstr&long_desc=
+ */
+ public static StringBuffer getQueryURLStart() {
+ StringBuffer sb = new StringBuffer(BugzillaPlugin.getDefault()
+ .getServerName());
+
+ if (sb.charAt(sb.length() - 1) != '/') {
+ sb.append('/');
+ }
+ sb.append("buglist.cgi?");
+
+ // use the username and password if we have it
+ if (BugzillaPreferences.getUserName() != null
+ && !BugzillaPreferences.getUserName().equals("")
+ && BugzillaPreferences.getPassword() != null
+ && !BugzillaPreferences.getPassword().equals("")) {
+ try{
+ sb.append("GoAheadAndLogIn=1&Bugzilla_login="
+ + URLEncoder.encode(BugzillaPreferences.getUserName(), Charset.defaultCharset().toString())
+ + "&Bugzilla_password="
+ + URLEncoder.encode(BugzillaPreferences.getPassword(), Charset.defaultCharset().toString())
+ + "&");
+ } catch (UnsupportedEncodingException e)
+ {
+ // should never get here since we are using the default encoding
+ }
+ }
+
+ // add the description search type
+ sb.append("long_desc_type=");
+ sb.append(patternOperationValues[0]); // search for all words
+ sb.append("&long_desc=");
+
+ return sb;
+ }
+
+ /**
+ * Search the given string for another string
+ * @param elementName The name of the element that we are looking for
+ * @param comment The text to search for this element name
+ * @return <code>true</code> if the element is found in the text else <code>false</code>
+ */
+ public static boolean hasElementName(String elementName, String comment) {
+
+ // setup a regex for the element name
+ String regexElement = ".*"+elementName+".*";
+
+ // get all of the individual lines for the string
+ String[] lines = comment.split("\n");
+
+ // go through each of the lines of the string
+ for (int i = 0; i < lines.length; i++) {
+
+ if (lines[i].matches(regexElement)){
+ return true;
+ }
+ }
+ return false;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/search/BugzillaMylarSearch.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/search/BugzillaMylarSearch.java
new file mode 100644
index 000000000..f8c655d55
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/search/BugzillaMylarSearch.java
@@ -0,0 +1,149 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2005 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
+ *******************************************************************************/
+/*
+ * Created on Oct 13, 2004
+ */
+package org.eclipse.mylar.tasks.bugzilla.search;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IMember;
+import org.eclipse.mylar.core.search.IActiveSearchListener;
+import org.eclipse.mylar.core.search.IMylarSearchOperation;
+import org.eclipse.mylar.tasks.MylarTasksPlugin;
+import org.eclipse.mylar.tasks.bugzilla.BugzillaMylarBridge;
+import org.eclipse.mylar.tasks.bugzilla.BugzillaReportNode;
+import org.eclipse.mylar.tasks.bugzilla.Util;
+
+
+/**
+ * Used to facilitate bugzilla searches based on IJavaElements
+ *
+ * @author sminto
+ */
+public class BugzillaMylarSearch implements IMylarSearchOperation {
+
+ // scope identifiers
+ public static final int LOCAL_QUAL = 1; // local implies a bugzilla task, not just an offline report
+ public static final int LOCAL_UNQUAL = 2;
+ public static final int FULLY_QUAL = 3;
+ public static final int UNQUAL = 4;
+
+ private int scope;
+
+ private IJavaElement element;
+
+ private String handle = "";
+
+ /**
+ * Constructor
+ * @param scope The scope of this search
+ */
+ public BugzillaMylarSearch(int scope, IJavaElement element) {
+ this.scope = scope;
+ this.element = element;
+ }
+
+ public IStatus run(IProgressMonitor monitor) {
+ handle = element.getHandleIdentifier() + " " + scope;
+ List<IJavaElement> landmarks = new ArrayList<IJavaElement>();
+ landmarks.add(element);
+
+ if (!BugzillaMylarBridge.doesJobExist(handle)) {
+
+ // perform the bugzilla search
+ // get only the useful landmarks (IMember)
+ List<IMember> members = Util.getMemberLandmarks(landmarks);
+
+ // go through all of the landmarks that we are given and perform a
+ // search on them
+ for(IMember m : members){
+
+ // FIXME: decide whether to do leave the caching in for now or not
+ // check if we have the info cached
+ List<BugzillaReportNode> landmarkDoi = MylarTasksPlugin.getBridge()
+ .getFromLandmarksHash(m, scope);
+
+ if (landmarkDoi != null) {
+ //TODO decide when to queue up and do a refresh search
+ notifySearchCompleted(landmarkDoi);
+ continue;
+ }
+
+ // create a search operation so that we can search
+ BugzillaMylarSearchOperation op = new BugzillaMylarSearchOperation(
+ this, m, scope);
+
+ // create a new search job so that it can be scheduled and
+ // run as a background thread
+ Job searchJob = new BugzillaMylarSearchJob(
+ "Querying Bugzilla Server - Mylar - "
+ + op.getSearchMemberName(), op);
+
+ // schedule the new search job
+ searchJob.schedule();
+
+ // save this searchJobs handle so that we can cancel it if need be
+ BugzillaMylarBridge.addJob(handle, searchJob);
+ }
+ }
+ return Status.OK_STATUS;
+ }
+
+ /** List of listeners wanting to know about the searches */
+ private List<IActiveSearchListener> listeners = new ArrayList<IActiveSearchListener>();
+
+ /**
+ * Add a listener for when the bugzilla search is completed
+ *
+ * @param l
+ * The listener to add
+ */
+ public void addListener(IActiveSearchListener l) {
+ // add the listener to the list
+ listeners.add(l);
+ }
+
+ /**
+ * Remove a listener for when the bugzilla search is completed
+ *
+ * @param l
+ * The listener to remove
+ */
+ public void removeListener(IActiveSearchListener l) {
+ // remove the listener from the list
+ listeners.remove(l);
+ }
+
+ /**
+ * Notify all of the listeners that the bugzilla search is completed
+ *
+ * @param doiList
+ * A list of BugzillaSearchHitDoiInfo
+ * @param member
+ * The IMember that the search was performed on
+ */
+ public void notifySearchCompleted(List<BugzillaReportNode> doiList) {
+ // go through all of the listeners and call searchCompleted(colelctor,
+ // member)
+ BugzillaMylarBridge.removeSearchJob(handle);
+ for (IActiveSearchListener listener : listeners) {
+ listener.searchCompleted(doiList);
+ }
+ }
+
+} \ No newline at end of file
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/search/BugzillaMylarSearchJob.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/search/BugzillaMylarSearchJob.java
new file mode 100644
index 000000000..499a39e4e
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/search/BugzillaMylarSearchJob.java
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2005 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
+ *******************************************************************************/
+/*
+ * Created on Oct 6, 2004
+ */
+package org.eclipse.mylar.tasks.bugzilla.search;
+
+import javax.security.auth.login.LoginException;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.mylar.bugzilla.BugzillaPlugin;
+import org.eclipse.mylar.bugzilla.IBugzillaConstants;
+import org.eclipse.mylar.tasks.bugzilla.BugzillaMylarBridge;
+import org.eclipse.ui.PlatformUI;
+
+
+/**
+ * The bugzilla search job used to search a bugzilla site
+ *
+ * @author sminto
+ */
+public class BugzillaMylarSearchJob extends Job {
+
+ /** The search operation used to perform the query */
+ private BugzillaMylarSearchOperation operation;
+
+ /**
+ * Constructor
+ *
+ * @param name
+ * Job name
+ * @param operation
+ * The operation to perform the search query
+ */
+ public BugzillaMylarSearchJob(String name,
+ BugzillaMylarSearchOperation operation) {
+ super(name);
+ this.operation = operation;
+ }
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ // create a new status
+ final IStatus[] status = new IStatus[1];
+
+ try {
+ // execute the search operation
+ operation.execute(monitor);
+
+ // get the status of the search operation
+ status[0] = operation.getStatus();
+
+ // determine if there was an error, if it was cancelled, or if it is
+ // ok
+ if ( status[0].getCode() == IStatus.CANCEL) {
+ // it was cancelled, so just return
+ status[0] = Status.OK_STATUS;
+
+ // make sure that we know this job is not running anymore
+ BugzillaMylarBridge.removeSearchJob(operation.getSearchMember().getHandleIdentifier()+" "+operation.getScope());//runningJobs.remove(operation.getSearchMember());
+ return status[0];
+ } else if (!status[0].isOK()) {
+ // there was an error, so display an error message
+ PlatformUI.getWorkbench().getDisplay().asyncExec(
+ new Runnable() {
+ public void run() {
+ ErrorDialog.openError(null,
+ "Bugzilla Search Error", null,
+ status[0]);
+ }
+ });
+ status[0] = Status.OK_STATUS;
+
+ // make sure we know that this job is not running anymore
+ BugzillaMylarBridge.removeSearchJob(operation.getSearchMember().getHandleIdentifier()+" "+operation.getScope());//runningJobs.remove(operation.getSearchMember());
+ return status[0];
+ }
+ } catch (LoginException e) {
+ // we had a problem while searching that seems like a login info
+ // problem
+ // thrown in BugzillaSearchOperation
+ MessageDialog
+ .openError(
+ null,
+ "Login Error",
+ "Bugzilla could not log you in to get the information you requested since login name or password is incorrect.\nPlease check your settings in the bugzilla preferences. ");
+ BugzillaPlugin.log(new Status(IStatus.ERROR,
+ IBugzillaConstants.PLUGIN_ID, IStatus.OK, "", e));
+ } finally {
+ // make sure that we know that this job is not running anymore
+ BugzillaMylarBridge.removeSearchJob(operation.getSearchMember().getHandleIdentifier()+" "+operation.getScope());//.runningJobs.remove(operation.getSearchMember());
+ }
+
+ return status[0];
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/search/BugzillaMylarSearchOperation.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/search/BugzillaMylarSearchOperation.java
new file mode 100644
index 000000000..ea109091d
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/search/BugzillaMylarSearchOperation.java
@@ -0,0 +1,526 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2005 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
+ *******************************************************************************/
+/*
+ * Created on Oct 14, 2004
+ */
+package org.eclipse.mylar.tasks.bugzilla.search;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.security.auth.login.LoginException;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IMember;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.mylar.bugzilla.core.BugReport;
+import org.eclipse.mylar.bugzilla.core.Comment;
+import org.eclipse.mylar.bugzilla.search.BugzillaSearchEngine;
+import org.eclipse.mylar.bugzilla.search.BugzillaSearchHit;
+import org.eclipse.mylar.bugzilla.search.BugzillaSearchQuery;
+import org.eclipse.mylar.bugzilla.search.IBugzillaSearchOperation;
+import org.eclipse.mylar.core.MylarPlugin;
+import org.eclipse.mylar.tasks.BugzillaTask;
+import org.eclipse.mylar.tasks.ITask;
+import org.eclipse.mylar.tasks.MylarTasksPlugin;
+import org.eclipse.mylar.tasks.bugzilla.BugzillaReportNode;
+import org.eclipse.mylar.tasks.bugzilla.StackTrace;
+import org.eclipse.mylar.tasks.bugzilla.Util;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+
+
+/**
+ * Bugzilla search operation for Mylar
+ *
+ * @author sminto
+ */
+public class BugzillaMylarSearchOperation extends WorkspaceModifyOperation
+ implements IBugzillaSearchOperation {
+ /** The IMember we are doing the search for */
+ private IMember javaElement;
+
+ /** The bugzilla collector for the search */
+ private BugzillaResultCollector collector = null;
+
+ /** The status of the search operation */
+ private IStatus status;
+
+ /** The LoginException that was thrown when trying to do the search */
+ private LoginException loginException = null;
+
+ /** The fully qualified name of the member we are searching for */
+ private String name;
+
+ /** The bugzilla search query */
+ private BugzillaSearchQuery query;
+
+ private BugzillaMylarSearch search;
+
+ private int scope;
+
+ /**
+ * Constructor
+ *
+ * @param m
+ * The member that we are doing the search for
+ */
+ public BugzillaMylarSearchOperation(BugzillaMylarSearch search, IMember m, int scope) {
+ this.javaElement = m;
+ this.search = search;
+ this.scope = scope;
+ name = getFullyQualifiedName(m);
+ }
+
+ /**
+ * Get the fully qualified name of a IMember
+ * TODO: move to a more central location so that others can use this, but don't want to add unecessary coupling
+ *
+ * @return String representing the fully qualified name
+ */
+ public static String getFullyQualifiedName(IJavaElement je) {
+ if(!(je instanceof IMember)) return null;
+
+ IMember m = (IMember)je;
+ if (m.getDeclaringType() == null)
+ return ((IType) m).getFullyQualifiedName();
+ else
+ return m.getDeclaringType().getFullyQualifiedName() + "."
+ + m.getElementName();
+ }
+
+ @Override
+ public void execute(IProgressMonitor monitor) {
+
+ BugzillaResultCollector searchCollector = null;
+
+ if(scope == BugzillaMylarSearch.FULLY_QUAL){
+ searchCollector = searchQualified(monitor);
+ }else if(scope == BugzillaMylarSearch.UNQUAL){
+ searchCollector = searchUnqualified(monitor);
+ }else if(scope == BugzillaMylarSearch.LOCAL_QUAL){
+ searchCollector = searchLocalQual(monitor);
+ }else if(scope == BugzillaMylarSearch.LOCAL_UNQUAL){
+ searchCollector = searchLocalUnQual(monitor);
+ }else{
+ return;
+ }
+
+ if(searchCollector == null){
+ search.notifySearchCompleted(
+ new ArrayList<BugzillaReportNode>());
+ return;
+ }
+
+ List<BugzillaSearchHit> l = searchCollector.getResults();
+
+ // get the list of doi elements
+ List<BugzillaReportNode> doiList = getDoiList(l);
+
+ // we completed the search, so notify all of the listeners
+ // that the search has been completed
+ MylarTasksPlugin.getBridge()
+ .addToLandmarksHash(doiList, javaElement, scope);
+ search.notifySearchCompleted(
+ doiList);
+ // MIK: commmented out logging
+// MonitorPlugin.log(this, "There were " + doiList.size() + " items found");
+ }
+
+ /**
+ * Search the local bugs for the member using the qualified name
+ * @param monitor The progress monitor to search with
+ * @return The BugzillaResultCollector with the results of the search
+ */
+ private BugzillaResultCollector searchLocalQual(IProgressMonitor monitor) {
+
+ //get the fully qualified name for searching
+ String elementName = getFullyQualifiedName(javaElement);
+
+ // setup the search result collector
+ collector = new BugzillaResultCollector();
+ collector.setOperation(this);
+ collector.setProgressMonitor(monitor);
+
+ // get all of the root tasks and start the search
+ List<ITask> tasks = MylarTasksPlugin.getTaskListManager().getTaskList().getRootTasks();
+ searchLocal(tasks, collector, elementName, monitor);
+
+ // return the collector
+ return collector;
+ }
+
+ /**
+ * Search the local bugs for the member using the unqualified name
+ * @param monitor The progress monitor to search with
+ * @return The BugzillaResultCollector with the results of the search
+ */
+ private BugzillaResultCollector searchLocalUnQual(IProgressMonitor monitor) {
+
+ // get the element name for searching
+ String elementName = javaElement.getElementName();
+
+ // setup the search result collector
+ collector = new BugzillaResultCollector();
+ collector.setOperation(this);
+ collector.setProgressMonitor(monitor);
+
+ // get all of the root tasks and start the search
+ List<ITask> tasks = MylarTasksPlugin.getTaskListManager().getTaskList().getRootTasks();
+ searchLocal(tasks, collector, elementName, monitor);
+
+ // return the collector
+ return collector;
+ }
+
+ /**
+ * Search the local bugs for the member
+ * @param tasks The tasks to search
+ * @param searchCollector The collector to add the results to
+ * @param elementName The name of the element that we are looking for
+ * @param monitor The progress monitor
+ */
+ private void searchLocal(List<ITask> tasks, BugzillaResultCollector searchCollector, String elementName, IProgressMonitor monitor) {
+ if(tasks == null) return;
+
+ // go through all of the tasks
+ for(ITask task : tasks){
+ monitor.worked(1);
+
+ // check what kind of task it is
+ if(task instanceof BugzillaTask){
+
+ // we have a bugzilla task, so get the bug report
+ BugzillaTask bugTask = (BugzillaTask)task;
+ BugReport bug = bugTask.getBugReport();
+
+ // parse the bug report for the element that we are searching for
+ boolean isHit = search(elementName, bug);
+
+ // determine if we have a hit or not
+ if(isHit){
+
+ // make a search hit from the bug and then add it to the collector
+ BugzillaSearchHit hit = new BugzillaSearchHit(bug.getId(), bug.getDescription(), "","","","","","","");
+ try{
+ searchCollector.accept(hit);
+ }catch(CoreException e){
+ MylarPlugin.log(this.getClass().toString(), e);
+ }
+ }
+ }
+
+ // get the children and perform the search on them as well
+ List<ITask> children = task.getChildren();
+ if(children != null){
+ searchLocal(children, searchCollector, elementName, monitor);
+ }
+ }
+ status = Status.OK_STATUS;
+ }
+
+ /**
+ * Search the bug for the given element name
+ * @param elementName The name of the element to search for
+ * @param bug The bug to search in
+ */
+ private boolean search(String elementName, BugReport bug) {
+
+ if (bug == null) return false; // MIK: added null check here
+ String description = bug.getDescription();
+ String summary = bug.getSummary();
+ List<Comment> comments = bug.getComments();
+
+ // search the description and the summary
+ if(Util.hasElementName(elementName, summary))
+ return true;
+
+ if(Util.hasElementName(elementName, description))
+ return true;
+
+ Iterator<Comment> comItr = comments.iterator();
+ while (comItr.hasNext()) {
+ Comment comment = comItr.next();
+ String commentText = comment.getText();
+ // search the text for a reference to the element
+ if(Util.hasElementName(elementName, commentText))
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Perform the actual search on the Bugzilla server
+ * @param url The url to use for the search
+ * @param searchCollector The collector to put the search results into
+ * @param monitor The progress monitor to use for the search
+ * @return The BugzillaResultCollector with the search results
+ */
+ private BugzillaResultCollector search(String url, BugzillaResultCollector searchCollector, IProgressMonitor monitor){
+
+ // set the initial number of matches to 0
+ int matches = 0;
+ // setup the progress monitor and start the search
+ searchCollector.setProgressMonitor(monitor);
+ BugzillaSearchEngine engine = new BugzillaSearchEngine(url);
+ try {
+
+ // perform the search
+ status = engine.search(searchCollector, matches);
+
+ // check the status so that we don't keep searching if there
+ // is a problem
+ if (status.getCode() == IStatus.CANCEL) {
+ MylarPlugin.log(this, "SEARCH CANCELLED");
+ return null;
+ } else if (!status.isOK()) {
+ MylarPlugin.log(this, "SEARCH ERROR");
+ MylarPlugin.log(status);
+ return null;
+ }
+ return searchCollector;
+ } catch (LoginException e) {
+ //save this exception to throw later
+ this.loginException = e;
+ }
+ return null;
+ }
+
+ /**
+ * Perform a search for qualified instances of the member
+ * @param monitor The progress monitor to use
+ * @return The BugzillaResultCollector with the search results
+ */
+ private BugzillaResultCollector searchQualified(IProgressMonitor monitor)
+ {
+ // create a new collector for the results
+ collector = new BugzillaResultCollector();
+ collector.setOperation(this);
+ collector.setProgressMonitor(monitor);
+
+ // get the search url
+ String url = Util.getExactSearchURL(javaElement);
+
+ // log the url that we are searching with
+ // MIK: commmented out logging
+// MonitorPlugin.log(this, url);
+
+ return search(url, collector, monitor);
+ }
+
+ /**
+ * Perform a search for unqualified instances of the member
+ * @param monitor The progress monitor to use
+ * @return The BugzillaResultCollector with the search results
+ */
+ private BugzillaResultCollector searchUnqualified(IProgressMonitor monitor)
+ {
+ // create a new collector for the results
+ collector = new BugzillaResultCollector();
+ collector.setOperation(this);
+ collector.setProgressMonitor(monitor);
+
+ // get the search url
+ String url = Util.getInexactSearchURL(javaElement);
+
+ // log the url that we are searching with
+ // MIK: commmented out logging
+// MonitorPlugin.log(this, url);
+
+ return search(url, collector, monitor);
+ }
+
+// /**
+// * Remove all of the duplicates
+// * @param compare The List of BugzillaSearchHits to compare with
+// * @param base The List of BugzillaSearchHits to remove the duplicates from
+// */
+// private void removeDuplicates(List<BugzillaSearchHit> compare, List<BugzillaSearchHit> base){
+//
+// for(BugzillaSearchHit h1 : compare){
+// Iterator itr2 = base.iterator();
+// while(itr2.hasNext()){
+// BugzillaSearchHit h2 = (BugzillaSearchHit)itr2.next();
+// if(h2.getId() == h1.getId()){
+// // we found a duplicate so remove it
+// itr2.remove();
+// break;
+// }
+// }
+// }
+// }
+//
+ /**
+ * Perform a second pass parse to determine if there are any stack traces in
+ * the bug - currently only used for the exact search results
+ *
+ * @param doiList -
+ * the list of BugzillaSearchHitDOI elements to parse
+ */
+ public static void secondPassBugzillaParser(List<BugzillaReportNode> doiList) {
+
+ // go through each of the items in the doiList
+ for(BugzillaReportNode info : doiList) {
+
+ // get the bug report so that we have all of the data
+ // - descriptions, comments, etc
+ BugReport b = null;
+ try{
+ b = info.getBug();
+ }catch(Exception e){
+ // don't care since null will be caught
+ }
+
+ // if the report could not be downloaded, try the next one
+ if (b == null)
+ continue;
+
+ // see if the description has a stack trace in it
+ StackTrace[] stackTrace = StackTrace.getStackTrace(b.getDescription(), b.getDescription());
+ if (stackTrace != null) {
+
+ // add the stack trace to the doi info
+ info.setExact(true);
+ info.addStackTraces(stackTrace);
+ }
+
+ // go through all of the comments for the bug
+ Iterator<Comment> comItr = b.getComments().iterator();
+ while (comItr.hasNext()) {
+ Comment comment = comItr.next();
+ String commentText = comment.getText();
+
+ // see if the comment has a stack trace in it
+ stackTrace = StackTrace.getStackTrace(commentText, comment);
+ if (stackTrace != null) {
+
+ // add the stack trace to the doi info
+ info.setExact(true);
+ info.addStackTraces(stackTrace);
+ }
+ }
+ }
+ }
+
+ /**
+ * Add the results returned to the Hash of landmarks
+ *
+ * @param results
+ * The list of results
+ * @param isExact
+ * whether the search was exact or not
+ */
+ private List<BugzillaReportNode> getDoiList(List<BugzillaSearchHit> results) {
+ List<BugzillaReportNode> doiList = new ArrayList<BugzillaReportNode>();
+
+ boolean isExact = (scope==BugzillaMylarSearch.FULLY_QUAL || scope==BugzillaMylarSearch.LOCAL_QUAL)?true:false;
+
+ BugzillaReportNode info = null;
+ // go through all of the results and create a DoiInfo list
+ for(BugzillaSearchHit hit : results){
+
+ try {
+ float value = 0;
+ info = new BugzillaReportNode(
+ value, hit, isExact);
+
+ // only download the bug for the exact matches
+ //downloading bugs kills the time - can we do this elsewhere? - different thread? persistant?
+// if(isExact){
+// // get the bug report for the doi info item
+// BugReport b = BugzillaRepository.getInstance().getBug(
+// hit.getId());
+// // add the bug to the doi info for future use
+// info.setBug(b);
+// }
+
+ } catch (Exception e) {
+ MylarPlugin.log(this.getClass().toString(), e);
+ }
+ finally{
+ doiList.add(info);
+ }
+ }
+ return doiList;
+ }
+
+ /**
+ * @see org.eclipse.mylar.bugzilla.search.IBugzillaSearchOperation#getStatus()
+ */
+ public IStatus getStatus() throws LoginException {
+ // if a LoginException was thrown while trying to search, throw this
+ if (loginException == null)
+ return status;
+ else
+ throw loginException;
+ }
+
+ /**
+ * @see org.eclipse.mylar.bugzilla.search.IBugzillaSearchOperation#getImageDescriptor()
+ */
+ public ImageDescriptor getImageDescriptor() {
+ return null;
+ }
+
+ /**
+ * Get the member that we are performing the search for
+ *
+ * @return The member this search is being performed for
+ */
+ public IMember getSearchMember() {
+ return javaElement;
+ }
+
+ /**
+ * Get the name of the member that we are searching for
+ *
+ * @return The fully qualified name of the member
+ */
+ public String getSearchMemberName() {
+ return name;
+ }
+
+ /**
+ * @see org.eclipse.mylar.bugzilla.search.IBugzillaSearchOperation#getQuery()
+ */
+ public BugzillaSearchQuery getQuery() {
+ return query;
+ }
+
+ /**
+ * @see org.eclipse.mylar.bugzilla.search.IBugzillaSearchOperation#setQuery(org.eclipse.mylar.bugzilla.search.BugzillaSearchQuery)
+ */
+ public void setQuery(BugzillaSearchQuery newQuery) {
+ this.query = newQuery;
+ }
+
+ /**
+ * Get the name of the element that we are searching for
+ *
+ * @return The name of the element
+ */
+ public String getName(){
+ return name;
+ }
+
+ /**
+ * Get the scope of the search operation
+ * @return The scope - defined in BugzillaMylarSearch
+ */
+ public int getScope() {
+ return scope;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/search/BugzillaResultCollector.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/search/BugzillaResultCollector.java
new file mode 100644
index 000000000..22b975f27
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/search/BugzillaResultCollector.java
@@ -0,0 +1,182 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2005 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
+ *******************************************************************************/
+/*
+ * Created on Oct 4, 2004
+ */
+package org.eclipse.mylar.tasks.bugzilla.search;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.mylar.bugzilla.search.BugzillaSearchHit;
+import org.eclipse.mylar.bugzilla.search.IBugzillaSearchOperation;
+import org.eclipse.mylar.bugzilla.search.IBugzillaSearchResultCollector;
+
+
+/**
+ * Collector for the bugzilla search results
+ *
+ * @author sminto
+ */
+public class BugzillaResultCollector implements IBugzillaSearchResultCollector {
+ /** A list of all of the search results found */
+ private List<BugzillaSearchHit> results = new ArrayList<BugzillaSearchHit>();
+
+ /** The progress monitor for the search operation */
+ private IProgressMonitor monitor;
+
+ /** The number of matches found */
+ private int matchCount;
+
+ /** The bugzilla search operation */
+ private IBugzillaSearchOperation operation;
+
+ /** The string to display to the user while querying */
+ private static final String STARTING = "querying the server";
+
+ /** The string to display to the user when the query is done */
+ private static final String DONE = "done";
+
+ /** The string to display when there is one match from the search */
+ private static final String MATCH = "Bugzilla Mylar search - 1 match";
+
+ /** The string to display when there is more than one match from the search */
+ private static final String MATCHES = "Bugzilla Mylar search - {0} matches";
+
+ /**
+ * @see org.eclipse.mylar.bugzilla.search.IBugzillaSearchResultCollector#aboutToStart()
+ */
+ public void aboutToStart(int startMatchCount) throws CoreException {
+ // initiailize the number of matches
+ matchCount = startMatchCount;
+
+ // set the progress monitor to say that we are querying the server
+ monitor.setTaskName(STARTING);
+ }
+
+ /**
+ * @see org.eclipse.mylar.bugzilla.search.IBugzillaSearchResultCollector#accept(org.eclipse.mylar.bugzilla.search.BugzillaSearchHit)
+ */
+ public void accept(BugzillaSearchHit hit) throws CoreException {
+ // add the result to the list of results
+ results.add(hit);
+
+ // increment the match count
+ matchCount++;
+
+ if (getProgressMonitor() != null) {
+ if (!getProgressMonitor().isCanceled()) {
+ // if the operation is cancelled finish with whatever data was
+ // already found
+ getProgressMonitor().subTask(
+ getFormattedMatchesString(matchCount));
+ getProgressMonitor().worked(1);
+ }
+ }
+ }
+
+ /**
+ * @see org.eclipse.mylar.bugzilla.search.IBugzillaSearchResultCollector#done()
+ */
+ public void done() {
+ if (getProgressMonitor() != null) {
+ if (!monitor.isCanceled()) {
+ // if the operation is cancelled, finish with the data that we
+ // already have
+ String matchesString = getFormattedMatchesString(matchCount);
+ monitor.setTaskName(MessageFormat.format(DONE,
+ new Object[] { matchesString }));
+ }
+ }
+
+ monitor = null;
+ }
+
+ /**
+ * @see org.eclipse.mylar.bugzilla.search.IBugzillaSearchResultCollector#getProgressMonitor()
+ */
+ public IProgressMonitor getProgressMonitor() {
+ return monitor;
+ }
+
+ /**
+ * @see org.eclipse.mylar.bugzilla.search.IBugzillaSearchResultCollector#setProgressMonitor(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public void setProgressMonitor(IProgressMonitor monitor) {
+ this.monitor = monitor;
+ }
+
+ /**
+ * @see org.eclipse.mylar.bugzilla.search.IBugzillaSearchResultCollector#setOperation(org.eclipse.mylar.bugzilla.search.BugzillaSearchOperation)
+ */
+ public void setOperation(IBugzillaSearchOperation operation) {
+ this.operation = operation;
+ }
+
+ /**
+ * @see org.eclipse.mylar.bugzilla.search.IBugzillaSearchResultCollector#getOperation()
+ */
+ public IBugzillaSearchOperation getOperation() {
+ return operation;
+ }
+
+ /**
+ * Get the string specifying the number of matches found
+ *
+ * @param count
+ * The number of matches found
+ * @return The <code>String</code> specifying the number of matches found
+ */
+ private String getFormattedMatchesString(int count) {
+ // if only 1 match, return the singular match string
+ String name = "";
+ if(operation instanceof BugzillaMylarSearchOperation)
+ name = " - " + ((BugzillaMylarSearchOperation)operation).getName();
+ if (count == 1)
+ return MATCH + name;
+
+ // format the matches string and return it
+ Object[] messageFormatArgs = { new Integer(count) };
+ return MessageFormat.format(MATCHES + name,
+ messageFormatArgs);
+ }
+
+ /**
+ * Get the list of results
+ *
+ * @return A List of BugzillaSearchHit
+ */
+ public List<BugzillaSearchHit> getResults() {
+ return results;
+ }
+
+ /**
+ * Get the number of matches from the operation
+ *
+ * @return Returns the matchCount.
+ */
+ public int getMatchCount() {
+ return matchCount;
+ }
+
+ /**
+ * Set the starting number of matches for the search operation
+ *
+ * @param matchCount
+ * The matchCount to set.
+ */
+ public void setMatchCount(int matchCount) {
+ this.matchCount = matchCount;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/ui/BugzillaNodeLabelProvider.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/ui/BugzillaNodeLabelProvider.java
new file mode 100644
index 000000000..a60e03546
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/ui/BugzillaNodeLabelProvider.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2005 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
+ *******************************************************************************/
+/*
+ * Created on Apr 18, 2005
+ */
+package org.eclipse.mylar.tasks.bugzilla.ui;
+
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.ILabelProviderListener;
+import org.eclipse.mylar.core.model.ITaskscapeNode;
+import org.eclipse.mylar.tasks.MylarTasksPlugin;
+import org.eclipse.mylar.ui.MylarImages;
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * @author Mik Kersten
+ */
+public class BugzillaNodeLabelProvider implements ILabelProvider {
+
+ public Image getImage(Object element) {
+ return MylarImages.getImage(MylarImages.BUG);
+ }
+
+ /**
+ * TODO: slow?
+ */
+ public String getText(Object element) {
+ ITaskscapeNode node = (ITaskscapeNode)element;
+ String name = MylarTasksPlugin.getStructureBridge().getName(
+ MylarTasksPlugin.getStructureBridge().getObjectForHandle(node.getElementHandle())
+ );
+ return name;
+ }
+
+ public void addListener(ILabelProviderListener listener) {
+ // don't need to worry about listeners
+ }
+
+ public void dispose() {
+ // don't care about dispose
+ }
+
+ public boolean isLabelProperty(Object element, String property) {
+ return false;
+ }
+
+ public void removeListener(ILabelProviderListener listener) {
+ // don't need to worry about listeners
+ }
+}
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/ui/BugzillaUiBridge.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/ui/BugzillaUiBridge.java
new file mode 100644
index 000000000..acd09e20c
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/bugzilla/ui/BugzillaUiBridge.java
@@ -0,0 +1,141 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2005 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
+ *******************************************************************************/
+/*
+ * Created on Apr 6, 2005
+ */
+package org.eclipse.mylar.tasks.bugzilla.ui;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.mylar.bugzilla.ui.BugzillaOpenStructure;
+import org.eclipse.mylar.bugzilla.ui.ViewBugzillaAction;
+import org.eclipse.mylar.bugzilla.ui.editor.AbstractBugEditor;
+import org.eclipse.mylar.bugzilla.ui.outline.BugzillaOutlinePage;
+import org.eclipse.mylar.core.model.ITaskscapeNode;
+import org.eclipse.mylar.tasks.BugzillaTask;
+import org.eclipse.mylar.tasks.ITask;
+import org.eclipse.mylar.tasks.MylarTasksPlugin;
+import org.eclipse.mylar.tasks.bugzilla.BugzillaReferencesProvider;
+import org.eclipse.mylar.tasks.ui.BugzillaTaskEditor;
+import org.eclipse.mylar.ui.IMylarUiBridge;
+import org.eclipse.mylar.ui.MylarImages;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IEditorReference;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.internal.Workbench;
+
+public class BugzillaUiBridge implements IMylarUiBridge {
+
+ protected BugzillaNodeLabelProvider labelProvider = new BugzillaNodeLabelProvider();
+
+ public void open(ITaskscapeNode node) {
+ String handle = node.getElementHandle();
+ String bugHandle = handle;
+ String server =handle.substring(0, handle.indexOf(";"));
+
+ handle = handle.substring(handle.indexOf(";") + 1);
+ int next = handle.indexOf(";");
+
+ int bugId;
+ int commentNumer = -1;
+ if(next == -1){
+ bugId = Integer.parseInt(handle);
+ }
+ else{
+ bugId = Integer.parseInt(handle.substring(0, handle.indexOf(";")));
+ commentNumer = Integer.parseInt(handle.substring(handle.indexOf(";") + 1));
+ bugHandle = bugHandle.substring(0, next);
+ }
+
+ List<BugzillaOpenStructure> l = new ArrayList<BugzillaOpenStructure>(1);
+ l.add(new BugzillaOpenStructure(server, bugId, commentNumer));
+
+ ITask task= MylarTasksPlugin.getTaskListManager().getTaskList().getTaskForId(bugHandle);
+ if (task != null && task instanceof BugzillaTask) {
+ BugzillaTask bugzillaTask = (BugzillaTask)task;
+ bugzillaTask.openTask(commentNumer);
+ } else {
+ // open the bug in the editor
+ ViewBugzillaAction viewBugs = new ViewBugzillaAction("Display bugs in editor", l);
+ viewBugs.schedule();
+ }
+ }
+
+ public ILabelProvider getLabelProvider() {
+ return labelProvider;
+ }
+
+ public void close(ITaskscapeNode node) {
+ IWorkbenchPage page = Workbench.getInstance().getActiveWorkbenchWindow().getActivePage();
+ if (page != null) {
+ IEditorReference[] references = page.getEditorReferences();
+ for (int i = 0; i < references.length; i++) {
+ IEditorPart part = references[i].getEditor(false);
+ if (part != null) {
+ if (part instanceof AbstractBugEditor) {
+ ((AbstractBugEditor)part).close();
+ } else if(part instanceof BugzillaTaskEditor){
+ ((BugzillaTaskEditor)part).close();
+ }
+ }
+ }
+ }
+ }
+
+ public boolean acceptsEditor(IEditorPart editorPart) {
+ return editorPart instanceof AbstractBugEditor;
+ }
+
+ public List<TreeViewer> getTreeViewers(IEditorPart editor) {
+ ArrayList<TreeViewer> outlines = new ArrayList<TreeViewer>(1);
+ TreeViewer outline = getOutlineTreeViewer(editor);
+ if (outline != null) {
+ outlines.add(outline);
+ return outlines;
+ } else {
+ return Collections.emptyList();
+ }
+ }
+
+ protected TreeViewer getOutlineTreeViewer(IEditorPart editor) {
+ if(editor instanceof AbstractBugEditor){
+ AbstractBugEditor abe = (AbstractBugEditor)editor;
+ BugzillaOutlinePage outline = abe.getOutline();
+ if(outline != null) return outline.getOutlineTreeViewer();
+ }
+ return null;
+ }
+
+ public void refreshOutline(Object element, boolean updateLabels) {
+ IEditorPart editorPart = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor();
+ TreeViewer treeViewer = getOutlineTreeViewer(editorPart);
+ if (treeViewer != null) {
+ treeViewer.refresh(true);
+
+ treeViewer.expandAll();
+ }
+ }
+
+ public ImageDescriptor getIconForRelationship(String relationshipHandle) {
+ return MylarImages.RELATIONSHIPS_REFS_BUGZILLA;
+
+ }
+
+ public String getNameForRelationship(String relationshipHandle) {
+ return BugzillaReferencesProvider.NAME;
+ }
+}
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/BugzillaTaskEditor.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/BugzillaTaskEditor.java
new file mode 100644
index 000000000..89236b5a2
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/BugzillaTaskEditor.java
@@ -0,0 +1,274 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2005 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
+ *******************************************************************************/
+/*
+ * Created on 31-Jan-2005
+ */
+package org.eclipse.mylar.tasks.ui;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.mylar.bugzilla.core.BugReport;
+import org.eclipse.mylar.bugzilla.ui.editor.AbstractBugEditor;
+import org.eclipse.mylar.bugzilla.ui.editor.ExistingBugEditor;
+import org.eclipse.mylar.core.MylarPlugin;
+import org.eclipse.mylar.tasks.BugzillaTask;
+import org.eclipse.mylar.tasks.MylarTasksPlugin;
+import org.eclipse.mylar.ui.MylarImages;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IEditorSite;
+import org.eclipse.ui.IPartListener;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.internal.Workbench;
+import org.eclipse.ui.part.MultiPageEditorPart;
+import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
+
+/**
+ * @author ebooth
+ */
+public class BugzillaTaskEditor extends MultiPageEditorPart {
+
+ /** The task that created this editor */
+ protected BugzillaTask bugTask;
+
+ /** This bug report can be modified by the user and saved offline. */
+ protected BugReport offlineBug;
+
+ private ExistingBugEditor buzillaEditor;
+
+ private BugzillaTaskEditorInput bugzillaEditorInput;
+
+ private TaskSummaryEditor taskSummaryEditor = new TaskSummaryEditor();
+
+ protected IContentOutlinePage outlinePage = null;
+
+ public BugzillaTaskEditor() {
+ super();
+
+ // get the workbench page and add a listener so we can detect when it closes
+ IWorkbench wb = MylarTasksPlugin.getDefault().getWorkbench();
+ IWorkbenchWindow aw = wb.getActiveWorkbenchWindow();
+ IWorkbenchPage ap = aw.getActivePage();
+ BugzillaTaskEditorListener listener = new BugzillaTaskEditorListener();
+ ap.addPartListener(listener);
+
+ buzillaEditor = new ExistingBugEditor();
+ taskSummaryEditor = new TaskSummaryEditor();
+ }
+
+ public AbstractBugEditor getBugzillaEditor(){
+ return buzillaEditor;
+ }
+
+ public TaskSummaryEditor getTaskEditor(){
+ return taskSummaryEditor;
+ }
+
+
+ public void gotoMarker(IMarker marker) {
+ // don't do anything
+ }
+
+ /**
+ * Creates page 1 of the multi-page editor,
+ * which allows you to change the font used in page 2.
+ */
+ private void createBugzillaSubmitPage() {
+ buzillaEditor.createPartControl(getContainer());
+ Composite composite = buzillaEditor.getEditorComposite();
+ int index = addPage(composite);
+ setPageText(index, "Bugzilla");
+ }
+
+
+ private void createSummaryPage() {
+ try{
+ int index = addPage(taskSummaryEditor, new TaskEditorInput(bugTask));
+ setPageText(index, "Summary");
+ }catch(Exception e){
+ MylarPlugin.log(this.getClass().toString(), e);
+ }
+ }
+
+ /**
+ * Creates the pages of the multi-page editor.
+ */
+ @Override
+ protected void createPages() {
+ createBugzillaSubmitPage();
+ createSummaryPage();
+ }
+
+ /**
+ * Saves the multi-page editor's document.
+ */
+ @Override
+ public void doSave(IProgressMonitor monitor) {
+ getEditor(0).doSave(monitor);
+ }
+
+ /**
+ * Saves the multi-page editor's document as another file.
+ * Also updates the text for page 0's tab, and updates this multi-page editor's input
+ * to correspond to the nested editor's.
+ */
+ @Override
+ public void doSaveAs() {
+ IEditorPart editor = getEditor(0);
+ editor.doSaveAs();
+ setPageText(0, editor.getTitle());
+ setInput(editor.getEditorInput());
+ }
+
+ @Override
+ public void init(IEditorSite site, IEditorInput editorInput) throws PartInitException {
+ if (!(editorInput instanceof BugzillaTaskEditorInput))
+ throw new PartInitException("Invalid Input: Must be BugzillaTaskEditorInput");
+ bugzillaEditorInput = (BugzillaTaskEditorInput) editorInput;
+ bugTask = bugzillaEditorInput.getBugTask();
+
+ offlineBug = bugzillaEditorInput.getOfflineBug();
+ super.init(site, editorInput);
+ super.setSite(site);
+ super.setInput(editorInput);
+
+ try {
+ buzillaEditor.init(this.getEditorSite(), this.getEditorInput());
+ }
+ catch (Exception e) {
+ throw new PartInitException(e.getMessage());
+ }
+
+ // Set the title on the editor's tab
+ this.setPartName("Bug #" + bugzillaEditorInput.getBugId());
+ this.setTitleImage(MylarImages.getImage(MylarImages.TASK_BUGZILLA));
+ }
+
+ @Override
+ public boolean isSaveAsAllowed() {
+ return false;
+ }
+
+ /**
+ * Calculates the contents of page 2 when the it is activated.
+ */
+ @Override
+ protected void pageChange(int newPageIndex) {
+ super.pageChange(newPageIndex);
+ }
+
+ /**
+ * Sets the font related data to be applied to the text in page 2.
+ */
+ @Override
+ public void setFocus() {
+ // The default focus for this editor is the submit page
+ buzillaEditor.setFocus();
+ }
+
+ /**
+ * @return Returns the bugTask.
+ */
+ public BugzillaTask getBugTask() {
+ return bugTask;
+ }
+
+ /**
+ * @return Returns the offlineBug.
+ */
+ public BugReport getOfflineBug() {
+ return offlineBug;
+ }
+
+ /**
+ * 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.
+ * @param isDirty
+ * is true when the bug report has been modified but not saved
+ */
+ public void showDirtyStatus(boolean isDirty) {
+ String prefix = (isDirty) ? "*" : "" ;
+ setPartName(prefix + "Bug #" + bugzillaEditorInput.getBugId());
+ }
+
+ /**
+ * Class to listen for editor events
+ */
+ private class BugzillaTaskEditorListener implements IPartListener
+ {
+
+ public void partActivated(IWorkbenchPart part) {
+ // don't care about this event
+ }
+
+ public void partBroughtToTop(IWorkbenchPart part) {
+ // don't care about this event
+ }
+
+ public void partClosed(IWorkbenchPart part) {
+
+ // if we are closing a bug editor
+ if (part instanceof BugzillaTaskEditor) {
+ BugzillaTaskEditor taskEditor = (BugzillaTaskEditor)part;
+
+ // check if it needs to be saved
+ if (taskEditor.buzillaEditor.isDirty) {
+ // ask the user whether they want to save it or not and perform the appropriate action
+ taskEditor.buzillaEditor.changeDirtyStatus(false);
+ boolean response = MessageDialog.openQuestion(null, "Save Changes",
+ "You have made some changes to the bug, do you want to save them?");
+ if (response) {
+ taskEditor.buzillaEditor.saveBug();
+ }
+ }
+ }
+ }
+
+ public void partDeactivated(IWorkbenchPart part) {
+ // don't care about this event
+ }
+
+ public void partOpened(IWorkbenchPart part) {
+ // don't care about this event
+ }
+ }
+
+ public void makeNewPage(BugReport serverBug, String newCommentText) {
+ if (serverBug == null) {
+ MessageDialog.openInformation(Workbench.getInstance().getActiveWorkbenchWindow().getShell(),
+ "Could not open bug.", "Bug #" + offlineBug.getId()
+ + " could not be read from the server. Try refreshing the bug task.");
+ return;
+ }
+ }
+
+ @Override
+ public Object getAdapter(Class adapter) {
+ return buzillaEditor.getAdapter(adapter);
+ }
+
+ public void close() {
+ Display display= getSite().getShell().getDisplay();
+ display.asyncExec(new Runnable() {
+ public void run() {
+ getSite().getPage().closeEditor(BugzillaTaskEditor.this, false);
+ }
+ });
+ }
+}
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/BugzillaTaskEditorInput.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/BugzillaTaskEditorInput.java
new file mode 100644
index 000000000..bac6c2166
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/BugzillaTaskEditorInput.java
@@ -0,0 +1,135 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2005 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
+ *******************************************************************************/
+/*
+ * Created on 1-Feb-2005
+ */
+package org.eclipse.mylar.tasks.ui;
+
+import java.io.IOException;
+
+import javax.security.auth.login.LoginException;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.mylar.bugzilla.core.BugReport;
+import org.eclipse.mylar.bugzilla.ui.editor.ExistingBugEditorInput;
+import org.eclipse.mylar.tasks.BugzillaTask;
+import org.eclipse.ui.IPersistableElement;
+
+
+/**
+ * @author ebooth
+ */
+public class BugzillaTaskEditorInput extends ExistingBugEditorInput {
+
+
+ private String bugTitle;
+
+ private BugReport offlineBug;
+
+ private BugzillaTask bugTask;
+
+ public BugzillaTaskEditorInput(BugzillaTask bugTask) throws LoginException, IOException {
+ super(BugzillaTask.getBugId(bugTask.getHandle()));
+ this.bugTask = bugTask;
+ offlineBug = bugTask.getBugReport();
+ bugId = BugzillaTask.getBugId(bugTask.getHandle());
+ bugTitle = "";
+ }
+
+ protected void setBugTitle(String str) {
+ // 03-20-03 Allows editor to store title (once it is known)
+ bugTitle = str;
+ }
+
+ @Override
+ public boolean exists() {
+ return true;
+ }
+
+ @Override
+ public ImageDescriptor getImageDescriptor() {
+ return null;
+ }
+
+ @Override
+ public String getName() {
+ return "Bug #" + bugId;
+ }
+
+ @Override
+ public IPersistableElement getPersistable() {
+ return null;
+ }
+
+ @Override
+ public String getToolTipText() {
+ return bugTitle;
+ }
+
+ @Override
+ public Object getAdapter(Class adapter) {
+ return null;
+ }
+
+ @Override
+ public int getBugId() {
+ return bugId;
+ }
+
+ /**
+ * Returns the online server bug for this input
+ *
+ * @see BugzillaRepository
+ * @see BugReport
+ */
+// public BugReport getServerBug() {
+// return serverBug;
+// }
+
+ /**
+ * Returns the offline bug for this input's Bugzilla task
+ */
+ public BugReport getOfflineBug() {
+ return offlineBug;
+ }
+
+ /**
+ * Gets the bug page input stream
+ */
+// public InputStream getInputStream() throws IOException {
+// try {
+// return url.openStream();
+// }
+// catch (Exception e) {
+// throw new IOException(e.getMessage());
+// }
+//
+// }
+
+ /**
+ * Returns true if the argument is a bug report editor input on the same bug id.
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof BugzillaTaskEditorInput) {
+ BugzillaTaskEditorInput input = (BugzillaTaskEditorInput) o;
+ return getBugId() == input.getBugId();
+ }
+ return false;
+ }
+
+ /**
+ * @return Returns the <code>BugzillaTask</code>
+ */
+ public BugzillaTask getBugTask() {
+ return bugTask;
+ }
+}
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/TaskEditor.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/TaskEditor.java
new file mode 100644
index 000000000..6ee539b0b
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/TaskEditor.java
@@ -0,0 +1,163 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2005 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
+ *******************************************************************************/
+/*
+ * Created on 19-Jan-2005
+ */
+package org.eclipse.mylar.tasks.ui;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.mylar.tasks.ITask;
+import org.eclipse.mylar.tasks.MylarTasksPlugin;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IEditorSite;
+import org.eclipse.ui.IPartListener;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.part.MultiPageEditorPart;
+
+
+/**
+ * @author ebooth
+ */
+public class TaskEditor extends MultiPageEditorPart {
+
+ protected ITask task;
+ private TaskSummaryEditor taskSummaryEditor;
+ private TaskEditorInput taskEditorInput;
+
+ public TaskEditor() {
+ super();
+
+ // get the workbench page and add a listener so we can detect when it closes
+ IWorkbench wb = MylarTasksPlugin.getDefault().getWorkbench();
+ IWorkbenchWindow aw = wb.getActiveWorkbenchWindow();
+ IWorkbenchPage ap = aw.getActivePage();
+ TaskEditorListener listener = new TaskEditorListener();
+ ap.addPartListener(listener);
+
+ taskSummaryEditor = new TaskSummaryEditor();
+ }
+
+ /**
+ * Creates page 1 of the multi-page editor,
+ * which displays the task for viewing.
+ */
+ private void createTaskSummaryPage() {
+ taskSummaryEditor.createPartControl(getContainer());
+ Composite composite = taskSummaryEditor.getEditorComposite();
+ int index = addPage(composite);
+ setPageText(index, "Summary");
+ }
+
+ /**
+ * Creates the pages of the multi-page editor.
+ */
+ @Override
+ protected void createPages() {
+ createTaskSummaryPage();
+ }
+
+ @Override
+ public void doSave(IProgressMonitor monitor) {
+ getEditor(0).doSave(monitor);
+ }
+
+ /**
+ * Saves the multi-page editor's document as another file.
+ * Also updates the text for page 0's tab, and updates this multi-page editor's input
+ * to correspond to the nested editor's.
+ *
+ * @see org.eclipse.ui.ISaveablePart#doSaveAs()
+ */
+ @Override
+ public void doSaveAs() {
+ IEditorPart editor = getEditor(0);
+ editor.doSaveAs();
+ setPageText(0, editor.getTitle());
+ setInput(editor.getEditorInput());
+ }
+
+ @Override
+ public void init(IEditorSite site, IEditorInput input) throws PartInitException {
+
+ if (!(input instanceof TaskEditorInput))
+ throw new PartInitException("Invalid Input: Must be TaskEditorInput");
+ taskEditorInput = (TaskEditorInput)input;
+ super.init(site, input);
+
+ /*
+ * The task data is saved only once, at the initialization of the editor. This is
+ * then passed to each of the child editors. This way, only one instance of
+ * the task data is stored for each editor opened.
+ */
+ task = taskEditorInput.getTask();
+ try {
+ taskSummaryEditor.init(this.getEditorSite(), this.getEditorInput());
+ taskSummaryEditor.setTask(task);
+ // Set the title on the editor's tab
+ this.setPartName(taskEditorInput.getLabel());
+ } catch (Exception e) {
+ throw new PartInitException(e.getMessage());
+ }
+ }
+
+ @Override
+ public boolean isSaveAsAllowed() {
+ return false;
+ }
+
+ /**
+ * Class to listen for editor events
+ */
+ private class TaskEditorListener implements IPartListener
+ {
+
+ /**
+ * @see org.eclipse.ui.IPartListener#partActivated(org.eclipse.ui.IWorkbenchPart)
+ */
+ public void partActivated(IWorkbenchPart part) {
+ // don't care about this event
+ }
+
+ /**
+ * @see org.eclipse.ui.IPartListener#partBroughtToTop(org.eclipse.ui.IWorkbenchPart)
+ */
+ public void partBroughtToTop(IWorkbenchPart part) {
+ // don't care about this event
+ }
+
+ /**
+ * @see org.eclipse.ui.IPartListener#partClosed(org.eclipse.ui.IWorkbenchPart)
+ */
+ public void partClosed(IWorkbenchPart part) {
+ // don't care about this event
+ }
+
+ /**
+ * @see org.eclipse.ui.IPartListener#partDeactivated(org.eclipse.ui.IWorkbenchPart)
+ */
+ public void partDeactivated(IWorkbenchPart part) {
+ // don't care about this event
+ }
+
+ /**
+ * @see org.eclipse.ui.IPartListener#partOpened(org.eclipse.ui.IWorkbenchPart)
+ */
+ public void partOpened(IWorkbenchPart part) {
+ // don't care about this event
+ }
+ }
+}
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/TaskEditorCopyAction.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/TaskEditorCopyAction.java
new file mode 100644
index 000000000..9a3c4175c
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/TaskEditorCopyAction.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2005 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
+ *******************************************************************************/
+/*
+ * Created on 20-Jan-2005
+ */
+package org.eclipse.mylar.tasks.ui;
+
+import org.eclipse.jface.action.Action;
+
+/**
+ * @author ebooth
+ */
+public class TaskEditorCopyAction extends Action {
+
+ public TaskEditorCopyAction() {
+ setText("TaskSummaryEditor.copy.text");
+ }
+
+ @Override
+ public void run() {
+// if (editorPart instanceof TaskSummaryEditor)
+// ((TaskSummaryEditor)editorPart).getCurrentText().copy();
+ }
+
+}
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/TaskEditorInput.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/TaskEditorInput.java
new file mode 100644
index 000000000..90b084e59
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/TaskEditorInput.java
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2005 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
+ *******************************************************************************/
+/*
+ * Created on 19-Jan-2005
+ */
+package org.eclipse.mylar.tasks.ui;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.mylar.tasks.ITask;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IPersistableElement;
+
+
+/**
+ * @author ebooth
+ */
+public class TaskEditorInput implements IEditorInput {
+
+ private ITask task;
+
+ private String id;
+
+ private String label;
+
+ public TaskEditorInput(ITask task) {
+ this.task = task;
+ id = task.getHandle();
+ label = task.getLabel();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IEditorInput#exists()
+ */
+ public boolean exists() {
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IEditorInput#getImageDescriptor()
+ */
+ public ImageDescriptor getImageDescriptor() {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IEditorInput#getName()
+ */
+ public String getName() {
+ return "Task #" + id;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IEditorInput#getPersistable()
+ */
+ public IPersistableElement getPersistable() {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IEditorInput#getToolTipText()
+ */
+ public String getToolTipText() {
+ return label;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
+ */
+ public Object getAdapter(Class adapter) {
+ return null;
+ }
+
+ /**
+ * @return Returns the task.
+ */
+ public ITask getTask() {
+ return task;
+ }
+
+ /**
+ * @return Returns the id.
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * @return Returns the label.
+ */
+ public String getLabel() {
+ return label;
+ }
+
+ /**
+ * Returns true if the argument is a bug report editor input on the same bug id.
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof TaskEditorInput) {
+ TaskEditorInput input = (TaskEditorInput) o;
+ return getId() == input.getId();
+ }
+ return false;
+ }
+}
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/actions/ToggleIntersectionModeAction.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/actions/ToggleIntersectionModeAction.java
new file mode 100644
index 000000000..2681be3df
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/actions/ToggleIntersectionModeAction.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2005 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
+ *******************************************************************************/
+/*
+ * Created on May 11, 2005
+ */
+package org.eclipse.mylar.tasks.ui.actions;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.mylar.core.ITaskscapeListener;
+import org.eclipse.mylar.core.MylarPlugin;
+import org.eclipse.mylar.ui.MylarImages;
+import org.eclipse.mylar.ui.MylarUiPlugin;
+
+/**
+ * @author Mik Kersten
+ */
+public class ToggleIntersectionModeAction extends Action {
+
+ public ToggleIntersectionModeAction() {
+ super();
+ setText("Intersect Tasskscapes");
+ setToolTipText("Intersect Taskscapes");
+ setImageDescriptor(MylarImages.INTERSECTION);
+ setActionDefinitionId("org.eclipse.mylar.ui.interest.intersection");
+ setChecked(MylarUiPlugin.getDefault().isGlobalFilteringEnabled());
+ }
+
+ @Override
+ public void run() {
+ setChecked(!isChecked());
+ MylarUiPlugin.getDefault().setIntersectionMode(isChecked());
+ MylarPlugin.getTaskscapeManager().notifyActivePresentationSettingsChange(ITaskscapeListener.UpdateKind.HIGHLIGHTER);
+ }
+}
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/views/ActiveTaskscapeView.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/views/ActiveTaskscapeView.java
new file mode 100644
index 000000000..fc6f237c2
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/views/ActiveTaskscapeView.java
@@ -0,0 +1,207 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2005 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.tasks.ui.views;
+
+
+import org.eclipse.jface.action.*;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.viewers.*;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.ui.*;
+import org.eclipse.ui.part.ViewPart;
+
+
+/**
+ * This sample class demonstrates how to plug-in a new
+ * workbench view. The view shows data obtained from the
+ * model. The sample creates a dummy model on the fly,
+ * but a real implementation would connect to the model
+ * available either in this or another plug-in (e.g. the workspace).
+ * The view is connected to the model using a content provider.
+ * <p>
+ * The view uses a label provider to define how model
+ * objects should be presented in the view. Each
+ * view can present the same model objects using
+ * different labels and icons, if needed. Alternatively,
+ * a single label provider can be shared between views
+ * in order to ensure that objects of the same type are
+ * presented in the same way everywhere.
+ * <p>
+ */
+
+public class ActiveTaskscapeView extends ViewPart {
+
+ private TableViewer viewer;
+ private Action action1;
+ private Action action2;
+ private Action doubleClickAction;
+
+ /*
+ * The content provider class is responsible for
+ * providing objects to the view. It can wrap
+ * existing objects in adapters or simply return
+ * objects as-is. These objects may be sensitive
+ * to the current input of the view, or ignore
+ * it and always show the same content
+ * (like Task List, for example).
+ */
+
+ class ViewContentProvider implements IStructuredContentProvider {
+ public void inputChanged(Viewer v, Object oldInput, Object newInput) {
+ // don't care if the input changes
+ }
+ public void dispose() {
+ // don't care if we are disposed
+ }
+ public Object[] getElements(Object parent) {
+ return new String[] { "One", "Two", "Three" };
+ }
+ }
+ class ViewLabelProvider extends LabelProvider implements ITableLabelProvider {
+ public String getColumnText(Object obj, int index) {
+ return getText(obj);
+ }
+ public Image getColumnImage(Object obj, int index) {
+ return getImage(obj);
+ }
+
+ @Override
+ public Image getImage(Object obj) {
+ return PlatformUI.getWorkbench().
+ getSharedImages().getImage(ISharedImages.IMG_OBJ_ELEMENT);
+ }
+ }
+ class NameSorter extends ViewerSorter {
+ // empty class used as default sorter for the view
+ }
+
+ /**
+ * The constructor.
+ */
+ public ActiveTaskscapeView() {
+ // don't have any initialization
+ }
+
+ /**
+ * This is a callback that will allow us
+ * to create the viewer and initialize it.
+ */
+ @Override
+ public void createPartControl(Composite parent) {
+ viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
+ viewer.setContentProvider(new ViewContentProvider());
+ viewer.setLabelProvider(new ViewLabelProvider());
+ viewer.setSorter(new NameSorter());
+ viewer.setInput(getViewSite());
+
+ makeActions();
+ hookContextMenu();
+ hookDoubleClickAction();
+ contributeToActionBars();
+ }
+
+ private void hookContextMenu() {
+ MenuManager menuMgr = new MenuManager("#PopupMenu");
+ menuMgr.setRemoveAllWhenShown(true);
+ menuMgr.addMenuListener(new IMenuListener() {
+ public void menuAboutToShow(IMenuManager manager) {
+ ActiveTaskscapeView.this.fillContextMenu(manager);
+ }
+ });
+ Menu menu = menuMgr.createContextMenu(viewer.getControl());
+ viewer.getControl().setMenu(menu);
+ getSite().registerContextMenu(menuMgr, viewer);
+ }
+
+ private void contributeToActionBars() {
+ IActionBars bars = getViewSite().getActionBars();
+ fillLocalPullDown(bars.getMenuManager());
+ fillLocalToolBar(bars.getToolBarManager());
+ }
+
+ private void fillLocalPullDown(IMenuManager manager) {
+ manager.add(action1);
+ manager.add(new Separator());
+ manager.add(action2);
+ }
+
+ private void fillContextMenu(IMenuManager manager) {
+ manager.add(action1);
+ manager.add(action2);
+ // Other plug-ins can contribute there actions here
+ manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
+ }
+
+ private void fillLocalToolBar(IToolBarManager manager) {
+ manager.add(action1);
+ manager.add(action2);
+ }
+
+ private void makeActions() {
+ action1 = new Action() {
+
+ @Override
+ public void run() {
+ showMessage("Action 1 executed");
+ }
+ };
+ action1.setText("Action 1");
+ action1.setToolTipText("Action 1 tooltip");
+ action1.setImageDescriptor(PlatformUI.getWorkbench().getSharedImages().
+ getImageDescriptor(ISharedImages.IMG_OBJS_INFO_TSK));
+
+ action2 = new Action() {
+
+ @Override
+ public void run() {
+ showMessage("Action 2 executed");
+ }
+ };
+ action2.setText("Action 2");
+ action2.setToolTipText("Action 2 tooltip");
+ action2.setImageDescriptor(PlatformUI.getWorkbench().getSharedImages().
+ getImageDescriptor(ISharedImages.IMG_OBJS_INFO_TSK));
+ doubleClickAction = new Action() {
+
+ @Override
+ public void run() {
+ ISelection selection = viewer.getSelection();
+ Object obj = ((IStructuredSelection)selection).getFirstElement();
+ showMessage("Double-click detected on "+obj.toString());
+ }
+ };
+ }
+
+ private void hookDoubleClickAction() {
+ viewer.addDoubleClickListener(new IDoubleClickListener() {
+ public void doubleClick(DoubleClickEvent event) {
+ doubleClickAction.run();
+ }
+ });
+ }
+ private void showMessage(String message) {
+ MessageDialog.openInformation(
+ viewer.getControl().getShell(),
+ "Sample View",
+ message);
+ }
+
+ /**
+ * Passing the focus request to the viewer's control.
+ */
+ @Override
+ public void setFocus() {
+ viewer.getControl().setFocus();
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/views/TaskListLabelProvider.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/views/TaskListLabelProvider.java
new file mode 100644
index 000000000..308c35755
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/views/TaskListLabelProvider.java
@@ -0,0 +1,213 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2005 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
+ *******************************************************************************/
+/*
+ * Created on Feb 18, 2005
+ */
+package org.eclipse.mylar.tasks.ui.views;
+
+import org.eclipse.jface.viewers.IColorProvider;
+import org.eclipse.jface.viewers.IFontProvider;
+import org.eclipse.jface.viewers.ITableLabelProvider;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.mylar.tasks.BugzillaTask;
+import org.eclipse.mylar.tasks.ITask;
+import org.eclipse.mylar.ui.MylarImages;
+import org.eclipse.mylar.ui.MylarUiPlugin;
+import org.eclipse.mylar.ui.internal.UiUtil;
+import org.eclipse.mylar.ui.internal.views.Highlighter;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * @author Mik Kersten
+ */
+public class TaskListLabelProvider extends LabelProvider implements ITableLabelProvider, IColorProvider, IFontProvider {
+
+ private Color backgroundColor = null;
+
+ public String getColumnText(Object obj, int columnIndex) {
+ if (obj instanceof ITask) {
+ ITask task = (ITask) obj;
+ switch (columnIndex) {
+ case 0: return " "; // padding for background
+ case 1: return "";
+ case 2:
+ if (task.isCategory()) {
+ return "";
+ } else {
+ return task.getPriority();
+ }
+ case 3:
+ return task.getLabel();
+ case 4:
+ return task.getHandle();
+ }
+ }
+ return null;
+ }
+
+ public Font getFont(Object element) {
+ if (element instanceof ITask) {
+ ITask task = (ITask)element;
+ if (task.isCategory()) {
+ for (ITask child : task.getChildren()) {
+ if (child.isActive()) return UiUtil.BOLD;
+ }
+ }
+ if (task.isActive()) return UiUtil.BOLD;
+ if (task.isCompleted()) return UiUtil.ITALIC;
+ }
+ return null;
+ }
+
+ public Image getColumnImage(Object element, int columnIndex) {
+
+ if (!(element instanceof ITask)) {
+ return null;
+ }
+ if (columnIndex == 0) {
+ if (((ITask) element).isCategory()) {
+ return null;
+ }
+ if (((ITask) element).isActive()) {
+ return MylarImages.getImage(MylarImages.TASK_ACTIVE);
+ } else {
+ return MylarImages.getImage(MylarImages.TASK_INACTIVE);
+ }
+ } else if (columnIndex == 1) {
+ if (((ITask) element).isCategory()) {
+ return null;
+ // return MylarImages.getImage(MylarImages.CATEGORY);
+ } else if (element instanceof BugzillaTask) {
+ return MylarImages.getImage(MylarImages.TASK_BUGZILLA);
+ } else {
+ return MylarImages.getImage(MylarImages.TASK);
+ }
+ } else {
+ return null;
+ }
+ }
+
+ public Color getBackground(Object element) {
+ if (element instanceof ITask) {
+ ITask task = (ITask)element;
+ if (task.isCategory()) {
+ return backgroundColor;
+ }
+ Highlighter highlighter = MylarUiPlugin.getDefault().getHighlighterForTaskId("" + task.getHandle());
+ if (highlighter != null) return highlighter.getHighlightColor();
+ }
+ return null;
+ }
+
+ public Color getForeground(Object element) {
+ if (element instanceof ITask) {
+ ITask task = (ITask)element;
+ if (task.isCompleted()) return MylarUiPlugin.getDefault().getColorMap().GRAY_VERY_LIGHT;
+ }
+ return null;
+ }
+
+ public void setBackgroundColor(Color c) {
+ this.backgroundColor = c;
+ }
+}
+
+//public Image getColumnImage(Object obj, int columnIndex) {
+//if (obj instanceof Highlighter) {
+// Highlighter h = (Highlighter) obj;
+// switch (columnIndex) {
+// case 1:
+// HighlighterImageDescriptor des;
+// if (h.isGradient()) {
+// des = new HighlighterImageDescriptor(h.getBase(), h
+// .getLandmarkColor());
+// } else {
+// des = new HighlighterImageDescriptor(h
+// .getLandmarkColor(), h.getLandmarkColor());
+// }
+// return des.getImage();
+// default:
+// break;
+// }
+// }
+// return null;
+//}
+
+//public Color getBackground(Object element) {
+//if (element instanceof Task) {
+// Task task = (Task)element;
+// if (task.isActive()) {
+// Highlighter highlighter = MylarUiPlugin.getDefault().getHighlighterForTaskId(((Task)task).getId());
+// if (highlighter != null) {
+// return highlighter.getHighlightColor();
+// } else {
+// return null;
+// }
+// }
+//}
+//return null;
+//}
+
+//public class TaskListLabelProvider extends LabelProvider implements IColorProvider, IFontProvider {
+//
+// public String getText(Object obj) {
+// if (obj instanceof BugzillaTask) {
+// String desc = MylarTasksPlugin.getDefault().getBugzillaProvider().getBugzillaDescription(
+// ((BugzillaTask)obj));
+// return desc;
+// } else if (obj instanceof Task) {
+// Task task = (Task)obj;
+// return task.toString();// + " [" + task.getId() + "]";
+// } else {
+// return obj.toString();
+// }
+// }
+//
+// public Image getImage(Object obj) {
+// String imageKey = ISharedImages.IMG_OBJ_ELEMENT;
+// if (obj instanceof BugzillaTask) {
+// return MylarImages.getImage(MylarImages.TASK_BUGZILLA);
+// } else if (obj instanceof Task) {
+// return MylarImages.getImage(MylarImages.TASK);
+// } else {
+// return null;
+// }
+// }
+// public Color getForeground(Object element) {
+// return null;
+// }
+//
+// public Color getBackground(Object element) {
+// if (element instanceof Task) {
+// Task task = (Task)element;
+// if (task.isActive()) {
+// Highlighter highlighter = MylarUiPlugin.getDefault().getHighlighterForTaskId(((Task)task).getId());
+// if (highlighter != null) {
+// return highlighter.getHighlightColor();
+// } else {
+// return null;
+// }
+// }
+// }
+// return null;
+// }
+//
+// public Font getFont(Object element) {
+// if (element instanceof Task) {
+// if (((Task)element).isActive()) {
+// return UiUtil.BOLD;
+// }
+// }
+// return null;
+// }
+//} \ No newline at end of file
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/views/TaskListView.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/views/TaskListView.java
new file mode 100644
index 000000000..d4c71ca84
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/views/TaskListView.java
@@ -0,0 +1,1156 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2005 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.tasks.ui.views;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.security.auth.login.LoginException;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IMenuListener;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.dialogs.InputDialog;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.viewers.CellEditor;
+import org.eclipse.jface.viewers.CheckboxCellEditor;
+import org.eclipse.jface.viewers.ComboBoxCellEditor;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.ICellModifier;
+import org.eclipse.jface.viewers.IDoubleClickListener;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TextCellEditor;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerDropAdapter;
+import org.eclipse.jface.viewers.ViewerSorter;
+import org.eclipse.jface.window.Window;
+import org.eclipse.mylar.core.ITaskscapeListener;
+import org.eclipse.mylar.core.MylarPlugin;
+import org.eclipse.mylar.core.model.ITaskscape;
+import org.eclipse.mylar.core.model.ITaskscapeNode;
+import org.eclipse.mylar.dt.MylarWebRef;
+import org.eclipse.mylar.tasks.BugzillaTask;
+import org.eclipse.mylar.tasks.Category;
+import org.eclipse.mylar.tasks.ITask;
+import org.eclipse.mylar.tasks.MylarTasksPlugin;
+import org.eclipse.mylar.tasks.Task;
+import org.eclipse.mylar.tasks.ui.BugzillaTaskEditorInput;
+import org.eclipse.mylar.ui.MylarImages;
+import org.eclipse.mylar.ui.MylarUiPlugin;
+import org.eclipse.mylar.ui.actions.ToggleGlobalInterestFilteringAction;
+import org.eclipse.mylar.ui.internal.views.Highlighter;
+import org.eclipse.mylar.ui.internal.views.HighlighterImageDescriptor;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.DND;
+import org.eclipse.swt.dnd.DragSourceEvent;
+import org.eclipse.swt.dnd.DragSourceListener;
+import org.eclipse.swt.dnd.TextTransfer;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.dnd.TransferData;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.TreeColumn;
+import org.eclipse.swt.widgets.TreeItem;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IMemento;
+import org.eclipse.ui.IViewSite;
+import org.eclipse.ui.IWorkbenchActionConstants;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.internal.Workbench;
+import org.eclipse.ui.part.DrillDownAdapter;
+import org.eclipse.ui.part.ViewPart;
+
+/**
+ * @author Mik Kersten
+ */
+public class TaskListView extends ViewPart {
+
+ private static TaskListView INSTANCE;
+
+ //private CheckboxTreeViewer viewer;
+ private TreeViewer viewer;
+ private DrillDownAdapter drillDownAdapter;
+
+ private Action refresh;
+ private Action createTask;
+ private Action createCategory;
+ private Action addBugzillaReport;
+ private Action rename;
+ private Action delete;
+ private Action doubleClickAction;
+ private Action clearSelectedTaskscapeAction;
+
+ //private Action toggleIntersectionModeAction = new ToggleIntersectionModeAction();
+ private Action toggleFilteringAction = new ToggleGlobalInterestFilteringAction();
+
+ private Action completeTask;
+ private Action incompleteTask;
+
+ private Action filterCompleteTask;
+ private Action filterInCompleteTask;
+
+ protected String[] columnNames = new String[] { "", ".", "!", "Description", "handle" };
+ protected int[] columnWidths = new int[] { 70, 20, 20, 120, 70 };
+ private TreeColumn[] columns;
+ private IMemento taskListMemento;
+ public static final String columnWidthIdentifier = "org.eclipse.mylar.tasks.ui.views.tasklist.columnwidth";
+ public static final String tableSortIdentifier = "org.eclipse.mylar.tasks.ui.views.tasklist.sortIndex";
+ private int sortIndex = 2;
+
+
+ private String[] PRIORITY_LEVELS = { "P1", "P2", "P3", "P4", "P5" };
+
+ private final class CreateTaskAction extends Action {
+ private boolean isCategory = false;
+
+ public CreateTaskAction(boolean isCategory) {
+ this.isCategory = isCategory;
+ }
+
+ @Override
+ public void run() {
+ String label = getLabelNameFromUser("task");
+ if(label == null) return;
+ Task newTask = new Task(MylarTasksPlugin.getTaskListManager().genUniqueTaskId(), label);
+
+ Object selectedObject = ((IStructuredSelection)viewer.getSelection()).getFirstElement();
+ if (selectedObject instanceof Task && !isCategory){
+ ((Task)selectedObject).addSubtask(newTask);
+ } else {
+ if (isCategory) {
+ newTask.setIsCategory(true);
+ }
+ MylarTasksPlugin.getTaskListManager().getTaskList().addRootTask(newTask);
+ }
+ MylarUiPlugin.getDefault().setHighlighterMapping(
+ newTask.getHandle(),
+ MylarUiPlugin.getDefault().getDefaultHighlighter().getName());
+ TaskListView.this.viewer.refresh();
+
+ viewer.refresh();
+ }
+ }
+
+ private final ITaskscapeListener FILTER_LISTENER = new ITaskscapeListener() {
+ public void interestChanged(ITaskscapeNode info) {}
+ public void interestChanged(List<ITaskscapeNode> nodes) {}
+ public void taskscapeActivated(ITaskscape taskscape) {}
+ public void taskscapeDeactivated(ITaskscape taskscape) {}
+ public void nodeDeleted(ITaskscapeNode node) {}
+ public void landmarkAdded(ITaskscapeNode element) {}
+ public void landmarkRemoved(ITaskscapeNode element) {}
+ public void relationshipsChanged() {}
+ public void presentationSettingsChanged(UpdateKind kind) {
+ refresh();
+ }
+ public void presentationSettingsChanging(UpdateKind kind) {
+ refresh();
+ }
+
+ private void refresh() {
+ if (viewer != null && !viewer.getTree().isDisposed()) {
+ viewer.refresh();
+ setCheckedState(viewer.getTree().getItems());
+ }
+ }
+ };
+
+ class TaskListContentProvider implements IStructuredContentProvider, ITreeContentProvider {
+ public void inputChanged(Viewer v, Object oldInput, Object newInput) {
+ // don't care if the input changes
+ }
+ public void dispose() {
+ // don't care if we are disposed
+ }
+ public Object[] getElements(Object parent) {
+ if (parent.equals(getViewSite())) {
+ if (MylarUiPlugin.getDefault().isGlobalFilteringEnabled()) {
+ return MylarTasksPlugin.getTaskListManager().getTaskList().getTasksInProgress().toArray();
+ } else {
+ return MylarTasksPlugin.getTaskListManager().getTaskList().getRootTasks().toArray();
+ }
+ }
+ return getChildren(parent);
+ }
+ public Object getParent(Object child) {
+ if (child instanceof Task) {
+ return ((Task)child).getParent();
+ }
+ return null;
+ }
+ public Object [] getChildren(Object parent) {
+ if (parent instanceof ITask) {
+ if (MylarUiPlugin.getDefault().isGlobalFilteringEnabled()) {
+ return ((ITask)parent).getSubTasksInProgress().toArray();
+ } else {
+ return ((ITask)parent).getChildren().toArray();
+ }
+ }
+ return new Object[0];
+ }
+ public boolean hasChildren(Object parent) {
+ if (parent instanceof ITask) {
+ ITask task = (ITask)parent;
+ return task.getChildren() != null && task.getChildren().size() > 0;
+ }
+ return false;
+ }
+ }
+
+ public TaskListView() {
+ INSTANCE = this;
+ MylarPlugin.getTaskscapeManager().addListener(FILTER_LISTENER);
+ }
+
+ class TaskListCellModifier implements ICellModifier {
+
+ public boolean canModify(Object element, String property) {
+ int columnIndex = Arrays.asList(columnNames).indexOf(property);
+ ITask task = (ITask) element;
+ switch (columnIndex) {
+ case 0:
+ return true;
+ case 1:
+ return false;
+ case 2:
+ return !(task instanceof BugzillaTask);
+ case 3:
+ return !(task instanceof BugzillaTask);
+ case 4:
+ return false;
+ }
+ return false;
+ }
+
+ public Object getValue(Object element, String property) {
+ int columnIndex = Arrays.asList(columnNames).indexOf(property);
+ ITask task = (ITask) element;
+ switch (columnIndex) {
+ case 0:
+ return new Boolean(task.isCompleted());
+ case 1:
+ return "";
+ case 2:
+ String priorityString = task.getPriority().substring(1);
+ return new Integer(priorityString);
+ case 3:
+ return task.getLabel();
+ case 4:
+ return task.getHandle();
+ }
+ return "";
+ }
+
+ public void modify(Object element, String property, Object value) {
+ int columnIndex = -1;
+ try {
+ columnIndex = Arrays.asList(columnNames).indexOf(property);
+ ITask task = (ITask) ((TreeItem) element).getData();
+ switch (columnIndex) {
+ case 0:
+ if (!task.isCategory()) {
+ if (task.isActive()) {
+ MylarTasksPlugin.getTaskListManager().deactivateTask(
+ task);
+ } else {
+ MylarTasksPlugin.getTaskListManager().activateTask(
+ task);
+ }
+ }
+ viewer.setSelection(null);
+ break;
+ case 1:
+ break;
+ case 2:
+ Integer intVal = (Integer) value;
+ task.setPriority("P" + (intVal + 1));
+ viewer.setSelection(null);
+ break;
+ case 3:
+ task.setLabel(((String) value).trim());
+ viewer.setSelection(null);
+ break;
+ case 4:
+ break;
+ }
+ viewer.refresh();
+ } catch (Exception e) {
+ }
+ }
+ }
+
+ private class TaskListTableSorter extends ViewerSorter {
+
+ private String column;
+
+ public TaskListTableSorter(String column) {
+ super();
+ this.column = column;
+ }
+
+ /**
+ * compare - invoked when column is selected calls the actual comparison
+ * method for particular criteria
+ */
+ @Override
+ public int compare(Viewer compareViewer, Object o1, Object o2) {
+ ITask task1 = (ITask) o1;
+ ITask task2 = (ITask) o2;
+
+ if (task1.isCompleted()) return 1;
+ if (task2.isCompleted()) return -1;
+ if (column == columnNames[1]) {
+ if (task1 instanceof BugzillaTask && !(task2 instanceof BugzillaTask)) {
+ return 1;
+ } else {
+ return -1;
+ }
+ } else if (column == columnNames[2]) {
+ return task1.getPriority().compareTo(task2.getPriority());
+ } else if (column == columnNames[3]) {
+ return task1.getLabel().compareTo(task2.getLabel());
+ } else if (column == columnNames[4]){
+ return task1.getPath().compareTo(task2.getPath());
+ } else {
+ return 0;
+ }
+ }
+ }
+
+ @Override
+ public void init(IViewSite site,IMemento memento) throws PartInitException {
+ init(site);
+ this.taskListMemento = memento;
+ }
+
+ @Override
+ public void saveState(IMemento memento) {
+ IMemento colMemento = memento.createChild(columnWidthIdentifier);
+
+ for (int i = 0; i < columnWidths.length; i++) {
+ IMemento m = colMemento.createChild("col"+i);
+ m.putInteger("width", columnWidths[i]);
+ }
+
+ IMemento sorter = memento.createChild(tableSortIdentifier);
+ IMemento m = sorter.createChild("sorter");
+ m.putInteger("sortIndex", sortIndex);
+ }
+
+ private void restoreState() {
+ if (taskListMemento == null)
+ return;
+ IMemento taskListWidth = taskListMemento.getChild(columnWidthIdentifier);
+ if (taskListWidth != null) {
+ for (int i = 0; i < columnWidths.length; i++) {
+ IMemento m = taskListWidth.getChild("col"+i);
+ if (m != null) {
+ int width = m.getInteger("width");
+ columnWidths[i] = width;
+ columns[i].setWidth(width);
+ }
+ }
+ }
+ IMemento sorterMemento = taskListMemento.getChild(tableSortIdentifier);
+ if (sorterMemento != null) {
+ IMemento m = sorterMemento.getChild("sorter");
+ if (m != null) {
+ sortIndex = m.getInteger("sortIndex");
+ } else {
+ sortIndex = 2;
+ }
+ } else {
+ sortIndex = 2; // default priority
+ }
+ viewer.setSorter(new TaskListTableSorter(columnNames[sortIndex]));
+ viewer.refresh();
+ }
+
+ /**
+ * This is a callback that will allow us
+ * to create the viewer and initialize it.
+ */
+ @Override
+ public void createPartControl(Composite parent) {
+ viewer = new TreeViewer(parent, SWT.VERTICAL | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.HIDE_SELECTION);
+ viewer.getTree().setHeaderVisible(true);
+ viewer.getTree().setLinesVisible(true);
+ viewer.setColumnProperties(columnNames);
+ viewer.setUseHashlookup(true);
+
+ columns = new TreeColumn[columnNames.length];
+ for (int i = 0; i < columnNames.length; i++) {
+ columns[i] = new TreeColumn(viewer.getTree(), 0); // SWT.LEFT
+ columns[i].setText(columnNames[i]);
+ columns[i].setWidth(columnWidths[i]);
+ final int index = i;
+ columns[i].addSelectionListener(new SelectionAdapter() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ sortIndex = index;
+ viewer.setSorter(new TaskListTableSorter(columnNames[sortIndex]));
+ }
+ });
+ columns[i].addControlListener(new ControlListener () {
+ public void controlResized(ControlEvent e) {
+ for (int j = 0; j < columnWidths.length; j++) {
+ if (columns[j].equals(e.getSource())) {
+ columnWidths[j] = columns[j].getWidth();
+ }
+ }
+ }
+
+ public void controlMoved(ControlEvent e) {
+ // don't care if the control is moved
+ }
+ });
+
+ }
+
+ CellEditor[] editors = new CellEditor[columnNames.length];
+ TextCellEditor textEditor = new TextCellEditor(viewer.getTree());
+ ((Text) textEditor.getControl()).setOrientation(SWT.LEFT_TO_RIGHT);
+ editors[0] = new CheckboxCellEditor();
+ editors[1] = textEditor;
+ editors[2] = new ComboBoxCellEditor(viewer.getTree(), PRIORITY_LEVELS, SWT.READ_ONLY);
+ editors[3] = textEditor;
+ viewer.setCellEditors(editors);
+ viewer.setCellModifier(new TaskListCellModifier());
+ viewer.setSorter(new TaskListTableSorter(columnNames[sortIndex]));
+
+ drillDownAdapter = new DrillDownAdapter(viewer);
+ viewer.setContentProvider(new TaskListContentProvider());
+ TaskListLabelProvider lp = new TaskListLabelProvider();
+ lp.setBackgroundColor(parent.getBackground());
+ viewer.setLabelProvider(lp);
+ viewer.setInput(getViewSite());
+
+ makeActions();
+ hookContextMenu();
+ hookDoubleClickAction();
+ contributeToActionBars();
+ ToolTipHandler toolTipHandler = new ToolTipHandler(viewer.getControl().getShell());
+ toolTipHandler.activateHoverHelp(viewer.getControl());
+
+ initDragAndDrop(parent);
+ restoreState();
+ }
+
+ @MylarWebRef(name="Drag and drop article", url="http://www.eclipse.org/articles/Article-Workbench-DND/drag_drop.html")
+ private void initDragAndDrop(Composite parent) {
+ Transfer[] types = new Transfer[] { TextTransfer.getInstance() };
+
+ viewer.addDragSupport(DND.DROP_MOVE, types, new DragSourceListener() {
+
+ public void dragStart(DragSourceEvent event) {
+ if (((StructuredSelection) viewer.getSelection()).isEmpty()) {
+ event.doit = false;
+ }
+ }
+
+ public void dragSetData(DragSourceEvent event) {
+ StructuredSelection selection = (StructuredSelection) viewer.getSelection();
+ if (!selection.isEmpty()) {
+ event.data = "" + ((ITask) selection.getFirstElement()).getHandle();
+ } else {
+ event.data = "null";
+ }
+ }
+
+ public void dragFinished(DragSourceEvent event) {
+ // don't care if the drag is done
+ }
+ });
+
+ viewer.addDropSupport(DND.DROP_MOVE, types, new ViewerDropAdapter(viewer) {
+ {
+ setFeedbackEnabled(false);
+ }
+
+ @Override
+ public boolean performDrop(Object data) {
+ Object selectedObject = ((IStructuredSelection) ((TreeViewer) getViewer())
+ .getSelection()).getFirstElement();
+ if (selectedObject instanceof ITask) {
+ ITask source = (ITask) selectedObject;
+ ITask target = (ITask) getCurrentTarget();
+ source.getParent().removeSubtask(source);
+ target.addSubtask(source);
+ viewer.refresh();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean validateDrop(Object targetObject, int operation,
+ TransferData transferType) {
+ Object selectedObject = ((IStructuredSelection) ((TreeViewer) getViewer())
+ .getSelection()).getFirstElement();
+ if (selectedObject instanceof ITask) {
+ ITask source = (ITask) selectedObject;
+ ITask target = (ITask) getCurrentTarget();
+ if (target != null && !target.isCategory())
+ return false;
+ if (source.isCategory())
+ return false;
+ }
+ return TextTransfer.getInstance().isSupportedType(transferType);
+ }
+
+ });
+ }
+
+ private void setCheckedState(TreeItem[] items) {
+ for (int i = 0; i < items.length; i++) {
+ TreeItem item = items[i];
+ if (item.getData() instanceof Task) {
+ item.setChecked(((Task)item.getData()).isCompleted());
+ } else if (item.getData() instanceof Category) {
+ item.setGrayed(true);
+ }
+ setCheckedState(item.getItems());
+ }
+ }
+
+ private void hookContextMenu() {
+ MenuManager menuMgr = new MenuManager("#PopupMenu");
+ menuMgr.setRemoveAllWhenShown(true);
+ menuMgr.addMenuListener(new IMenuListener() {
+ public void menuAboutToShow(IMenuManager manager) {
+ TaskListView.this.fillContextMenu(manager);
+ }
+ });
+ Menu menu = menuMgr.createContextMenu(viewer.getControl());
+ viewer.getControl().setMenu(menu);
+ getSite().registerContextMenu(menuMgr, viewer);
+ }
+
+ private void contributeToActionBars() {
+ IActionBars bars = getViewSite().getActionBars();
+ fillLocalPullDown(bars.getMenuManager());
+ fillLocalToolBar(bars.getToolBarManager());
+ }
+
+ private void fillLocalPullDown(IMenuManager manager) {
+// manager.add(createCategory);
+// manager.add(new Separator());
+// manager.add(createTask);
+ }
+
+ void fillContextMenu(IMenuManager manager) {
+ manager.add(completeTask);
+ manager.add(incompleteTask);
+// manager.add(new Separator());
+ manager.add(createTask);
+ manager.add(addBugzillaReport);
+ manager.add(rename);
+ manager.add(delete);
+ manager.add(clearSelectedTaskscapeAction);
+ manager.add(new Separator());
+ MenuManager subMenuManager = new MenuManager("choose highlighter");
+ for (Iterator<Highlighter> it = MylarUiPlugin.getDefault().getHighlighters().iterator(); it.hasNext();) {
+ final Highlighter highlighter = it.next();
+ final Object selectedObject = ((IStructuredSelection)viewer.getSelection()).getFirstElement();
+ if (selectedObject instanceof Task){
+ Action action = new Action() {
+
+ @Override
+ public void run() {
+ Task task = (Task)selectedObject;
+ MylarUiPlugin.getDefault().setHighlighterMapping(task.getHandle(), highlighter.getName());
+ TaskListView.this.viewer.refresh();
+ MylarPlugin.getTaskscapeManager().notifyPostPresentationSettingsChange(ITaskscapeListener.UpdateKind.HIGHLIGHTER);
+// taskscapeComponent.getTableViewer().refresh();
+ }
+ };
+ if (highlighter.isGradient()) {
+ action.setImageDescriptor(new HighlighterImageDescriptor(highlighter.getBase(), highlighter.getLandmarkColor()));
+ } else {
+ action.setImageDescriptor(new HighlighterImageDescriptor(highlighter.getLandmarkColor(), highlighter.getLandmarkColor()));
+ }
+ action.setText(highlighter.toString());
+ subMenuManager.add(action);
+ } else {
+// showMessage("Select task before choosing highlighter");
+ }
+ }
+ manager.add(subMenuManager);
+ manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
+ }
+
+ private void fillLocalToolBar(IToolBarManager manager) {
+ manager.add(createCategory);
+ manager.add(createTask);
+// manager.add(new Separator());
+ manager.add(addBugzillaReport);
+ manager.add(refresh);
+ manager.add(new Separator());
+ manager.add(toggleFilteringAction);
+ //manager.add(toggleIntersectionModeAction);
+ manager.add(new Separator());
+ manager.add(filterCompleteTask);
+ manager.add(filterInCompleteTask);
+ drillDownAdapter.addNavigationActions(manager);
+ }
+
+ private void makeActions() {
+ refresh = new Action() {
+
+ @Override
+ public void run() {
+// Object[] expanded = viewer.getExpandedElements();
+// for (int i = 0; i < expanded.length; i++) {
+// Object element = expanded[i];
+// if (element instanceof BugzillaTask) {
+// ((BugzillaTask)element).refresh();
+// }
+// }
+
+ List<ITask> tasks = MylarTasksPlugin.getTaskListManager().getTaskList().getRootTasks();
+
+ for (ITask task : tasks) {
+ if (task instanceof BugzillaTask) {
+ ((BugzillaTask)task).refresh();
+ }
+ refreshChildren(task.getChildren());
+ }
+
+ viewer.refresh();
+ }
+ };
+ refresh.setText("Refresh all Bugzilla reports");
+ refresh.setToolTipText("Refresh all Bugzilla reports");
+ refresh.setImageDescriptor(MylarImages.REFRESH);
+
+// createCategory = new Action() {
+// public void run() {
+// try {
+// String label = getLabelNameFromUser("category");
+// MylarTasksPlugin.getTaskListManager().getTaskList().createCategory(label);
+// viewer.refresh();
+// } catch (Exception e) {
+// MylarPlugin.fail(e, "Couldn't create category", true);
+// }
+// }
+// };
+// createCategory.setText("Create category");
+// createCategory.setToolTipText("Create category");
+// createCategory.setImageDescriptor(MylarImages.TASK_CATEGORY_NEW);
+
+ createTask = new CreateTaskAction(false);
+ createTask.setText("Create task");
+ createTask.setToolTipText("Create task");
+ createTask.setImageDescriptor(MylarImages.TASK_NEW);
+
+ createCategory = new CreateTaskAction(true);
+ createCategory.setText("Create category");
+ createCategory.setToolTipText("Create category");
+ createCategory.setImageDescriptor(MylarImages.CATEGORY_NEW);
+
+ addBugzillaReport = new Action() {
+
+ @Override
+ public void run() {
+ String bugIdString = getBugIdFromUser();
+ int bugId = -1;
+ try {
+ bugId = Integer.parseInt(bugIdString);
+ } catch (NumberFormatException nfe) {
+ showMessage("Please enter a valid report number");
+ return;
+ }
+
+ // Check the existing tasks to see if the id is used already.
+ // This is to prevent the creation of mutliple Bugzilla tasks
+ // for the same Bugzilla report.
+ boolean doesIdExistAlready = false;
+ List<ITask> tasks = MylarTasksPlugin.getTaskListManager().getTaskList().getRootTasks();
+ for (Iterator<ITask> iter = tasks.iterator(); iter.hasNext() && !doesIdExistAlready;) {
+ ITask task = iter.next();
+ doesIdExistAlready = lookForId(task, "" + bugId); // HACK:
+ }
+ if (doesIdExistAlready) {
+ showMessage("A Bugzilla task with ID " + bugId + " already exists.");
+ return;
+ }
+
+
+ //HACK need the server name and handle properly
+ ITask newTask = new BugzillaTask("Bugzilla-"+bugId, "<bugzilla info>");
+
+ Object selectedObject = ((IStructuredSelection)viewer.getSelection()).getFirstElement();
+ if (selectedObject instanceof Task){
+ ((Task)selectedObject).addSubtask(newTask);
+ } else {
+ MylarTasksPlugin.getTaskListManager().getTaskList().addRootTask(newTask);
+ }
+// viewer.expandAll();
+ viewer.refresh();
+ }
+ };
+ addBugzillaReport.setText("Add bugzilla report");
+ addBugzillaReport.setToolTipText("Add bugzilla report");
+ addBugzillaReport.setImageDescriptor(MylarImages.TASK_BUGZILLA_NEW);
+
+ delete = new Action() {
+
+ @Override
+ public void run() {
+ boolean deleteConfirmed = MessageDialog.openQuestion(
+ Workbench.getInstance().getActiveWorkbenchWindow().getShell(),
+ "Confirm delete",
+ "Delete selected task and all subtasks?");
+ if (!deleteConfirmed) {
+ return;
+ } else {
+ Object selectedObject = ((IStructuredSelection)viewer.getSelection()).getFirstElement();
+ if (selectedObject instanceof Task) {
+ MylarTasksPlugin.getTaskListManager().deleteTask((Task)selectedObject);
+ MylarPlugin.getTaskscapeManager().taskDeleted(((Task)selectedObject).getHandle(), ((Task)selectedObject).getPath());
+ IWorkbenchPage page = MylarTasksPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getActivePage();
+
+ // if we couldn't get the page, get out of here
+ if (page == null)
+ return;
+ try{
+ closeBugTaskEditors((ITask)selectedObject, page);
+ }catch(Exception e){
+ MylarPlugin.log(this.getClass().toString(), e);
+ }
+ }
+ }
+ viewer.refresh();
+ }
+ };
+ delete.setText("Delete");
+ delete.setToolTipText("Delete");
+ delete.setImageDescriptor(MylarImages.REMOVE);
+
+ completeTask = new Action() {
+
+ @Override
+ public void run() {
+ Object selectedObject = ((IStructuredSelection)viewer.getSelection()).getFirstElement();
+ if (selectedObject instanceof Task){
+ ((Task)selectedObject).setCompleted(true);
+ viewer.refresh();
+ }
+ }
+ };
+ completeTask.setText("Mark Complete");
+ completeTask.setToolTipText("Mark Complete");
+// activateTask.setImageDescriptor(MylarImages.REMOVE);
+
+ incompleteTask = new Action() {
+
+ @Override
+ public void run() {
+ Object selectedObject = ((IStructuredSelection)viewer.getSelection()).getFirstElement();
+ if (selectedObject instanceof Task){
+ ((Task)selectedObject).setCompleted(false);
+ viewer.refresh();
+ }
+ }
+ };
+ incompleteTask.setText("Mark Incomplete");
+ incompleteTask.setToolTipText("Mark Incomplete");
+// deactivateTask.setImageDescriptor(MylarImages.REMOVE);
+
+ rename = new Action() {
+
+ @Override
+ public void run() {
+ String label = "category";
+ Object selectedObject = ((IStructuredSelection)viewer.getSelection()).getFirstElement();
+ if (selectedObject instanceof Task) label = "task";
+
+ String newName = getLabelNameFromUser(label);
+ if (selectedObject instanceof Task) {
+ ((Task)selectedObject).setLabel(newName);
+ } else if (selectedObject instanceof Category) {
+ ((Category)selectedObject).setName(newName);
+ }
+ viewer.refresh();
+ }
+ };
+ rename.setText("Rename");
+ rename.setToolTipText("Rename");
+
+ clearSelectedTaskscapeAction = new Action() {
+
+ @Override
+ public void run() {
+ Object selectedObject = ((IStructuredSelection)viewer.getSelection()).getFirstElement();
+ MylarPlugin.getTaskscapeManager().taskDeleted(((Task)selectedObject).getHandle(), ((Task)selectedObject).getPath());
+ viewer.refresh();
+ }
+ };
+ clearSelectedTaskscapeAction.setText("Erase Taskscape");
+ clearSelectedTaskscapeAction.setToolTipText("Erase Taskscape");
+ clearSelectedTaskscapeAction.setImageDescriptor(MylarImages.ERASE_TASKSCAPE);
+
+ doubleClickAction = new Action() {
+ @Override
+ public void run() {
+ ISelection selection = viewer.getSelection();
+ Object obj = ((IStructuredSelection)selection).getFirstElement();
+ if (obj instanceof ITask) {
+ ((ITask)obj).openTaskInEditor();
+ }
+ viewer.refresh();
+ }
+ };
+
+ filterCompleteTask = new Action() {
+ @Override
+ public void run() {
+ MylarUiPlugin.getDefault().setFilterCompleteMode(isChecked());
+ viewer.refresh();
+ }
+ };
+ filterCompleteTask.setText("Filter Complete tasks");
+ filterCompleteTask.setToolTipText("Filter Completed tasks");
+ filterCompleteTask.setImageDescriptor(MylarImages.TASK_ACTIVE);
+
+ filterInCompleteTask = new Action() {
+ @Override
+ public void run() {
+ MylarUiPlugin.getDefault().setFilterInCompleteMode(isChecked());
+ viewer.refresh();
+ }
+ };
+ filterInCompleteTask.setText("Filter Incomplete tasks");
+ filterInCompleteTask.setToolTipText("Filter Incomplete tasks");
+ filterInCompleteTask.setImageDescriptor(MylarImages.TASK_INACTIVE);
+ }
+
+ /**
+ * Recursive function that checks for the occurrence of a certain task id.
+ * All children of the supplied node will be checked.
+ *
+ * @param task
+ * The <code>ITask</code> object that is to be searched.
+ * @param taskId
+ * The id that is being searched for.
+ * @return <code>true</code> if the id was found in the node or any of its
+ * children
+ */
+ protected boolean lookForId(ITask task, String taskId) {
+ if (task.getHandle() == taskId) {
+ return true;
+ }
+
+ List<ITask> children = task.getChildren();
+ if (children == null) {
+ return false;
+ }
+
+ for (ITask childTask : children) {
+ if (lookForId(childTask, taskId)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ protected void closeBugTaskEditors(ITask task, IWorkbenchPage page) throws LoginException, IOException{
+ if (task instanceof BugzillaTask) {
+ IEditorInput input = new BugzillaTaskEditorInput((BugzillaTask)task);
+ IEditorPart bugEditor = page.findEditor(input);
+
+ if (bugEditor != null) {
+ page.closeEditor(bugEditor, false);
+ }
+ }
+
+ List<ITask> children = task.getChildren();
+ if (children == null) return;
+ for (ITask child : children) closeBugTaskEditors(child, page);
+ }
+
+ protected void refreshChildren(List<ITask> children) {
+ if (children != null) {
+ for (ITask child : children) {
+ if (child instanceof BugzillaTask) {
+ ((BugzillaTask)child).refresh();
+ }
+ refreshChildren(child.getChildren());
+ }
+ }
+ }
+
+ private void hookDoubleClickAction() {
+ viewer.addDoubleClickListener(new IDoubleClickListener() {
+ public void doubleClick(DoubleClickEvent event) {
+ doubleClickAction.run();
+ }
+ });
+ }
+ private void showMessage(String message) {
+ MessageDialog.openInformation(
+ viewer.getControl().getShell(),
+ "Tasklist Message",
+ message);
+ }
+
+ /**
+ * Passing the focus request to the viewer's control.
+ */
+ @Override
+ public void setFocus() {
+ viewer.getControl().setFocus();
+ //TODO: foo
+ }
+
+ private String getBugIdFromUser() {
+ InputDialog dialog = new InputDialog(
+ Workbench.getInstance().getActiveWorkbenchWindow().getShell(),
+ "Enter Bugzilla ID",
+ "Enter the Bugzilla ID: ",
+ "",
+ null);
+ int dialogResult = dialog.open();
+ if (dialogResult == Window.OK) {
+ return dialog.getValue();
+ } else {
+ return null;
+ }
+ }
+
+ private String getLabelNameFromUser(String kind) {
+
+ InputDialog dialog = new InputDialog(
+ Workbench.getInstance().getActiveWorkbenchWindow().getShell(),
+ "Enter name",
+ "Enter a name for the " + kind + ": ",
+ "",
+ null);
+ int dialogResult = dialog.open();
+ if (dialogResult == Window.OK) {
+ return dialog.getValue();
+ } else {
+ return null;
+ }
+ }
+
+ public void notifyTaskDataChanged() {
+ if (viewer.getTree() != null && !viewer.getTree().isDisposed()) viewer.refresh();
+ }
+
+ public static TaskListView getDefault() {
+ return INSTANCE;
+ }
+}
+
+//TextTransfer textTransfer = TextTransfer.getInstance();
+//DropTarget target = new DropTarget(viewer.getTree(), DND.DROP_MOVE);
+//target.setTransfer(new Transfer[] { textTransfer });
+//target.addDropListener(new TaskListDropTargetListener(parent, null, textTransfer, true));
+//
+//DragSource source = new DragSource(viewer.getTree(), DND.DROP_MOVE);
+//source.setTransfer(types);
+
+//source.addDragListener(new DragSourceListener() {
+//public void dragStart(DragSourceEvent event) {
+// if (((StructuredSelection)viewer.getSelection()).isEmpty()) {
+// event.doit = false;
+// }
+//}
+//public void dragSetData(DragSourceEvent event) {
+// StructuredSelection selection = (StructuredSelection) viewer.getSelection();
+// if (!selection.isEmpty()) {
+// event.data = "" + ((ITask)selection.getFirstElement()).getId();
+// } else {
+// event.data = "null";
+// }
+//}
+//
+//public void dragFinished(DragSourceEvent event) { }
+//});
+
+
+// public boolean getServerStatus() {
+// return serverStatus;
+// }
+//
+// /**
+// * Sets whether or not we could connect to the Bugzilla server. If
+// * necessary, the corresponding label in the view is updated.
+// *
+// * @param canRead
+// * <code>true</code> if the Bugzilla server could be connected
+// * to
+// */
+// public void setServerStatus(boolean canRead) {
+// if (serverStatus != canRead) {
+// serverStatus = canRead;
+// updateServerStatusLabel();
+// }
+// }
+//
+// private void updateServerStatusLabel() {
+// if (serverStatusLabel.isDisposed()) {
+// return;
+// }
+// if (serverStatus) {
+// serverStatusLabel.setText(CAN_READ_LABEL);
+// }
+// else {
+// serverStatusLabel.setText(CANNOT_READ_LABEL);
+// }
+// }
+//
+// private class ServerPingJob extends Job {
+// private boolean shouldCheckAgain = true;
+// private int counter = 0;
+//
+// public ServerPingJob(String name) {
+// super(name);
+// }
+//
+// public void stopPinging() {
+// shouldCheckAgain = false;
+// }
+//
+// protected IStatus run(IProgressMonitor monitor) {
+// while (shouldCheckAgain) {
+// try {
+// final boolean canReadFromServer = TaskListView.checkServer();
+// Workbench.getInstance().getDisplay().asyncExec(new Runnable() {
+// public void run() {
+// setServerStatus(canReadFromServer);
+// }
+// });
+// System.out.println(counter++);
+// Thread.sleep(10000/*MylarPreferencePage.getServerPing()*5000*/);
+// } catch (InterruptedException e) {
+// break;
+// }
+// }
+// return new Status(IStatus.OK, MylarPlugin.IDENTIFIER, IStatus.OK, "", null);
+// }
+// }
+//
+// /**
+// * @return <code>true</code> if we could connect to the Bugzilla server
+// */
+// public static boolean checkServer() {
+// boolean canRead = true;
+// BufferedReader in = null;
+//
+// // Call this function to intialize the Bugzilla url that the repository
+// // is using.
+// BugzillaRepository.getInstance();
+//
+// try {
+// // connect to the bugzilla server
+// SSLContext ctx = SSLContext.getInstance("TLS");
+// javax.net.ssl.TrustManager[] tm = new javax.net.ssl.TrustManager[]{new TrustAll()};
+// ctx.init(null, tm, null);
+// HttpsURLConnection.setDefaultSSLSocketFactory(ctx.getSocketFactory());
+// String urlText = "";
+//
+// // use the usename and password to get into bugzilla if we have it
+// if(BugzillaPreferences.getUserName() != null && !BugzillaPreferences.getUserName().equals("") && BugzillaPreferences.getPassword() != null && !BugzillaPreferences.getPassword().equals(""))
+// {
+// /*
+// * The UnsupportedEncodingException exception for
+// * URLEncoder.encode() should not be thrown, since every
+// * implementation of the Java platform is required to support
+// * the standard charset "UTF-8"
+// */
+// try {
+// urlText += "?GoAheadAndLogIn=1&Bugzilla_login=" + URLEncoder.encode(BugzillaPreferences.getUserName(), "UTF-8") + "&Bugzilla_password=" + URLEncoder.encode(BugzillaPreferences.getPassword(), "UTF-8");
+// } catch (UnsupportedEncodingException e) { }
+// }
+//
+// URL url = new URL(BugzillaRepository.getURL() + "/enter_bug.cgi" + urlText);
+//
+// // create a new input stream for getting the bug
+// in = new BufferedReader(new InputStreamReader(url.openStream()));
+// }
+// catch (Exception e) {
+// // If there was an IOException, then there was a problem connecting.
+// // If there was some other exception, then it was a problem not
+// // related to the server.
+// if (e instanceof IOException) {
+// canRead = false;
+// }
+// }
+//
+// // Close the BufferedReader if we opened one.
+// try {
+// if (in != null)
+// in.close();
+// } catch(IOException e) {}
+//
+// return canRead;
+// }
+//
+// public void dispose() {
+// if (serverPingJob != null) {
+// serverPingJob.stopPinging();
+// }
+// super.dispose();
+// }
+
+// source.addDragListener(new DragSourceListener() {
+//
+// public void dragStart(DragSourceEvent event) {
+// if (((StructuredSelection) viewer.getSelection()).getFirstElement() == null) {
+// event.doit = false;
+// }
+// }
+//
+// public void dragSetData(DragSourceEvent event) {
+// StructuredSelection selection = (StructuredSelection)viewer.getSelection();
+// ITask task = (ITask) selection.getFirstElement();
+// if (task != null) {
+// event.data = "" + task.getId();
+// } else {
+// event.data = " ";
+// }
+// }
+//
+// public void dragFinished(DragSourceEvent event) {
+// StructuredSelection selection = (StructuredSelection)viewer.getSelection();
+// if (selection.isEmpty()) {
+// return;
+// } else {
+// ITask task = (ITask) selection.getFirstElement();
+//
+// System.err.println(">>> got task: " + task + ">> " + );
+//
+// }
+// }
+//
+// });
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/views/ToolTipHandler.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/views/ToolTipHandler.java
new file mode 100644
index 000000000..5efee7529
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/views/ToolTipHandler.java
@@ -0,0 +1,240 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2005 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
+ *******************************************************************************/
+/**
+ * Copied from newsgroup, forwarded from Make Technologies
+ */
+
+package org.eclipse.mylar.tasks.ui.views;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.mylar.tasks.ITask;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.layout.*;
+import org.eclipse.swt.widgets.*;
+
+
+public class ToolTipHandler {
+ private Shell parentShell;
+
+ private Shell tipShell;
+
+ private Label tipLabelImage;
+ private Label tipLabelText;
+
+ private Widget tipWidget; // widget this tooltip is hovering over
+
+ protected Point tipPosition; // the position being hovered over on the
+ // Entire display
+
+ protected Point widgetPosition; // the position hovered over in the Widget;
+
+ /**
+ * Creates a new tooltip handler
+ *
+ * @param parent
+ * the parent Shell
+ */
+ public ToolTipHandler(Shell parent) {
+ final Display display = parent.getDisplay();
+ this.parentShell = parent;
+ tipShell = new Shell(parent, SWT.NONE);
+ GridLayout gridLayout = new GridLayout();
+ gridLayout.numColumns = 2;
+ gridLayout.marginWidth = 2;
+ gridLayout.marginHeight = 2;
+ tipShell.setLayout(gridLayout);
+ tipShell.setBackground(display
+ .getSystemColor(SWT.COLOR_INFO_BACKGROUND));
+
+ tipLabelImage = new Label(tipShell, SWT.NONE);
+ tipLabelImage.setForeground(display
+ .getSystemColor(SWT.COLOR_INFO_FOREGROUND));
+ tipLabelImage.setBackground(display
+ .getSystemColor(SWT.COLOR_INFO_BACKGROUND));
+
+ GridData imageGridData = new GridData(
+ GridData.HORIZONTAL_ALIGN_BEGINNING
+ | GridData.VERTICAL_ALIGN_BEGINNING);
+ tipLabelImage.setLayoutData(imageGridData);
+
+ tipLabelText = new Label(tipShell, SWT.NONE);
+ tipLabelText.setForeground(display
+ .getSystemColor(SWT.COLOR_INFO_FOREGROUND));
+ tipLabelText.setBackground(display
+ .getSystemColor(SWT.COLOR_INFO_BACKGROUND));
+
+ GridData textGridData = new GridData(GridData.FILL_HORIZONTAL
+ | GridData.VERTICAL_ALIGN_CENTER);
+ tipLabelText.setLayoutData(textGridData);
+ }
+
+ private ITask getTask(Object hoverObject) {
+ if (hoverObject instanceof Widget) {
+ Object data = ((Widget) hoverObject).getData();
+ if (data != null) {
+ if (data instanceof ITask) {
+ return (ITask) data;
+ } else if (data instanceof IAdaptable) {
+ return (ITask) ((IAdaptable) data).getAdapter(ITask.class);
+ }
+
+ }
+ }
+ return null;
+ }
+
+ protected String getToolTipText(Object object) {
+ ITask task = getTask(object);
+ if (task != null) {
+ return task.getToolTipText();
+ }
+
+ if (object instanceof Control) {
+ return (String) ((Control) object).getData("TIP_TEXT");
+ }
+ return null;
+ }
+
+ protected Image getToolTipImage(Object object) {
+ ITask projectNode = getTask(object);
+ if (projectNode != null) {
+ // TODO Code for determining image
+ }
+ if (object instanceof Control) {
+ return (Image) ((Control) object).getData("TIP_IMAGE");
+ }
+ return null;
+ }
+
+ protected Object getToolTipHelp(Object object) {
+ if (object instanceof Control) {
+ return (String) ((Control) object).getData("TIP_HELPTEXT");
+ }
+ return null;
+ }
+
+ /**
+ * Enables customized hover help for a specified control
+ *
+ * @control the control on which to enable hoverhelp
+ */
+ public void activateHoverHelp(final Control control) {
+ /*
+ * Get out of the way if we attempt to activate the control underneath
+ * the tooltip
+ */
+ control.addMouseListener(new MouseAdapter() {
+
+ @Override
+ public void mouseDown(MouseEvent e) {
+ if (tipShell.isVisible())
+ tipShell.setVisible(false);
+ }
+ });
+ /*
+ * Trap hover events to pop-up tooltip
+ */
+ control.addMouseTrackListener(new MouseTrackAdapter() {
+
+ @Override
+ public void mouseExit(MouseEvent e) {
+ if (tipShell.isVisible())
+ tipShell.setVisible(false);
+ tipWidget = null;
+ }
+
+ @Override
+ public void mouseHover(MouseEvent event) {
+ widgetPosition = new Point(event.x, event.y);
+ Widget widget = event.widget;
+ if (widget instanceof ToolBar) {
+ ToolBar w = (ToolBar) widget;
+ widget = w.getItem(widgetPosition);
+ }
+ if (widget instanceof Table) {
+ Table w = (Table) widget;
+ widget = w.getItem(widgetPosition);
+ }
+ if (widget instanceof Tree) {
+ Tree w = (Tree) widget;
+ widget = w.getItem(widgetPosition);
+ }
+ if (widget == null) {
+ tipShell.setVisible(false);
+ tipWidget = null;
+ return;
+ }
+ if (widget == tipWidget)
+ return;
+ tipWidget = widget;
+ tipPosition = control.toDisplay(widgetPosition);
+ String text = getToolTipText(widget);
+ Image image = getToolTipImage(widget);
+ if (text == null) {
+ return;
+ }
+ Control sourceControl = (Control) event.getSource();
+ sourceControl.setFocus();
+ tipLabelText.setText(text);
+ tipLabelImage.setImage(image); // accepts null
+ tipShell.pack();
+ setHoverLocation(tipShell, tipPosition);
+ tipShell.setVisible(true);
+ }
+ });
+ /*
+ * Trap F1 Help to pop up a custom help box
+ */
+ control.addHelpListener(new HelpListener() {
+ public void helpRequested(HelpEvent event) {
+ if (tipWidget == null)
+ return;
+ Object help = getToolTipHelp(tipWidget);
+ if (help == null)
+ return;
+ if (help.getClass() != String.class) {
+ return;
+ }
+ if (tipShell.isVisible()) {
+ tipShell.setVisible(false);
+ Shell helpShell = new Shell(parentShell, SWT.SHELL_TRIM);
+ helpShell.setLayout(new FillLayout());
+ Label label = new Label(helpShell, SWT.NONE);
+ label.setText((String) help);
+ helpShell.pack();
+ setHoverLocation(helpShell, tipPosition);
+ helpShell.open();
+ }
+ }
+ });
+ }
+
+ /**
+ * Sets the location for a hovering shell
+ *
+ * @param shell
+ * the object that is to hover
+ * @param position
+ * the position of a widget to hover over
+ * @return the top-left location for a hovering box
+ */
+ private void setHoverLocation(Shell shell, Point position) {
+ Rectangle displayBounds = shell.getDisplay().getBounds();
+ Rectangle shellBounds = shell.getBounds();
+ shellBounds.x = Math.max(Math.min(position.x, displayBounds.width
+ - shellBounds.width), 0);
+ shellBounds.y = Math.max(Math.min(position.y + 16, displayBounds.height
+ - shellBounds.height), 0);
+ shell.setBounds(shellBounds);
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/util/RelativePathUtil.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/util/RelativePathUtil.java
new file mode 100644
index 000000000..732fc557b
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/util/RelativePathUtil.java
@@ -0,0 +1,31 @@
+package org.eclipse.mylar.tasks.util;
+
+public class RelativePathUtil {
+ public static String findRelativePath(String baseDirectory, String filePath) {
+ if (filePath.startsWith(baseDirectory)) {
+ return filePath.substring(baseDirectory.length(), filePath.lastIndexOf('.'));
+ } else {
+ StringBuffer result = new StringBuffer(filePath.length());
+ String[] rootFolders = baseDirectory.split("/");
+ String[] pathFolders = filePath.split("/");
+ int diff = 0;
+ for (int i = 0; i < pathFolders.length; i++) {
+ if (!rootFolders[i].equals(pathFolders[i])) {
+ diff = i;
+ while (i < rootFolders.length) {
+ result.append('.');
+ result.append('.');
+ result.append('/');
+ i++;
+ }
+ while(diff < pathFolders.length - 1){
+ result.append(pathFolders[diff]);
+ diff++;
+ }
+ result.append(pathFolders[diff].substring(0, pathFolders[diff].lastIndexOf('.')));
+ }
+ }
+ return result.toString();
+ }
+ }
+}
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/util/TaskTest.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/util/TaskTest.java
new file mode 100644
index 000000000..389b64844
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/util/TaskTest.java
@@ -0,0 +1,148 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2005 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
+ *******************************************************************************/
+/*
+ * Created on Jan 24, 2005
+ */
+package org.eclipse.mylar.tasks.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author sminto
+ */
+public class TaskTest {
+ private int id = -1;
+
+ private String name = "";
+
+ private List<String> categories = new ArrayList<String>();
+
+ private List<TaskTest> taskList = new ArrayList<TaskTest>();
+
+ @Override
+ public String toString() {
+ return "Task";
+ }
+
+ public boolean isEqual(TaskTest otherTask) {
+
+ boolean result = true;
+ result = result && (this.id == otherTask.id);
+ if (!result)
+ System.out.println("ID's are different");
+ int compare = (this.name.compareTo(otherTask.name));
+ if (compare != 0)
+ result = false;
+ if (!result)
+ System.out.println("this: " + this.name + " otherName: "
+ + otherTask.name + " a");
+
+ if (this.categories.size() == otherTask.categories.size()) {
+ for (int i = 0; i < this.categories.size(); i++) {
+ compare = this.categories.get(i).compareTo(
+ otherTask.categories.get(i));
+ if (compare != 0) {
+ System.err.println(this.categories.get(i) + " vs "
+ + otherTask.categories.get(i));
+ result = false;
+ break;
+ }
+ }
+ } else {
+ System.err.println("Category length different");
+ System.err.println("length: " + this.categories.size()
+ + " VS Length: " + otherTask.categories.size());
+ result = false;
+ }
+ if (this.taskList.size() == otherTask.taskList.size()) {
+ for (int i = 0; i < this.taskList.size(); i++) {
+ result = result
+ && (this.taskList.get(i).isEqual(otherTask.taskList
+ .get(i)));
+ }
+ } else {
+ System.err.println("TaskList size is different!");
+ result = false;
+ }
+ return result;
+ }
+
+ public static void printTask(TaskTest t, String tab) {
+ System.out.println(tab + "TaskID: " + t.id);
+ System.out.println(tab + "Name: " + t.name);
+ System.out.println(tab + "Categories: ");
+ for (int i = 0; i < t.categories.size(); i++) {
+ System.out.println(tab + "\t " + t.categories.get(i));
+ }
+ for (int i = 0; i < t.taskList.size(); i++) {
+ printTask(t.taskList.get(i), tab + "\t");
+ }
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ // public List<String> getCategories() {
+ // return categories;
+ // }
+ // public void setCategories(List<String> categories) {
+ // this.categories = categories;
+ // }
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public void addCategory(String category) {
+ this.categories.add(category);
+ }
+
+ /**
+ * @return Returns the categories.
+ */
+ public List<String> getCategories() {
+ return categories;
+ }
+
+ /**
+ * @param categories The categories to set.
+ */
+ public void setCategories(List<String> categories) {
+ this.categories = categories;
+ }
+
+ /**
+ * @return Returns the taskList.
+ */
+ public List<TaskTest> getTaskList() {
+ return taskList;
+ }
+
+ /**
+ * @param taskList The taskList to set.
+ */
+ public void setTaskList(List<TaskTest> taskList) {
+ this.taskList = taskList;
+ }
+
+ public void addSubTask(TaskTest sub) {
+ this.taskList.add(sub);
+ }
+}

Back to the top