Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsminto2005-07-12 12:27:45 -0400
committersminto2005-07-12 12:27:45 -0400
commit3059c812ee26d2a7374a7d4d7d11b1227148a900 (patch)
tree6c551073d81506e7b5811186bdbd2887243a6d8e /org.eclipse.mylyn.bugzilla.ui
parentc8565b6360ad4e68817c124b17d637ed300577ce (diff)
downloadorg.eclipse.mylyn.tasks-3059c812ee26d2a7374a7d4d7d11b1227148a900.tar.gz
org.eclipse.mylyn.tasks-3059c812ee26d2a7374a7d4d7d11b1227148a900.tar.xz
org.eclipse.mylyn.tasks-3059c812ee26d2a7374a7d4d7d11b1227148a900.zip
Fixed Bug# 102916 split bugzilla into core and ui plugins
Diffstat (limited to 'org.eclipse.mylyn.bugzilla.ui')
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/.classpath6
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/META-INF/MANIFEST.MF20
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/about.html22
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/bugzilla_contexts.xml16
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/build.properties19
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/docs/html/Bugzilla.gifbin0 -> 1662 bytes
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/docs/html/book.html29
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/docs/html/start.html17
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/docs/html/start/bugEditor.html56
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/docs/html/start/bugWizard.html61
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/docs/html/start/bugzillaFavorites.html47
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/docs/html/start/bugzillaSearch.html66
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/docs/html/start/bugzillaUpdate.html46
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/Icons/remove-all.gifbin0 -> 117 bytes
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/Icons/remove.gifbin0 -> 97 bytes
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/bugzilla-bug-editor.pngbin0 -> 13188 bytes
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/bugzilla-favorites-window.pngbin0 -> 7191 bytes
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/bugzilla-search-context.pngbin0 -> 25071 bytes
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/bugzilla-search-dialog.pngbin0 -> 23762 bytes
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/bugzilla-search-sorting.pngbin0 -> 14970 bytes
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/bugzilla-searchresults.pngbin0 -> 12779 bytes
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/existing-bug-editor.pngbin0 -> 24719 bytes
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/new-bug-wizard-attributes.pngbin0 -> 10595 bytes
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/new-bug-wizard-products.pngbin0 -> 11036 bytes
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/new-bug-wizard.pngbin0 -> 14750 bytes
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/offline-reports.pngbin0 -> 7126 bytes
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/prefs.pngbin0 -> 17837 bytes
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/previous-searches.pngbin0 -> 12742 bytes
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/remember-query-overwrite.pngbin0 -> 6687 bytes
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/remember-query.pngbin0 -> 4326 bytes
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/saved-query-list.pngbin0 -> 5074 bytes
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/update-search-dialog.pngbin0 -> 23503 bytes
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/docs/html/start/offlineReports.html37
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/docs/html/start/saveQuery.html50
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/docs/html/start/setup.html52
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/docs/html/start/use.html19
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/icons/elcl16/bug-comment.gifbin0 -> 343 bytes
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/icons/elcl16/bug-dirty.gifbin0 -> 237 bytes
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/icons/elcl16/bug-favorite.gifbin0 -> 358 bytes
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/icons/elcl16/bug-new.gifbin0 -> 233 bytes
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/icons/elcl16/bug-search-new.gifbin0 -> 596 bytes
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/icons/elcl16/bug-search.gifbin0 -> 583 bytes
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/icons/elcl16/bug.gifbin0 -> 159 bytes
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/icons/openresult.gifbin0 -> 157 bytes
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/icons/remove-all.gifbin0 -> 117 bytes
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/icons/remove.gifbin0 -> 97 bytes
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/icons/selectAll.gifbin0 -> 149 bytes
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/icons/wizban/bug-wizard.gifbin0 -> 3895 bytes
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/plugin.xml100
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/BugzillaOpenStructure.java59
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/BugzillaResultMatchAdapter.java53
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/BugzillaTableContentProvider.java91
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/BugzillaUITools.java214
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/BugzillaUiPlugin.java4
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/FavoritesView.java576
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/OfflineView.java644
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/ViewBugzillaAction.java93
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/actions/BugzillaSortAction.java53
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/actions/DeleteOfflineReportAction.java70
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/actions/OpenBugsAction.java75
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/actions/ViewOfflineReportAction.java87
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/editor/AbstractBugEditor.java1550
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/editor/AbstractBugEditorInput.java67
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/editor/BugzillaEditorCopyAction.java39
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/editor/ExistingBugEditor.java745
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/editor/ExistingBugEditorInput.java86
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/editor/NewBugEditor.java361
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/editor/NewBugEditorInput.java48
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/favorites/actions/AbstractFavoritesAction.java56
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/favorites/actions/AddFavoriteAction.java113
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/favorites/actions/AddToFavoritesAction.java49
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/favorites/actions/DeleteFavoriteAction.java69
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/favorites/actions/ViewFavoriteAction.java58
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/outline/BugzillaOutlineComparer.java50
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/outline/BugzillaOutlineNode.java341
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/outline/BugzillaOutlinePage.java157
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/outline/BugzillaReportSelection.java170
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/query/GetQueryDialog.java203
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/query/SaveQueryDialog.java90
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/query/SavedQueryFile.java249
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/search/BugzillaSearchPage.java984
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/search/BugzillaSearchResultView.java229
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/wizard/AbstractBugWizard.java351
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/wizard/AbstractWizardDataPage.java621
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/wizard/AbstractWizardListPage.java213
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/wizard/NewBugWizard.java123
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/wizard/WizardAttributesPage.java30
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/wizard/WizardProductPage.java119
-rw-r--r--org.eclipse.mylyn.bugzilla.ui/toc.xml17
89 files changed, 9863 insertions, 7 deletions
diff --git a/org.eclipse.mylyn.bugzilla.ui/.classpath b/org.eclipse.mylyn.bugzilla.ui/.classpath
index 751c8f2e5..cc69b552e 100644
--- a/org.eclipse.mylyn.bugzilla.ui/.classpath
+++ b/org.eclipse.mylyn.bugzilla.ui/.classpath
@@ -2,6 +2,10 @@
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
- <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins">
+ <accessrules>
+ <accessrule kind="accessible" pattern="**/internal/**"/>
+ </accessrules>
+ </classpathentry>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/org.eclipse.mylyn.bugzilla.ui/META-INF/MANIFEST.MF b/org.eclipse.mylyn.bugzilla.ui/META-INF/MANIFEST.MF
index 2633aa0ce..b7431aebd 100644
--- a/org.eclipse.mylyn.bugzilla.ui/META-INF/MANIFEST.MF
+++ b/org.eclipse.mylyn.bugzilla.ui/META-INF/MANIFEST.MF
@@ -1,11 +1,27 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Ui Plug-in
-Bundle-SymbolicName: org.eclipse.mylar.bugzilla.ui
+Bundle-SymbolicName: org.eclipse.mylar.bugzilla.ui; singleton:=true
Bundle-Version: 0.3.1
Bundle-Activator: org.eclipse.mylar.bugzilla.ui.BugzillaUiPlugin
Bundle-Vendor: University of British Columbia
Bundle-Localization: plugin
Require-Bundle: org.eclipse.ui,
- org.eclipse.core.runtime
+ org.eclipse.core.runtime,
+ org.eclipse.core.resources,
+ org.eclipse.compare,
+ org.eclipse.search,
+ org.eclipse.ui.views,
+ org.eclipse.ui.ide,
+ org.eclipse.mylar.bugzilla.core
Eclipse-AutoStart: true
+Export-Package: org.eclipse.mylar.bugzilla.ui,
+ org.eclipse.mylar.bugzilla.ui.actions,
+ org.eclipse.mylar.bugzilla.ui.editor,
+ org.eclipse.mylar.bugzilla.ui.favorites,
+ org.eclipse.mylar.bugzilla.ui.favorites.actions,
+ org.eclipse.mylar.bugzilla.ui.outline,
+ org.eclipse.mylar.bugzilla.ui.query,
+ org.eclipse.mylar.bugzilla.ui.search,
+ org.eclipse.mylar.bugzilla.ui.wizard
+Bundle-ClassPath: bugzilla-ui.jar
diff --git a/org.eclipse.mylyn.bugzilla.ui/about.html b/org.eclipse.mylyn.bugzilla.ui/about.html
new file mode 100644
index 000000000..60ca57b4b
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.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.bugzilla.ui/bugzilla_contexts.xml b/org.eclipse.mylyn.bugzilla.ui/bugzilla_contexts.xml
new file mode 100644
index 000000000..78b55ca14
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/bugzilla_contexts.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<contexts>
+
+ <context id="bugzillaSearchContext">
+ <description>Enter a Bug ID or some text to search the Bugzilla database for related bugs.</description>
+ <topic href="docs/html/start/bugzillaSearch.html" label="Bugzilla Search"/>
+ <topic href="http://www.bugzilla.org" label="Bugzilla Website"/>
+ </context>
+
+ <context id="bugzillaEditorContext">
+ <description>View the bug with the "Preview" tab, and submit changes with the "Submit" tab.</description>
+ <topic href="docs/html/start/submitEditor.html" label="Bugzilla Editor"/>
+ </context>
+
+</contexts> \ No newline at end of file
diff --git a/org.eclipse.mylyn.bugzilla.ui/build.properties b/org.eclipse.mylyn.bugzilla.ui/build.properties
index 34d2e4d2d..40658c0e1 100644
--- a/org.eclipse.mylyn.bugzilla.ui/build.properties
+++ b/org.eclipse.mylyn.bugzilla.ui/build.properties
@@ -1,4 +1,17 @@
-source.. = src/
-output.. = bin/
bin.includes = META-INF/,\
- .
+ bugzilla-ui.jar,\
+ plugin.xml,\
+ icons/,\
+ docs/,\
+ about.html,\
+ toc.xml
+jars.compile.order = bugzilla-ui.jar
+source.bugzilla-ui.jar = src/
+output.bugzilla-ui.jar = bin/
+src.includes = src/,\
+ plugin.xml,\
+ META-INF/,\
+ about.html,\
+ docs/,\
+ icons/,\
+ toc.xml
diff --git a/org.eclipse.mylyn.bugzilla.ui/docs/html/Bugzilla.gif b/org.eclipse.mylyn.bugzilla.ui/docs/html/Bugzilla.gif
new file mode 100644
index 000000000..4d0c53d05
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/docs/html/Bugzilla.gif
Binary files differ
diff --git a/org.eclipse.mylyn.bugzilla.ui/docs/html/book.html b/org.eclipse.mylyn.bugzilla.ui/docs/html/book.html
new file mode 100644
index 000000000..10ffb6918
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/docs/html/book.html
@@ -0,0 +1,29 @@
+<!DOCTYPE doctype PUBLIC "-//w3c//dtd html 4.0 transitional//en">
+
+<html>
+
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <meta name="GENERATOR" content="Mozilla/4.73 [en] (Win98; U) [Netscape]">
+ <title>Bugzilla: Bugzilla Database Server Querying</title>
+</head>
+
+<body bgcolor="#c8ccad">
+ <font face="arial, lucida console" size="+3">
+ <table cellpadding="5">
+ <tr>
+ <td><img src="Bugzilla.gif" alt="Bugzilla"></td>
+ <td valign=bottom><u><b>Bugzilla Database Server Querying</b></u><br><br></td>
+ </tr>
+ </table>
+ </font>
+
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+
+</body>
+
+</html>
diff --git a/org.eclipse.mylyn.bugzilla.ui/docs/html/start.html b/org.eclipse.mylyn.bugzilla.ui/docs/html/start.html
new file mode 100644
index 000000000..e59973752
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/docs/html/start.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<html>
+
+<head>
+ <title>Getting Started</title>
+</head>
+
+<body>
+ <font face="arial, lucida console">
+ <h3>Getting Started</h3>
+ <br>
+ <p>Setting up and configuring Bugzilla, as well as instructions on its use.
+ </font>
+</body>
+
+</html>
diff --git a/org.eclipse.mylyn.bugzilla.ui/docs/html/start/bugEditor.html b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/bugEditor.html
new file mode 100644
index 000000000..d18582899
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/bugEditor.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<html>
+
+<head>
+ <title>Getting Started</title>
+</head>
+
+<body>
+ <font face="arial, lucida console">
+
+ <h3>Bugzilla Editor</h3>
+ <br>
+
+ <p><b>NOTE:</b> It is required that you have entered your Bugzilla login name and password
+ into the Bugzilla plugin preferences if you wish to create new bugs.
+ </p>
+
+ <p>The bug editor is opened when you view a bug. Here, you can edit the bug and submit the changes.
+ Once you are finished modifying the bug, you can click the "Submit" button at the
+ bottom of the editor to submit it to the Bugzilla server. Also, if you have made
+ changes to the bug, you can click the "Compare" button to inspect any changes between
+ your version of the bug and the online version.
+ </p>
+
+ <p>If you click the "Save" button, you will save the current version of the bug offline on your
+ hard-drive. The locally saved bug report will be accessible from the Offline Reports View. Any
+ time you try to view a bug with this id, your offline copy will be shown instead of the one
+ from the server. You can continue to make and save changes until you either delete the offline
+ version or submit your changes to the server.
+ </p>
+
+
+ <img src="./images/existing-bug-editor.png"><br>
+
+ <p>A bug saved from the New Bug Wizard will open in a simpler editor, since it does
+ not yet exist on a server, and so there are fewer tasks you can perform on it.
+ The most notable change is that there is no button to allow you to compare it with
+ the online version.
+ </p>
+
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+
+ </font>
+
+</body>
+</html> \ No newline at end of file
diff --git a/org.eclipse.mylyn.bugzilla.ui/docs/html/start/bugWizard.html b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/bugWizard.html
new file mode 100644
index 000000000..df9abf109
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/bugWizard.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<html>
+
+<head>
+ <title>Getting Started</title>
+</head>
+
+<body>
+ <font face="arial, lucida console">
+
+ <h3>Bugzilla New Bug Wizard</h3>
+ <br>
+
+ <p><b>NOTE:</b> It is required that you have entered your Bugzilla login name and password
+ into the Bugzilla plugin preferences if you wish to create new bugs.
+ </p>
+
+ <p>This Bugzilla plugin allows you to submit new bugs. To do this, go to "File->New->Other..."
+ and then choose "Bugzilla Wizards" and "New Bug Report".
+ </p>
+
+ <img src="./images/new-bug-wizard.png"><br>
+
+ <p>When you click "Next", you will be presented with a dialog that contains the list
+ of products that you can submit a bug for. Choose the product that you wish to submit
+ the bug for and click "Next". If there is only one product to choose from, then this
+ screen will be skipped over automatically.
+ </p>
+
+ <img src="./images/new-bug-wizard-products.png"><br>
+
+ <p>Now, you can modify the required attributes for the new bug. When you are finished,
+ you can submit the bug to the server. To do so, select "Submit bug report to the server."
+ and click the "Finish" button. This will submit your new bug to the server. If no
+ problems occured, the new bug will be opened in a Bugzilla editor so that you can review
+ your submission.
+ </p>
+
+ <img src="./images/new-bug-wizard-attributes.png"><br>
+
+ <p>Alternatively, you can save the bug offline on your hard-drive. Select "Save bug
+ report offline." and click the "Finish" button. The locally saved bug report will be
+ accessible from the Offline Reports View.</p>
+
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+
+ </font>
+
+</body>
+</html> \ No newline at end of file
diff --git a/org.eclipse.mylyn.bugzilla.ui/docs/html/start/bugzillaFavorites.html b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/bugzillaFavorites.html
new file mode 100644
index 000000000..d5ab217e7
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/bugzillaFavorites.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+
+<head>
+ <title>Getting Started</title>
+</head>
+
+<body>
+ <font face="arial, lucida console">
+
+ <h3>Bugzilla Favorites</h3>
+ <br>
+
+ <p>
+ Bugzilla has the ability to save bugs that are frequently accessed as a favorite
+ similar to a web browser. To do this, after a search is done right click on the bug
+ in the Search results window that you would like to save and choose "Mark Result as Favorite".
+ </p>
+
+ <img src="./images/bugzilla-search-context.png"><br>
+
+ <p>
+ A new window will now open that contains your specific list of favorites. To access
+ any of the bugs in the favorites view, just double click on the one that you want to
+ open and a Bugzilla bug editor will open containing the specific bug. To access the favorites
+ menu without adding a new bug, go to Window --> Show View --> Other... --> Bugzilla and click
+ on Bugzilla Favorites.
+ </p>
+
+ <img src="./images/bugzilla-favorites-window.png"><br>
+
+
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+
+ </font>
+</body>
+
+</html>
diff --git a/org.eclipse.mylyn.bugzilla.ui/docs/html/start/bugzillaSearch.html b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/bugzillaSearch.html
new file mode 100644
index 000000000..412906e5b
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/bugzillaSearch.html
@@ -0,0 +1,66 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+
+<head>
+ <title>Getting Started</title>
+</head>
+
+<body>
+ <font face="arial, lucida console">
+
+ <h3>Bugzilla Search</h3>
+ <br>
+
+ <p>
+ A Bugzilla Search tab has also been added to the Eclipse Search pane. You can enter
+ a bug ID or keywords in the text field and, if desired, limit the
+ search by selecting particular attributes that the bug must have.
+ Press Enter or click "Search" to proceed with the query to the Bugzilla server.
+ </p>
+
+ <img src="./images/bugzilla-search-dialog.png"><br>
+
+ <p>
+ If a Bugzilla ID was entered, that particular bug report will open in
+ the Bugzilla bug viewer.
+ </p>
+
+ <img src="./images/bugzilla-bug-editor.png"><br>
+
+ <p>
+ If a bug id was not entered, the results will appear in the Search results tab.
+ From here, you can open any of the items by double-clicking on them.
+ </p>
+
+ <img src="./images/bugzilla-searchresults.png">
+
+ <p>
+ By default, all of the results are sorted by Bug ID. They can also be sorted by
+ priority, severity, or status. To change the way the items are sorted, right-click
+ any bug and choose from the "Sort By" submenu.
+ </p>
+
+ <img src="./images/bugzilla-search-sorting.png">
+
+ <p>
+ Previous Bugzilla queries are accessible through the drop-down
+ history menu at the right end of the view's toolbar.
+ </p>
+
+ <img src="./images/previous-searches.png"><br>
+
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+
+ </font>
+</body>
+
+</html>
diff --git a/org.eclipse.mylyn.bugzilla.ui/docs/html/start/bugzillaUpdate.html b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/bugzillaUpdate.html
new file mode 100644
index 000000000..5b0dc1341
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/bugzillaUpdate.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+
+<head>
+ <title>Getting Started</title>
+</head>
+
+<body>
+ <font face="arial, lucida console">
+
+ <h3>Bugzilla Update</h3>
+ <br>
+
+ <p>
+ There is an update button near the bottom of the Bugzilla search dialog.
+ When this button is clicked, it will query the Bugzilla server for new
+ options. These options are for the bug attributes such as milestone, product, etc.
+ This allows Bugzilla to contain the latest information so that you can
+ perform a better search.
+ </p>
+
+ <img src="./images/bugzilla-search-dialog.png"><br>
+
+ <p>
+ When the update button is clicked, a status bar will appear at the bottom
+ of the search dialog so that you can see the progress of the update. Once the operation
+ is complete, the bug attributes will be updated to contain their latest values.
+ </p>
+
+ <img src="./images/update-search-dialog.png"><br>
+
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+
+ </font>
+</body>
+
+</html>
diff --git a/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/Icons/remove-all.gif b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/Icons/remove-all.gif
new file mode 100644
index 000000000..2c069ab3f
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/Icons/remove-all.gif
Binary files differ
diff --git a/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/Icons/remove.gif b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/Icons/remove.gif
new file mode 100644
index 000000000..12a9167c5
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/Icons/remove.gif
Binary files differ
diff --git a/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/bugzilla-bug-editor.png b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/bugzilla-bug-editor.png
new file mode 100644
index 000000000..192110478
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/bugzilla-bug-editor.png
Binary files differ
diff --git a/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/bugzilla-favorites-window.png b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/bugzilla-favorites-window.png
new file mode 100644
index 000000000..01f46ae83
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/bugzilla-favorites-window.png
Binary files differ
diff --git a/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/bugzilla-search-context.png b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/bugzilla-search-context.png
new file mode 100644
index 000000000..4c883b54d
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/bugzilla-search-context.png
Binary files differ
diff --git a/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/bugzilla-search-dialog.png b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/bugzilla-search-dialog.png
new file mode 100644
index 000000000..93098c8ea
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/bugzilla-search-dialog.png
Binary files differ
diff --git a/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/bugzilla-search-sorting.png b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/bugzilla-search-sorting.png
new file mode 100644
index 000000000..9ceb8f0e1
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/bugzilla-search-sorting.png
Binary files differ
diff --git a/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/bugzilla-searchresults.png b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/bugzilla-searchresults.png
new file mode 100644
index 000000000..5f538de0c
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/bugzilla-searchresults.png
Binary files differ
diff --git a/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/existing-bug-editor.png b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/existing-bug-editor.png
new file mode 100644
index 000000000..82ced89e4
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/existing-bug-editor.png
Binary files differ
diff --git a/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/new-bug-wizard-attributes.png b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/new-bug-wizard-attributes.png
new file mode 100644
index 000000000..027fea913
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/new-bug-wizard-attributes.png
Binary files differ
diff --git a/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/new-bug-wizard-products.png b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/new-bug-wizard-products.png
new file mode 100644
index 000000000..859533dba
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/new-bug-wizard-products.png
Binary files differ
diff --git a/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/new-bug-wizard.png b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/new-bug-wizard.png
new file mode 100644
index 000000000..d0e05339a
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/new-bug-wizard.png
Binary files differ
diff --git a/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/offline-reports.png b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/offline-reports.png
new file mode 100644
index 000000000..966384dcf
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/offline-reports.png
Binary files differ
diff --git a/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/prefs.png b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/prefs.png
new file mode 100644
index 000000000..c608502d1
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/prefs.png
Binary files differ
diff --git a/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/previous-searches.png b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/previous-searches.png
new file mode 100644
index 000000000..8bcb49d1b
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/previous-searches.png
Binary files differ
diff --git a/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/remember-query-overwrite.png b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/remember-query-overwrite.png
new file mode 100644
index 000000000..e16bbb8ca
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/remember-query-overwrite.png
Binary files differ
diff --git a/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/remember-query.png b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/remember-query.png
new file mode 100644
index 000000000..4d8ac7e1a
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/remember-query.png
Binary files differ
diff --git a/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/saved-query-list.png b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/saved-query-list.png
new file mode 100644
index 000000000..c7681ee9a
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/saved-query-list.png
Binary files differ
diff --git a/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/update-search-dialog.png b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/update-search-dialog.png
new file mode 100644
index 000000000..c28f642c6
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/images/update-search-dialog.png
Binary files differ
diff --git a/org.eclipse.mylyn.bugzilla.ui/docs/html/start/offlineReports.html b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/offlineReports.html
new file mode 100644
index 000000000..8c4f10fc5
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/offlineReports.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<html>
+
+<head>
+ <title>Getting Started</title>
+</head>
+
+<body>
+ <font face="arial, lucida console">
+
+ <h3>Offline Reports</h3>
+ <br>
+
+ <p>Bugzilla has the ability to save bugs offline on your hard-drive from both the bug editor and the
+ New Bug Wizard. The Offline Reports View is where the saved bug reports can be accessed. To show the
+ offline reports menu without saving a new bug, go to Window --> Show View --> Other... --> Bugzilla
+ and click on Bugzilla Offline Reports. To open an offline bug, simply double-click it.
+ </p>
+
+ <img src="./images/offline-reports.png"><br>
+
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+
+ </font>
+
+</body>
+</html> \ No newline at end of file
diff --git a/org.eclipse.mylyn.bugzilla.ui/docs/html/start/saveQuery.html b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/saveQuery.html
new file mode 100644
index 000000000..d80fa6f6d
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/saveQuery.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+
+<head>
+ <title>Getting Started</title>
+</head>
+
+<body>
+ <font face="arial, lucida console">
+
+ <h3>Save Query</h3>
+ <br>
+
+ <p>
+ There is a pair of buttons in the middle of the search dialog that allow you
+ to save frequently used queries and run these saved queries. The "Remember..." button
+ will save the current query, and the "Saved Queries..." button will display
+ a list of saved queries that you can choose from.
+ </p>
+
+ <img src="./images/bugzilla-search-dialog.png"><br>
+
+ <p>
+ When the "Remember..." button is clicked, a dialog will be displayed asking
+ you to name the query. Once "OK" is clicked, the query will be saved locally.
+ </p>
+
+ <img src="./images/remember-query.png"><br>
+
+ <p>
+ If you try to name a new query with an name that already exists, you will be prompted
+ asking whether to overwrite the currently saved query with the new one.
+ </p>
+
+ <img src="./images/remember-query-overwrite.png"><br>
+
+ <p>
+ When the "Saved Queries..." button is clicked on, another dialog will be displayed
+ containing a list of all of the queries that are currently saved on your computer. Select
+ the query that you wish to run and click "run" to execute it.
+ </p>
+
+ <img src="./images/saved-query-list.png"><br>
+
+ <br>
+
+ </font>
+</body>
+
+</html>
diff --git a/org.eclipse.mylyn.bugzilla.ui/docs/html/start/setup.html b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/setup.html
new file mode 100644
index 000000000..9f7c5a83f
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/setup.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<html>
+
+<head>
+ <title>Getting Started</title>
+</head>
+
+<body>
+ <font face="arial, lucida console">
+
+ <h3>Bugzilla Setup</h3>
+ <br>
+
+ <p>Although default settings are provided, the bugzilla server can be changed. To change the configuration, go to Preferences (in the Window menu) and open the Bugzilla tab.
+ </p>
+
+ <p>Here, the following parameters can be set:
+
+ <ol>
+ <li>URL of the Bugzilla server (set by default to the Eclipse Bugzilla server). This is used by the bug report
+ viewer to view Bugzilla items within Eclipse.</li>
+
+ <li>Your login name for the Bugzilla server (optional).</li>
+ <li>Your password for the Bugzilla server (optional).</li>
+
+ </ol>
+ <br>
+ Your Bugzilla login name and password are only required if you wish to create or modify bugs
+ using this Bugzilla plugin. Viewing bugs using this plugin does not require that a user name
+ and password be specified.
+ </p>
+
+ <img src="./images/prefs.png">
+ <br>
+ <br>
+
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+
+ </font>
+
+</body>
+</html> \ No newline at end of file
diff --git a/org.eclipse.mylyn.bugzilla.ui/docs/html/start/use.html b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/use.html
new file mode 100644
index 000000000..e1c0599f4
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/docs/html/start/use.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+
+<head>
+ <title>Getting Started</title>
+</head>
+
+<body>
+ <font face="arial, lucida console">
+
+ <h3>Using Bugzilla</h3>
+ <br>
+
+ <p>How to use the various functions of bugzilla, and interpret their results.
+
+ </font>
+</body>
+
+</html>
diff --git a/org.eclipse.mylyn.bugzilla.ui/icons/elcl16/bug-comment.gif b/org.eclipse.mylyn.bugzilla.ui/icons/elcl16/bug-comment.gif
new file mode 100644
index 000000000..97bbdf7dd
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/icons/elcl16/bug-comment.gif
Binary files differ
diff --git a/org.eclipse.mylyn.bugzilla.ui/icons/elcl16/bug-dirty.gif b/org.eclipse.mylyn.bugzilla.ui/icons/elcl16/bug-dirty.gif
new file mode 100644
index 000000000..8628881c7
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/icons/elcl16/bug-dirty.gif
Binary files differ
diff --git a/org.eclipse.mylyn.bugzilla.ui/icons/elcl16/bug-favorite.gif b/org.eclipse.mylyn.bugzilla.ui/icons/elcl16/bug-favorite.gif
new file mode 100644
index 000000000..6830e3348
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/icons/elcl16/bug-favorite.gif
Binary files differ
diff --git a/org.eclipse.mylyn.bugzilla.ui/icons/elcl16/bug-new.gif b/org.eclipse.mylyn.bugzilla.ui/icons/elcl16/bug-new.gif
new file mode 100644
index 000000000..312ca8f9d
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/icons/elcl16/bug-new.gif
Binary files differ
diff --git a/org.eclipse.mylyn.bugzilla.ui/icons/elcl16/bug-search-new.gif b/org.eclipse.mylyn.bugzilla.ui/icons/elcl16/bug-search-new.gif
new file mode 100644
index 000000000..8ac5e5706
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/icons/elcl16/bug-search-new.gif
Binary files differ
diff --git a/org.eclipse.mylyn.bugzilla.ui/icons/elcl16/bug-search.gif b/org.eclipse.mylyn.bugzilla.ui/icons/elcl16/bug-search.gif
new file mode 100644
index 000000000..b70606376
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/icons/elcl16/bug-search.gif
Binary files differ
diff --git a/org.eclipse.mylyn.bugzilla.ui/icons/elcl16/bug.gif b/org.eclipse.mylyn.bugzilla.ui/icons/elcl16/bug.gif
new file mode 100644
index 000000000..54525a775
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/icons/elcl16/bug.gif
Binary files differ
diff --git a/org.eclipse.mylyn.bugzilla.ui/icons/openresult.gif b/org.eclipse.mylyn.bugzilla.ui/icons/openresult.gif
new file mode 100644
index 000000000..2b645a88f
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/icons/openresult.gif
Binary files differ
diff --git a/org.eclipse.mylyn.bugzilla.ui/icons/remove-all.gif b/org.eclipse.mylyn.bugzilla.ui/icons/remove-all.gif
new file mode 100644
index 000000000..2c069ab3f
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/icons/remove-all.gif
Binary files differ
diff --git a/org.eclipse.mylyn.bugzilla.ui/icons/remove.gif b/org.eclipse.mylyn.bugzilla.ui/icons/remove.gif
new file mode 100644
index 000000000..12a9167c5
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/icons/remove.gif
Binary files differ
diff --git a/org.eclipse.mylyn.bugzilla.ui/icons/selectAll.gif b/org.eclipse.mylyn.bugzilla.ui/icons/selectAll.gif
new file mode 100644
index 000000000..e159862e3
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/icons/selectAll.gif
Binary files differ
diff --git a/org.eclipse.mylyn.bugzilla.ui/icons/wizban/bug-wizard.gif b/org.eclipse.mylyn.bugzilla.ui/icons/wizban/bug-wizard.gif
new file mode 100644
index 000000000..ec6d66ce3
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/icons/wizban/bug-wizard.gif
Binary files differ
diff --git a/org.eclipse.mylyn.bugzilla.ui/plugin.xml b/org.eclipse.mylyn.bugzilla.ui/plugin.xml
new file mode 100644
index 000000000..2aee1614d
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/plugin.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.0"?>
+<?eclipse version="3.0"?>
+<plugin>
+
+ <extension
+ id="org.eclipse.mylar.bugzilla.wizards"
+ name="Bug Wizard"
+ point="org.eclipse.ui.newWizards">
+ <category
+ name="Bugzilla Client"
+ id="org.eclipse.mylar.bugzilla.wizard.category">
+ </category>
+ <wizard
+ category="org.eclipse.mylar.bugzilla.wizard.category"
+ class="org.eclipse.mylar.bugzilla.ui.wizard.NewBugWizard"
+ icon="icons/elcl16/bug-new.gif"
+ id="org.eclipse.mylar.bugzilla.bugWizard"
+ name="New Bug Report">
+ <description>
+ Create a new bug report
+ </description>
+ </wizard>
+</extension>
+
+ <extension
+ id="org.eclipse.mylar.bugzilla.help.browser"
+ name="Bugzilla Help"
+ point="org.eclipse.help.toc">
+ <toc
+ file="toc.xml"
+ primary="true">
+ </toc>
+ </extension>
+ <extension
+ id="org.eclipse.mylar.bugzilla.ui"
+ name="Bugzilla Client Views"
+ point="org.eclipse.ui.views">
+ <category
+ name="Bugzilla"
+ id="org.eclipse.mylar.bugzilla.category"/>
+ <view
+ name="Bugzilla Offline Reports"
+ icon="icons/elcl16/bug-dirty.gif"
+ category="org.eclipse.mylar.bugzilla.category"
+ class="org.eclipse.mylar.bugzilla.ui.OfflineView"
+ id="org.eclipse.mylar.bugzilla.ui.offlineReportsView"/>
+ <view
+ name="Bugzilla Favorites"
+ icon="icons/elcl16/bug-favorite.gif"
+ category="org.eclipse.mylar.bugzilla.category"
+ class="org.eclipse.mylar.bugzilla.ui.FavoritesView"
+ id="org.eclipse.mylar.bugzilla.ui.favoritesView"/>
+ </extension>
+ <extension
+ point="org.eclipse.search.searchPages"
+ id="org.eclipse.mylar.bugzilla.core.search.searchPage"
+ name="Bugzilla Search Page">
+ <page
+ class="org.eclipse.mylar.bugzilla.ui.search.BugzillaSearchPage"
+ enabled="true"
+ icon="icons/elcl16/bug-search.gif"
+ id="org.eclipse.mylar.bugzilla.core.search.bugzillaSearchPage"
+ label="Bugzilla Search"
+ tabPosition="999"/>
+ </extension>
+ <extension
+ point="org.eclipse.ui.editors">
+ <editor
+ name="Bugzilla viewer"
+ icon="icons/elcl16/bug.gif"
+ class="org.eclipse.mylar.bugzilla.ui.editor.ExistingBugEditor"
+ id="org.eclipse.mylar.bugzilla.ui.existingBugEditor">
+ </editor>
+ <editor
+ name="Bugzilla viewer"
+ icon="icons/elcl16/bug.gif"
+ class="org.eclipse.mylar.bugzilla.ui.editor.NewBugEditor"
+ id="org.eclipse.mylar.bugzilla.ui.newBugEditor">
+ </editor>
+ </extension>
+ <extension
+ id="org.eclipse.mylar.bugzilla.help.context"
+ name="Bugzilla Context-sensitive Help"
+ point="org.eclipse.help.contexts">
+ <contexts
+ file="bugzilla_contexts.xml">
+ </contexts>
+ </extension>
+ <extension
+ id="BugzillaSearchPage"
+ point="org.eclipse.search.searchResultViewPages">
+ <viewPage
+ class="org.eclipse.mylar.bugzilla.ui.search.BugzillaSearchResultView"
+ searchResultClass="org.eclipse.mylar.bugzilla.core.search.BugzillaSearchResult"
+ id="org.eclipse.mylar.bugzilla.BugzillaSearchResultPage"/>
+ </extension>
+
+</plugin>
+
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/BugzillaOpenStructure.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/BugzillaOpenStructure.java
new file mode 100644
index 000000000..c902d5dd7
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/BugzillaOpenStructure.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 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.bugzilla.ui;
+
+/**
+ * Class to hold information about opening a bug report, such as what comment
+ * number to jump to
+ * @author Shawn Minto
+ */
+public class BugzillaOpenStructure{
+
+ private String server;
+ private int bugId;
+ private int commentNumber;
+
+ /**
+ * Constructor
+ * @param server The server that the bug resides on
+ * @param bugId The id of the bug
+ * @param commentNumber The comment number to jump to when opened, or -1
+ */
+ public BugzillaOpenStructure(String server, int bugId, int commentNumber){
+ this.bugId = bugId;
+ this.commentNumber = commentNumber;
+ this.server = server;
+ }
+
+ /**
+ * Get the bug id to open
+ * @return The bug id
+ */
+ public Integer getBugId() {
+ return bugId;
+ }
+
+ /**
+ * Get the comment number to jump to
+ * @return The comment number or -1 if none
+ */
+ public Integer getCommentNumber() {
+ return commentNumber;
+ }
+
+ /**
+ * Get the server the bug resides on
+ * @return The server url string
+ */
+ public String getServer(){
+ return server;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/BugzillaResultMatchAdapter.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/BugzillaResultMatchAdapter.java
new file mode 100644
index 000000000..233409273
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/BugzillaResultMatchAdapter.java
@@ -0,0 +1,53 @@
+package org.eclipse.mylar.bugzilla.ui;
+
+import org.eclipse.mylar.bugzilla.core.search.BugzillaSearchResult;
+import org.eclipse.mylar.bugzilla.core.search.IBugzillaResultEditorMatchAdapter;
+import org.eclipse.mylar.bugzilla.ui.editor.ExistingBugEditorInput;
+import org.eclipse.search.ui.text.AbstractTextSearchResult;
+import org.eclipse.search.ui.text.Match;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+
+public class BugzillaResultMatchAdapter implements
+ IBugzillaResultEditorMatchAdapter {
+
+
+ /** An empty array of matches */
+ private final Match[] EMPTY_ARR= new Match[0];
+
+
+ private BugzillaSearchResult result;
+
+ /* (non-Javadoc)
+ * @see org.eclipse.search.ui.text.IEditorMatchAdapter#isShownInEditor(org.eclipse.search.ui.text.Match, org.eclipse.ui.IEditorPart)
+ */
+ public boolean isShownInEditor(Match match, IEditorPart editor) {
+ if(result == null)
+ return false;
+ IEditorInput ei= editor.getEditorInput();
+ if (ei instanceof ExistingBugEditorInput) {
+ ExistingBugEditorInput bi= (ExistingBugEditorInput) ei;
+ return match.getElement().equals(bi.getBug());
+ }
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.search.ui.text.IEditorMatchAdapter#computeContainedMatches(org.eclipse.search.ui.text.AbstractTextSearchResult, org.eclipse.ui.IEditorPart)
+ */
+ public Match[] computeContainedMatches(AbstractTextSearchResult result, IEditorPart editor) {
+ if(result == null)
+ return EMPTY_ARR;
+ IEditorInput ei= editor.getEditorInput();
+ if (ei instanceof ExistingBugEditorInput) {
+ ExistingBugEditorInput bi= (ExistingBugEditorInput) ei;
+ return result.getMatches(bi.getBug());
+ }
+ return EMPTY_ARR;
+ }
+
+ public void setResult(BugzillaSearchResult result) {
+ this.result = result;
+ }
+
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/BugzillaTableContentProvider.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/BugzillaTableContentProvider.java
new file mode 100644
index 000000000..bbc4609a7
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/BugzillaTableContentProvider.java
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * Copyright (c) 2003 - 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.bugzilla.ui;
+
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.mylar.bugzilla.core.search.BugzillaContentProvider;
+import org.eclipse.mylar.bugzilla.core.search.BugzillaSearchResult;
+import org.eclipse.mylar.bugzilla.ui.search.BugzillaSearchResultView;
+import org.eclipse.search.internal.ui.SearchPreferencePage;
+
+/**
+ * This implementation of <code>BugzillaContentProvider</code> is used for the
+ * table view of a Bugzilla search result.
+ */
+public class BugzillaTableContentProvider extends BugzillaContentProvider implements IStructuredContentProvider {
+
+ /** The page the Bugzilla search results are displayed in */
+ private BugzillaSearchResultView bugPage;
+
+ /**
+ * Constructor
+ * @param page The page the Bugzilla search results are displayed in
+ */
+ public BugzillaTableContentProvider(BugzillaSearchResultView page) {
+ bugPage = page;
+ }
+
+ @Override
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ if (newInput instanceof BugzillaSearchResult) {
+ bugResult = (BugzillaSearchResult) newInput;
+ }
+ }
+
+ @Override
+ public void elementsChanged(Object[] updatedElements) {
+ TableViewer viewer= getViewer();
+ boolean tableLimited= SearchPreferencePage.isTableLimited();
+ for (int i= 0; i < updatedElements.length; i++) {
+ if (bugResult.getMatchCount(updatedElements[i]) > 0) {
+ if (viewer.testFindItem(updatedElements[i]) != null)
+ viewer.update(updatedElements[i], null);
+ else {
+ if (!tableLimited || viewer.getTable().getItemCount() < SearchPreferencePage.getTableLimit())
+ viewer.add(updatedElements[i]);
+ }
+ } else
+ viewer.remove(updatedElements[i]);
+ }
+ }
+
+ /**
+ * Returns the viewer the bug results are displayed in.
+ */
+ private TableViewer getViewer() {
+ return (TableViewer) bugPage.getViewer();
+ }
+
+ @Override
+ public void clear() {
+ getViewer().refresh();
+ }
+
+ /**
+ * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
+ */
+ public Object[] getElements(Object inputElement) {
+ if (inputElement instanceof BugzillaSearchResult) {
+ Object[] elements= ((BugzillaSearchResult)inputElement).getElements();
+ int tableLimit= SearchPreferencePage.getTableLimit();
+ if (SearchPreferencePage.isTableLimited() && elements.length > tableLimit) {
+ Object[] shownElements= new Object[tableLimit];
+ System.arraycopy(elements, 0, shownElements, 0, tableLimit);
+ return shownElements;
+ }
+ return elements;
+ }
+ return EMPTY_ARR;
+ }
+
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/BugzillaUITools.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/BugzillaUITools.java
new file mode 100644
index 000000000..88e4a184e
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/BugzillaUITools.java
@@ -0,0 +1,214 @@
+package org.eclipse.mylar.bugzilla.ui;
+
+import java.io.IOException;
+
+import javax.security.auth.login.LoginException;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.mylar.bugzilla.core.BugReport;
+import org.eclipse.mylar.bugzilla.core.BugzillaPlugin;
+import org.eclipse.mylar.bugzilla.core.IBugzillaBug;
+import org.eclipse.mylar.bugzilla.core.IBugzillaConstants;
+import org.eclipse.mylar.bugzilla.core.NewBugModel;
+import org.eclipse.mylar.bugzilla.ui.editor.ExistingBugEditor;
+import org.eclipse.mylar.bugzilla.ui.editor.ExistingBugEditorInput;
+import org.eclipse.mylar.bugzilla.ui.editor.NewBugEditorInput;
+import org.eclipse.search.internal.ui.SearchMessages;
+import org.eclipse.search.internal.ui.SearchPlugin;
+import org.eclipse.search.internal.ui.util.ExceptionHandler;
+import org.eclipse.search.ui.NewSearchUI;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PartInitException;
+
+public class BugzillaUITools {
+
+ /** The editor to use when a bug is opened */
+ private static IEditorPart fEditor;
+
+ /**
+ * Convenience method for opening a bug in an editor.
+ * @param id The bug id of the bug to open in the editor
+ */
+ public static boolean show(int id)
+ {
+ // determine if the editor is to be reused or not and call the appropriate
+ // function to show the bug
+ if (NewSearchUI.reuseEditor())
+ return showWithReuse(id);
+ else
+ return showWithoutReuse(id);
+ }
+
+ /**
+ * Show the bug in the same editor window
+ * @param id The id of the bug to show
+ */
+ private static boolean showWithReuse(int id)
+ {
+ // get the active page so that we can reuse it
+ IWorkbenchPage page = SearchPlugin.getActivePage();
+ try
+ {
+ // if we couldn't get a page, get out
+ if (page == null)
+ return true;
+
+ IEditorInput input = null;
+
+ // try to get an editor input on the bug
+ input = new ExistingBugEditorInput(id);
+
+ // check if we found a valid bug
+ if(((ExistingBugEditorInput)input).getBug() == null)
+ {
+ MessageDialog.openError(null, "No such bug", "No bug exists with this id");
+ return false;
+ }
+
+ // get the editor for the page
+ IEditorPart editor = page.findEditor(input);
+
+ if (editor == null)
+ {
+ // close the current editor if it is clean and open
+ if (fEditor != null && !fEditor.isDirty())
+ page.closeEditor(fEditor, false);
+
+ try
+ {
+ // try to open a new editor with the input bug, but don't activate it
+ editor= page.openEditor(input, IBugzillaConstants.EXISTING_BUG_EDITOR_ID, false);
+ }
+ catch (PartInitException ex)
+ {
+ // if there was a problem, handle it and log it, then get out of here
+ ExceptionHandler.handle(ex, SearchMessages.Search_Error_search_title, SearchMessages.Search_Error_search_message); //$NON-NLS-2$ //$NON-NLS-1$
+ BugzillaPlugin.log(ex.getStatus());
+ return false;
+ }
+
+ }
+ else
+ {
+ // if a editor is openon that bug, just bring it to the top
+ // of the editors
+ page.bringToTop(editor);
+ }
+
+ if (editor != null)
+ {
+ // if we have an editor, save it for later use
+ fEditor= editor;
+ }
+ }
+ catch(LoginException e)
+ {
+ 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(e);
+ }
+ catch(IOException e){
+ IStatus status = new MultiStatus( IBugzillaConstants.PLUGIN_ID, IStatus.ERROR, e.getClass().toString() + " occurred while opening the bug report. \n\nClick Details or see log for more information.", e);
+ IStatus s = new Status(IStatus.ERROR, IBugzillaConstants.PLUGIN_ID, IStatus.ERROR, e.getClass().toString() + ": ", e);
+ ((MultiStatus)status).add(s);
+ s = new Status (IStatus.ERROR, IBugzillaConstants.PLUGIN_ID, IStatus.ERROR, e.getMessage(), e);
+ ((MultiStatus)status).add(s);
+
+ //write error to log
+ BugzillaPlugin.log(status);
+
+ ErrorDialog.openError(null, "Bugzilla Error", null, status);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Show the bug in a new editor window
+ * @param id The id of the bug to show
+ */
+ private static boolean showWithoutReuse(int id)
+ {
+ // get the active workbench page
+ IWorkbenchPage page = SearchPlugin.getActivePage();
+ try
+ {
+ // if we couldn't get the page, get out of here
+ if (page == null)
+ return true;
+
+ IEditorInput input = null;
+ String editorId = IBugzillaConstants.EXISTING_BUG_EDITOR_ID;
+
+ // get a new editor input on the bug that we want to open
+ input = new ExistingBugEditorInput(id);
+
+ // check if we found a valid bug
+ if(((ExistingBugEditorInput)input).getBug() == null)
+ {
+ MessageDialog.openError(null, "No such bug", "No bug exists with this id");
+ return false;
+ }
+
+ try
+ {
+ // try to open an editor on the input bug
+ page.openEditor(input, editorId);
+ }
+ catch (PartInitException ex)
+ {
+ // if we have a problem, handle it, log it, and get out of here
+ ExceptionHandler.handle(ex, SearchMessages.Search_Error_search_title, SearchMessages.Search_Error_search_message); //$NON-NLS-2$ //$NON-NLS-1$
+ BugzillaPlugin.log(ex.getStatus());
+ return false;
+ }
+ }
+ catch(LoginException e)
+ {
+ 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(e);
+ }
+ catch(IOException e){
+ IStatus status = new MultiStatus( IBugzillaConstants.PLUGIN_ID, IStatus.ERROR, e.getClass().toString() + " occurred while opening the bug report. \n\nClick Details or see log for more information.", e);
+ IStatus s = new Status(IStatus.ERROR, IBugzillaConstants.PLUGIN_ID, IStatus.ERROR, e.getClass().toString() + ": ", e);
+ ((MultiStatus)status).add(s);
+ s = new Status (IStatus.ERROR, IBugzillaConstants.PLUGIN_ID, IStatus.ERROR, e.getMessage(), e);
+ ((MultiStatus)status).add(s);
+
+ //write error to log
+ BugzillaPlugin.log(status);
+
+ ErrorDialog.openError(null, "Bugzilla Error", null, status);
+ return false;
+ }
+ return true;
+ }
+
+ public static void closeEditor(IWorkbenchPage page, IBugzillaBug bug) {
+ if(bug instanceof NewBugModel){
+ IEditorInput input = new NewBugEditorInput((NewBugModel)bug);
+ IEditorPart bugEditor = page.findEditor(input);
+ if (bugEditor != null) {
+ page.closeEditor(bugEditor, false);
+ }
+ } else if(bug instanceof BugReport){
+ IEditorInput input = new ExistingBugEditorInput((BugReport)bug);
+ IEditorPart bugEditor = page.findEditor(input);
+ if (bugEditor != null) {
+ page.closeEditor(bugEditor, false);
+ IEditorPart compareEditor = page.findEditor(((ExistingBugEditor)bugEditor).getCompareInput());
+ if (compareEditor != null) {
+ page.closeEditor(compareEditor, false);
+ }
+ }
+ }
+
+ }
+
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/BugzillaUiPlugin.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/BugzillaUiPlugin.java
index ff1c1f843..37b2b640b 100644
--- a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/BugzillaUiPlugin.java
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/BugzillaUiPlugin.java
@@ -1,7 +1,8 @@
package org.eclipse.mylar.bugzilla.ui;
-import org.eclipse.ui.plugin.*;
import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.mylar.bugzilla.core.BugzillaPlugin;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.BundleContext;
/**
@@ -24,6 +25,7 @@ public class BugzillaUiPlugin extends AbstractUIPlugin {
*/
public void start(BundleContext context) throws Exception {
super.start(context);
+ BugzillaPlugin.setResultEditorMatchAdapter(new BugzillaResultMatchAdapter());
}
/**
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/FavoritesView.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/FavoritesView.java
new file mode 100644
index 000000000..b36ce2b1b
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/FavoritesView.java
@@ -0,0 +1,576 @@
+/*******************************************************************************
+ * Copyright (c) 2003 - 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.bugzilla.ui;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.GroupMarker;
+import org.eclipse.jface.action.IMenuListener;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.IStatusLineManager;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.viewers.ColumnLayoutData;
+import org.eclipse.jface.viewers.ColumnWeightData;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ITableLabelProvider;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableLayout;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.mylar.bugzilla.core.BugzillaPlugin;
+import org.eclipse.mylar.bugzilla.core.IBugzillaConstants;
+import org.eclipse.mylar.bugzilla.core.favorites.Favorite;
+import org.eclipse.mylar.bugzilla.core.favorites.FavoritesFile;
+import org.eclipse.mylar.bugzilla.ui.favorites.actions.AbstractFavoritesAction;
+import org.eclipse.mylar.bugzilla.ui.favorites.actions.DeleteFavoriteAction;
+import org.eclipse.mylar.bugzilla.ui.favorites.actions.ViewFavoriteAction;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.IMemento;
+import org.eclipse.ui.IViewSite;
+import org.eclipse.ui.IWorkbenchActionConstants;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.actions.ActionFactory;
+import org.eclipse.ui.part.ViewPart;
+
+
+/**
+ * A view that shows any bug marked as favorites.
+ */
+public class FavoritesView extends ViewPart {
+
+ private static Composite savedParent;
+
+ private IMemento savedMemento;
+
+ private static DeleteFavoriteAction remove;
+
+ public static DeleteFavoriteAction removeAll;
+
+ public static SelectAllAction selectAll;
+
+ private static ViewFavoriteAction open;
+
+ private Table table;
+
+ private MenuManager contextMenu;
+
+ private static TableViewer viewer;
+
+ private String[] columnHeaders = {
+ "Bug",
+ "Query",
+ "Date"
+ };
+
+ private ColumnLayoutData columnLayouts[] = {
+ new ColumnWeightData(10),
+ new ColumnWeightData(3),
+ new ColumnWeightData(5)
+ };
+
+ /**
+ * Constructor initializes favorites' source file initializes actions
+ */
+ public FavoritesView() {
+ super();
+ open = new ViewFavoriteAction(this);
+ selectAll = new SelectAllAction();
+ remove = new DeleteFavoriteAction(this, false);
+ removeAll = new DeleteFavoriteAction(this, true);
+ }
+
+ @Override
+ public void init(IViewSite site) throws PartInitException {
+ super.init(site);
+ }
+
+ /**
+ * Initializes this view with the given view site. A memento is passed to
+ * the view which contains a snapshot of the views state from a previous
+ * session.
+ */
+ @Override
+ public void init(IViewSite site, IMemento memento) throws PartInitException {
+ init(site);
+ this.savedMemento = memento;
+ }
+
+ @Override
+ public void createPartControl(Composite parent) {
+ FavoritesView.savedParent = parent;
+ setPartName("Bugzilla Favorites");
+ createTable();
+
+ viewer = new TableViewer(table);
+ viewer.setUseHashlookup(true);
+ createColumns();
+
+ GridData gd = new GridData(GridData.FILL_BOTH);
+ gd.verticalSpan = 20;
+ viewer.getTable().setLayoutData(gd);
+
+ viewer.setContentProvider(new FavoritesViewContentProvider(this));
+ viewer.setLabelProvider(new FavoritesViewLabelProvider());
+ viewer.setInput(BugzillaPlugin.getDefault().getFavorites().elements());
+
+ viewer.addSelectionChangedListener(new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ FavoritesView.this.widgetSelected(event);
+ }
+ });
+
+ fillToolbar();
+ createContextMenu();
+
+ Menu menu = contextMenu.createContextMenu(table);
+ table.setMenu(menu);
+
+ hookGlobalActions();
+ parent.layout();
+
+ // Restore state from the previous session.
+ restoreState();
+ }
+
+ @Override
+ public void setFocus() {
+ // don't need to do anything when the focus is set
+ }
+
+ private void createColumns() {
+ TableLayout layout = new TableLayout();
+ table.setLayout(layout);
+ table.setHeaderVisible(true);
+
+ for (int i = 0; i < columnHeaders.length; i++) {
+ TableColumn tc = new TableColumn(table, SWT.NONE, i);
+
+ tc.setText(columnHeaders[i]);
+ tc.pack();
+ tc.setResizable(columnLayouts[i].resizable);
+ layout.addColumnData(columnLayouts[i]);
+ }
+ }
+
+ private void createTable() {
+
+ table = new Table(savedParent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI | SWT.FULL_SELECTION);
+ table.setLinesVisible(true);
+
+ // Add action support for a double-click
+ table.addMouseListener(new MouseAdapter() {
+
+ @Override
+ public void mouseDoubleClick(MouseEvent e) {
+ open.run();
+ }
+ });
+ }
+
+ private void fillToolbar() {
+ IActionBars actionBars = getViewSite().getActionBars();
+ IToolBarManager toolbar = actionBars.getToolBarManager();
+
+ remove.setEnabled(false);
+ toolbar.add(remove);
+ toolbar.add(removeAll);
+ toolbar.add(new Separator());
+ toolbar.add(selectAll);
+
+ // create actions to handle the sorting of the favorites
+ sortByIDAction = new SortByAction(FavoritesFile.ID_SORT);
+ sortByIDAction.setText("by &Bug ID");
+ sortByIDAction.setToolTipText("Sorts by Bug number");
+
+ sortByPriorityAction = new SortByAction(FavoritesFile.PRIORITY_SORT);
+ sortByPriorityAction.setText("by &Priority");
+ sortByPriorityAction.setToolTipText("Sorts by priority of the bug");
+
+ sortBySeverityAction = new SortByAction(FavoritesFile.SEVERITY_SORT);
+ sortBySeverityAction.setText("by &Severity");
+ sortBySeverityAction.setToolTipText("Sorts by severity of the bug");
+
+ sortByStatusAction = new SortByAction(FavoritesFile.STATE_SORT);
+ sortByStatusAction.setText("by S&tatus");
+ sortByStatusAction.setToolTipText("Sorts by status of the bug");
+
+ // get the menu manager and create a submenu to contain sorting
+ IMenuManager menu = actionBars.getMenuManager();
+ IMenuManager submenu = new MenuManager("&Sort");
+
+ // add the sorting actions to the menu bar
+ menu.add(submenu);
+ submenu.add(sortByIDAction);
+ submenu.add(sortBySeverityAction);
+ submenu.add(sortByPriorityAction);
+ submenu.add(sortByStatusAction);
+
+ updateSortingState();
+ }
+
+ /**
+ * Function to make sure that the appropriate sort is checked
+ */
+ void updateSortingState() {
+ int curCriterion = FavoritesFile.lastSel;
+
+ sortByIDAction.setChecked(curCriterion == FavoritesFile.ID_SORT);
+ sortBySeverityAction.setChecked(curCriterion == FavoritesFile.SEVERITY_SORT);
+ sortByPriorityAction.setChecked(curCriterion == FavoritesFile.PRIORITY_SORT);
+ sortByStatusAction.setChecked(curCriterion == FavoritesFile.STATE_SORT);
+ viewer.setInput(viewer.getInput());
+ }
+
+ // Sorting actions for the favorites view
+ SortByAction sortByIDAction, sortBySeverityAction, sortByPriorityAction, sortByStatusAction;
+
+ /**
+ * Inner class to handle sorting
+ * @author Shawn Minto
+ */
+ class SortByAction extends Action {
+ /** The criteria to sort the favorites menu based on */
+ private int criterion;
+
+ /**
+ * Constructor
+ * @param criteria The criteria to sort the favorites menu based on
+ */
+ public SortByAction(int criteria) {
+ this.criterion = criteria;
+ }
+
+ /**
+ * Perform the sort
+ */
+ @Override
+ public void run() {
+ BugzillaPlugin.getDefault().getFavorites().sort(criterion);
+ updateSortingState();
+ }
+ }
+
+ /**
+ * Create context menu.
+ */
+ private void createContextMenu() {
+ contextMenu = new MenuManager("#FavoritesView");
+ contextMenu.setRemoveAllWhenShown(true);
+ contextMenu.addMenuListener(new IMenuListener() {
+ public void menuAboutToShow(IMenuManager manager) {
+ fillContextMenu(manager);
+ updateActionEnablement();
+ }
+ });
+
+ // Register menu for extension.
+ getSite().registerContextMenu("#FavoritesView", contextMenu, viewer);
+ }
+
+ /**
+ * Hook global actions
+ */
+ private void hookGlobalActions() {
+ IActionBars bars = getViewSite().getActionBars();
+ bars.setGlobalActionHandler(ActionFactory.SELECT_ALL.getId(), selectAll);
+ bars.setGlobalActionHandler(ActionFactory.DELETE.getId(), remove);
+ table.addKeyListener(new KeyAdapter() {
+
+ @Override
+ public void keyPressed(KeyEvent event) {
+ if (event.character == SWT.DEL && event.stateMask == 0 &&
+ remove.isEnabled()) {
+ remove.run();
+ }
+ }
+ });
+ }
+
+ /**
+ * Populate context menu
+ */
+ private void fillContextMenu(IMenuManager mgr) {
+ mgr.add(open);
+ mgr.add(new Separator());
+ mgr.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS));
+ mgr.add(new Separator());
+ mgr.add(remove);
+ mgr.add(new DeleteFavoriteAction(this, true));
+ mgr.add(new SelectAllAction());
+ }
+
+ /**
+ * Update action enablement depending on whether or not any items are selected.
+ * Displays name of current item in status bar.
+ */
+ public static void updateActionEnablement() {
+
+ boolean hasSelected = viewer.getTable().getSelectionCount() > 0;
+ remove.setEnabled(hasSelected);
+ open.setEnabled(hasSelected);
+
+ boolean hasItems = viewer.getTable().getItemCount() > 0;
+ removeAll.setEnabled(hasItems);
+ selectAll.setEnabled(hasItems);
+ }
+
+ @Override
+ public void saveState(IMemento memento) {
+ TableItem[] sel = table.getSelection();
+ if (sel.length == 0)
+ return;
+ memento = memento.createChild("selection");
+ for (int i = 0; i < sel.length; i++) {
+ memento.createChild("descriptor", new Integer(table.indexOf(sel[i])).toString());
+ }
+ }
+
+ private void restoreState() {
+ if (savedMemento == null)
+ return;
+ savedMemento = savedMemento.getChild("selection");
+ if (savedMemento != null) {
+ IMemento descriptors[] = savedMemento.getChildren("descriptor");
+ if (descriptors.length > 0) {
+ int[] objList = new int[descriptors.length];
+ for (int nX = 0; nX < descriptors.length; nX++) {
+ String id = descriptors[nX].getID();
+ objList[nX] = BugzillaPlugin.getDefault().getFavorites().find(Integer.valueOf(id).intValue());
+ }
+ table.setSelection(objList);
+ }
+ }
+ viewer.setSelection(viewer.getSelection(), true);
+ savedMemento = null;
+ updateActionEnablement();
+ }
+
+ /**
+ * Returns list of names of selected items.
+ */
+ public List<BugzillaOpenStructure> getBugIdsOfSelected() {
+ IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();//TableItem[] sel = table.getSelection();
+ List<?> sel = selection.toList();
+ List<BugzillaOpenStructure> Ids = new ArrayList<BugzillaOpenStructure>();
+
+ Iterator<?> itr = sel.iterator();
+ while (itr.hasNext()) {
+ Object o = itr.next();
+ if (o instanceof Favorite) {
+ Favorite entry = (Favorite) o;
+ Integer id = (Integer) entry.getAttributes().get(IBugzillaConstants.HIT_MARKER_ATTR_ID);
+ Ids.add(new BugzillaOpenStructure(entry.getServer(), id, -1));
+ }
+ }
+
+ return Ids;
+ }
+
+ /**
+ * Calls remove function in FavoritesFile
+ */
+ @SuppressWarnings("unchecked")
+ public void deleteSelectedFavorites() {
+ List<Favorite> selection = ((IStructuredSelection)viewer.getSelection()).toList();
+ BugzillaPlugin.getDefault().getFavorites().remove(selection);
+ viewer.setInput(viewer.getInput());
+ }
+
+ /**
+ * Removes all of the favorites in the FavoritesFile.
+ */
+ public void deleteAllFavorites() {
+ BugzillaPlugin.getDefault().getFavorites().removeAll();
+ viewer.setInput(viewer.getInput());
+ }
+
+ /**
+ * Refreshes the view.
+ */
+ public static void add() {
+ if (viewer != null)
+ viewer.setInput(viewer.getInput());
+ }
+
+
+ /**
+ * @see SelectionListener#widgetSelected(SelectionEvent)
+ */
+ @SuppressWarnings("unchecked")
+ public void widgetSelected(SelectionChangedEvent e) {
+
+ IStructuredSelection selection =
+ (IStructuredSelection) e.getSelection();
+
+ boolean enable = selection.size() > 0;
+ selectAll.setEnabled(enable);
+ remove.setEnabled(enable);
+ open.setEnabled(enable);
+
+ IStructuredSelection viewerSelection = (IStructuredSelection)viewer.getSelection();//TableItem[] sel = table.getSelection();
+ List<Favorite> sel = viewerSelection.toList();
+ if (sel.size() > 0) {
+ IStatusLineManager manager = this.getViewSite().getActionBars().getStatusLineManager();
+ manager.setMessage(sel.get(0).toString());// table.getItem(selected).getText(0));
+ }
+
+ updateActionEnablement();
+ }
+
+ /**
+ * Attempts to display this view on the workbench.
+ */
+ public static void checkWindow() {
+ if (savedParent == null || savedParent.isDisposed()) {
+ IWorkbenchWindow w = BugzillaPlugin.getDefault().getWorkbench()
+ .getActiveWorkbenchWindow();
+ if (w != null) {
+ IWorkbenchPage page = w.getActivePage();
+ if (page != null) {
+ try {
+ page.showView(IBugzillaConstants.PLUGIN_ID + ".ui.favoritesView");
+ } catch (PartInitException pie) {
+ BugzillaPlugin.log(pie.getStatus());
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Action class - "Select All"
+ */
+ public class SelectAllAction extends AbstractFavoritesAction {
+
+ public SelectAllAction() {
+ setToolTipText("Select all favorites");
+ setText("Select all");
+ setIcon("icons/selectAll.gif");
+ }
+
+ @Override
+ public void run() {
+ checkWindow();
+ table.selectAll();
+ viewer.setSelection(viewer.getSelection(), true);
+ updateActionEnablement();
+ }
+ }
+
+ private class FavoritesViewLabelProvider extends LabelProvider implements ITableLabelProvider {
+
+ /**
+ * Returns the label text for the given column of a recommendation in the table.
+ */
+ public String getColumnText(Object element, int columnIndex) {
+ if (element instanceof Favorite) {
+ Favorite f = (Favorite) element;
+ switch (columnIndex) {
+ case 0:
+ return f.toString();
+ case 1:
+ return f.getQuery();
+ case 2:
+ return f.getDate().toString();
+ default:
+ return "Undefined column text";
+ }
+ }
+ return ""; //$NON-NLS-1$
+ }
+
+ /*
+ * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnImage(java.lang.Object, int)
+ */
+ public Image getColumnImage(Object arg0, int arg1) {
+ return null;
+ }
+ }
+
+ public void refresh() {
+ // don't need to do anything to refresh
+ }
+
+ private class FavoritesViewContentProvider implements IStructuredContentProvider {
+
+ private List results;
+
+ /**
+ * The constructor.
+ */
+ public FavoritesViewContentProvider(FavoritesView taskList) {
+ // no setup to do
+ }
+
+ /**
+ * Returns the elements to display in the viewer
+ * when its input is set to the given element.
+ * These elements can be presented as rows in a table, items in a list, etc.
+ * The result is not modified by the viewer.
+ *
+ * @param inputElement the input element
+ * @return the array of elements to display in the viewer
+ */
+ public Object[] getElements(Object inputElement) {
+ if (results != null) {
+ return results.toArray();
+ }
+ else return null;
+ }
+
+ /**
+ * Notifies this content provider that a given viewer's input has been changed.
+ *
+ * @see IContentProvider#inputChanged
+ */
+ public void inputChanged(Viewer viewerChanged, Object oldInput, Object newInput) {
+ this.results = (List) newInput;
+
+ if (viewerChanged.getInput() != null) {
+ viewerChanged.getControl().getDisplay().syncExec(new Runnable() {
+ public void run() {
+ FavoritesView.this.refresh();
+ }
+ });
+ }
+ }
+
+ public void dispose() {
+ if (results != null)
+ results = null;
+ }
+ }
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/OfflineView.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/OfflineView.java
new file mode 100644
index 000000000..49faa0438
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/OfflineView.java
@@ -0,0 +1,644 @@
+/*******************************************************************************
+ * Copyright (c) 2003 - 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.bugzilla.ui;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.GroupMarker;
+import org.eclipse.jface.action.IMenuListener;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.IStatusLineManager;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.viewers.ColumnLayoutData;
+import org.eclipse.jface.viewers.ColumnWeightData;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ITableLabelProvider;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableLayout;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.mylar.bugzilla.core.BugzillaPlugin;
+import org.eclipse.mylar.bugzilla.core.IBugzillaBug;
+import org.eclipse.mylar.bugzilla.core.IBugzillaConstants;
+import org.eclipse.mylar.bugzilla.core.offline.AbstractOfflineReportsAction;
+import org.eclipse.mylar.bugzilla.core.offline.OfflineReportsFile;
+import org.eclipse.mylar.bugzilla.ui.actions.DeleteOfflineReportAction;
+import org.eclipse.mylar.bugzilla.ui.actions.ViewOfflineReportAction;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.IMemento;
+import org.eclipse.ui.IViewSite;
+import org.eclipse.ui.IWorkbenchActionConstants;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.actions.ActionFactory;
+import org.eclipse.ui.part.ViewPart;
+
+
+/**
+ * A view that displays any bugs that have been saved offline.
+ */
+public class OfflineView extends ViewPart {
+
+ private static Composite parent;
+
+ private IMemento savedMemento;
+
+ private static DeleteOfflineReportAction remove;
+
+ public static DeleteOfflineReportAction removeAll;
+
+ public static SelectAllAction selectAll;
+
+ private static ViewOfflineReportAction open;
+
+ private Table table;
+
+ private MenuManager contextMenu;
+
+ private static TableViewer viewer;
+
+ private String[] columnHeaders = {
+ "Bug",
+ "Summary",
+ "Description"
+ };
+
+ private ColumnLayoutData columnLayouts[] = {
+ new ColumnWeightData(3),
+ new ColumnWeightData(5),
+ new ColumnWeightData(10)
+ };
+
+ /**
+ * Constructor initializes OfflineReports' source file initializes actions
+ */
+ public OfflineView() {
+ super();
+ open = new ViewOfflineReportAction(this);
+ selectAll = new SelectAllAction();
+ remove = new DeleteOfflineReportAction(this, false);
+ removeAll = new DeleteOfflineReportAction(this, true);
+ }
+
+ @Override
+ public void init(IViewSite site) throws PartInitException {
+ super.init(site);
+ }
+
+ /**
+ * Initializes this view with the given view site. A memento is passed to
+ * the view which contains a snapshot of the views state from a previous
+ * session.
+ */
+ @Override
+ public void init(IViewSite site, IMemento memento) throws PartInitException {
+ init(site);
+ this.savedMemento = memento;
+ }
+
+ @Override
+ public void createPartControl(Composite parentComposite) {
+ OfflineView.parent = parentComposite;
+ setPartName("Bugzilla Offline Reports");
+ createTable();
+
+ viewer = new TableViewer(table);
+ viewer.setUseHashlookup(true);
+ createColumns();
+
+ GridData gd = new GridData(GridData.FILL_BOTH);
+ gd.verticalSpan = 20;
+ viewer.getTable().setLayoutData(gd);
+
+ viewer.setContentProvider(new OfflineReportsViewContentProvider());
+ viewer.setLabelProvider(new OfflineReportsViewLabelProvider());
+ viewer.setInput(BugzillaPlugin.getDefault().getOfflineReports().elements());
+
+ viewer.addSelectionChangedListener(new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ OfflineView.this.widgetSelected(event);
+ }
+ });
+
+ fillToolbar();
+ createContextMenu();
+
+ Menu menu = contextMenu.createContextMenu(table);
+ table.setMenu(menu);
+
+ hookGlobalActions();
+ parentComposite.layout();
+
+ // Restore state from the previous session.
+ restoreState();
+ }
+
+ @Override
+ public void setFocus() {
+ // don't need to do anything when we get focus
+ }
+
+ private void createColumns() {
+ TableLayout layout = new TableLayout();
+ table.setLayout(layout);
+ table.setHeaderVisible(true);
+
+ for (int i = 0; i < columnHeaders.length; i++) {
+ TableColumn tc = new TableColumn(table, SWT.NONE, i);
+
+ tc.setText(columnHeaders[i]);
+ tc.pack();
+ tc.setResizable(columnLayouts[i].resizable);
+ layout.addColumnData(columnLayouts[i]);
+ }
+ }
+
+ private void createTable() {
+
+ table = new Table(parent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI | SWT.FULL_SELECTION);
+ table.setLinesVisible(true);
+
+ // Add action support for a double-click
+ table.addMouseListener(new MouseAdapter() {
+
+ @Override
+ public void mouseDoubleClick(MouseEvent e) {
+ open.run();
+ }
+ });
+ }
+
+ private void fillToolbar() {
+ IActionBars actionBars = getViewSite().getActionBars();
+ IToolBarManager toolbar = actionBars.getToolBarManager();
+
+ remove.setEnabled(false);
+ toolbar.add(remove);
+ toolbar.add(removeAll);
+ toolbar.add(new Separator());
+ toolbar.add(selectAll);
+
+ // create actions to handle the sorting of the OfflineReports
+ sortByIDAction = new SortByAction(OfflineReportsFile.ID_SORT);
+ sortByIDAction.setText("by &Bug ID");
+ sortByIDAction.setToolTipText("Sorts by Bug number");
+
+ sortByTypeAction = new SortByAction(OfflineReportsFile.TYPE_SORT);
+ sortByTypeAction.setText("by &Bug Type");
+ sortByTypeAction.setToolTipText("Sorts by locally created/from server status");
+
+ // get the menu manager and create a submenu to contain sorting
+ IMenuManager menu = actionBars.getMenuManager();
+ IMenuManager submenu = new MenuManager("&Sort");
+
+ // add the sorting actions to the menu bar
+ menu.add(submenu);
+ submenu.add(sortByIDAction);
+ submenu.add(sortByTypeAction);
+
+ updateSortingState();
+ }
+
+ /**
+ * Function to make sure that the appropriate sort is checked
+ */
+ void updateSortingState() {
+ int curCriterion = OfflineReportsFile.lastSel;
+
+ sortByIDAction.setChecked(curCriterion == OfflineReportsFile.ID_SORT);
+ sortByTypeAction.setChecked(curCriterion == OfflineReportsFile.TYPE_SORT);
+ viewer.setInput(viewer.getInput());
+ }
+
+ // Sorting actions for the OfflineReports view
+ SortByAction sortByIDAction, sortByTypeAction/*, sortBySeverityAction, sortByPriorityAction, sortByStatusAction*/;
+
+ /**
+ * Inner class to handle sorting
+ * @author Shawn Minto
+ */
+ class SortByAction extends Action {
+ /** The criteria to sort the OfflineReports menu based on */
+ private int criterion;
+
+ /**
+ * Constructor
+ * @param criteria The criteria to sort the OfflineReports menu based on
+ */
+ public SortByAction(int criteria) {
+ this.criterion = criteria;
+ }
+
+ /**
+ * Perform the sort
+ */
+ @Override
+ public void run() {
+ BugzillaPlugin.getDefault().getOfflineReports().sort(criterion);
+ updateSortingState();
+ }
+ }
+
+ /**
+ * Create context menu.
+ */
+ private void createContextMenu() {
+ contextMenu = new MenuManager("#OfflineReportsView");
+ contextMenu.setRemoveAllWhenShown(true);
+ contextMenu.addMenuListener(new IMenuListener() {
+ public void menuAboutToShow(IMenuManager manager) {
+ fillContextMenu(manager);
+ updateActionEnablement();
+ }
+ });
+
+ // Register menu for extension.
+ getSite().registerContextMenu("#OfflineReportsView", contextMenu, viewer);
+ }
+
+ /**
+ * Hook global actions
+ */
+ private void hookGlobalActions() {
+ IActionBars bars = getViewSite().getActionBars();
+ bars.setGlobalActionHandler(ActionFactory.SELECT_ALL.getId(), selectAll);
+ bars.setGlobalActionHandler(ActionFactory.DELETE.getId(), remove);
+ table.addKeyListener(new KeyAdapter() {
+
+ @Override
+ public void keyPressed(KeyEvent event) {
+ if (event.character == SWT.DEL && event.stateMask == 0 &&
+ remove.isEnabled()) {
+ remove.run();
+ }
+ }
+ });
+ }
+
+ /**
+ * Populate context menu
+ */
+ private void fillContextMenu(IMenuManager mgr) {
+ mgr.add(open);
+ mgr.add(new Separator());
+ mgr.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS));
+ mgr.add(new Separator());
+ mgr.add(remove);
+ mgr.add(new DeleteOfflineReportAction(this, true));
+ mgr.add(new SelectAllAction());
+ }
+
+ /**
+ * Update action enablement depending on whether or not any items are selected.
+ * Displays name of current item in status bar.
+ */
+ public static void updateActionEnablement() {
+
+ boolean hasSelected = viewer.getTable().getSelectionCount() > 0;
+ remove.setEnabled(hasSelected);
+ open.setEnabled(hasSelected);
+
+ boolean hasItems = viewer.getTable().getItemCount() > 0;
+ removeAll.setEnabled(hasItems);
+ selectAll.setEnabled(hasItems);
+ }
+
+ @Override
+ public void saveState(IMemento memento) {
+ TableItem[] sel = table.getSelection();
+ if (sel.length == 0)
+ return;
+ memento = memento.createChild("selection");
+ for (int i = 0; i < sel.length; i++) {
+ memento.createChild("descriptor", new Integer(table.indexOf(sel[i])).toString());
+ }
+ }
+
+ private void restoreState() {
+ if (savedMemento == null)
+ return;
+ savedMemento = savedMemento.getChild("selection");
+ if (savedMemento != null) {
+ IMemento descriptors[] = savedMemento.getChildren("descriptor");
+ if (descriptors.length > 0) {
+ int[] objList = new int[descriptors.length];
+ for (int nX = 0; nX < descriptors.length; nX++) {
+ String id = descriptors[nX].getID();
+ objList[nX] = BugzillaPlugin.getDefault().getOfflineReports().find(Integer.valueOf(id).intValue());
+ }
+ table.setSelection(objList);
+ }
+ }
+ viewer.setSelection(viewer.getSelection(), true);
+ savedMemento = null;
+ updateActionEnablement();
+ }
+
+ /**
+ * Returns list of names of selected items.
+ */
+ @SuppressWarnings("unchecked")
+ public List<Integer> getBugIdsOfSelected() {
+ IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();//TableItem[] sel = table.getSelection();
+ List<Object> sel = selection.toList();
+ List<Integer> Ids = new ArrayList<Integer>();
+
+ Iterator<Object> itr = sel.iterator();
+ while (itr.hasNext()) {
+ Object o = itr.next();
+ if (o instanceof IBugzillaBug) {
+ IBugzillaBug entry = (IBugzillaBug) o;
+ Integer id = entry.getId();
+ if (!entry.isLocallyCreated()) {
+ Ids.add(id);
+ }
+ }
+ }
+
+ return Ids;
+ }
+
+ /**
+ * @return List of selected offline bug reports.
+ */
+ @SuppressWarnings("unchecked")
+ public List<IBugzillaBug> getSelectedBugs() {
+ List<Object> selection = ((IStructuredSelection)viewer.getSelection()).toList();
+ List<IBugzillaBug> bugs = new ArrayList<IBugzillaBug>();
+ for (Iterator<Object> iter = selection.iterator(); iter.hasNext();) {
+ Object obj = iter.next();
+ if (obj instanceof IBugzillaBug) {
+ bugs.add((IBugzillaBug)obj);
+ }
+ }
+ return bugs;
+ }
+
+ /**
+ * Closes any open editors of the given offline reports.
+ * @param reports The list of offline reports that need their editors closed.
+ */
+ protected void closeOfflineReports(List<IBugzillaBug> reports) {
+ if (reports == null)
+ return;
+
+ IWorkbenchPage page = BugzillaPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getActivePage();
+
+ // if we couldn't get the page, get out of here
+ if (page == null)
+ return;
+
+ for (Iterator<IBugzillaBug> iter = reports.iterator(); iter.hasNext();) {
+ IBugzillaBug bug = iter.next();
+ BugzillaUITools.closeEditor(page, bug);
+ }
+ }
+ /**
+ * Refreshes the view.
+ */
+ public static void add() {
+ if (viewer != null)
+ viewer.setInput(viewer.getInput());
+ }
+
+ /**
+ * Calls remove function in OfflineReportsFile
+ */
+ @SuppressWarnings("unchecked")
+ public void deleteSelectedOfflineReports() {
+ List<IBugzillaBug> selection = ((IStructuredSelection)viewer.getSelection()).toList();
+ closeOfflineReports(selection);
+ BugzillaPlugin.getDefault().getOfflineReports().remove(selection);
+ viewer.setInput(viewer.getInput());
+ }
+
+ /**
+ * Removes all of the offline reports in the OfflineReportsFile.
+ */
+ public void deleteAllOfflineReports() {
+ closeOfflineReports(BugzillaPlugin.getDefault().getOfflineReports().elements());
+ BugzillaPlugin.getDefault().getOfflineReports().removeAll();
+ viewer.setInput(viewer.getInput());
+ }
+
+ /**
+ * Saves the given report to the offlineReportsFile, or, if it already
+ * exists in the file, updates it.
+ *
+ * @param bug
+ * The bug to add/update.
+ */
+ public static void saveOffline(IBugzillaBug bug) {
+ OfflineReportsFile file = BugzillaPlugin.getDefault().getOfflineReports();
+ // If there is already an offline report for this bug, update the file.
+ if (bug.isSavedOffline()) {
+ file.update();
+ }
+ // If this bug has not been saved offline before, add it to the file.
+ else {
+ // If there is already an offline report with the same id, don't save this report.
+ if (file.find(bug.getId()) >= 0) {
+ MessageDialog.openInformation(null, "Bug's Id is already used.", "There is already a bug saved offline with an identical id.");
+ return;
+ }
+ file.add(bug);
+ bug.setOfflineState(true);
+ file.sort(OfflineReportsFile.lastSel);
+ }
+ OfflineView.checkWindow();
+ OfflineView.add();
+ }
+
+ public static List<IBugzillaBug> getOfflineBugs(){
+ OfflineReportsFile file = BugzillaPlugin.getDefault().getOfflineReports();
+ return file.elements();
+ }
+
+ /**
+ * Removes the given report from the offlineReportsFile.
+ *
+ * @param bug
+ * The report to remove.
+ */
+ public static void removeReport(IBugzillaBug bug) {
+ ArrayList<IBugzillaBug> bugList = new ArrayList<IBugzillaBug>();
+ bugList.add(bug);
+ BugzillaPlugin.getDefault().getOfflineReports().remove(bugList);
+ if(viewer != null)
+ viewer.setInput(viewer.getInput());
+ }
+
+ /**
+ * @see SelectionListener#widgetSelected(SelectionEvent)
+ */
+ @SuppressWarnings("unchecked")
+ public void widgetSelected(SelectionChangedEvent e) {
+
+ IStructuredSelection selection =
+ (IStructuredSelection) e.getSelection();
+
+ boolean enable = selection.size() > 0;
+ selectAll.setEnabled(enable);
+ remove.setEnabled(enable);
+ open.setEnabled(enable);
+
+ IStructuredSelection viewerSelection = (IStructuredSelection)viewer.getSelection();//TableItem[] sel = table.getSelection();
+ List<IBugzillaBug> sel = viewerSelection.toList();
+ if (sel.size() > 0) {
+ IStatusLineManager manager = this.getViewSite().getActionBars().getStatusLineManager();
+ manager.setMessage(sel.get(0).toString());
+ }
+
+ updateActionEnablement();
+ }
+
+ /**
+ * Attempts to display this view on the workbench.
+ */
+ public static void checkWindow() {
+ if (parent == null || parent.isDisposed()) {
+ IWorkbenchWindow w = BugzillaPlugin.getDefault().getWorkbench()
+ .getActiveWorkbenchWindow();
+ if (w != null) {
+ IWorkbenchPage page = w.getActivePage();
+ if (page != null) {
+ try {
+ page.showView(IBugzillaConstants.PLUGIN_ID +".ui.offlineReportsView");
+ } catch (PartInitException pie) {
+ BugzillaPlugin.log(pie.getStatus());
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Action class - "Select All"
+ */
+ public class SelectAllAction extends AbstractOfflineReportsAction {
+
+ public SelectAllAction() {
+ setToolTipText("Select all offline Bugzilla reports");
+ setText("Select all");
+ setIcon("icons/selectAll.gif");
+ }
+
+ @Override
+ public void run() {
+ checkWindow();
+ table.selectAll();
+ viewer.setSelection(viewer.getSelection(), true);
+ updateActionEnablement();
+ }
+ }
+
+ private class OfflineReportsViewLabelProvider extends LabelProvider implements ITableLabelProvider {
+
+ /**
+ * Returns the label text for the given column of a recommendation in the table.
+ */
+ public String getColumnText(Object element, int columnIndex) {
+ if (element instanceof IBugzillaBug) {
+ IBugzillaBug f = (IBugzillaBug) element;
+ switch (columnIndex) {
+ case 0:
+ return f.getLabel();
+ case 1:
+ return f.getSummary();
+ case 2:
+ return f.getDescription();
+ default:
+ return "Undefined column text";
+ }
+ }
+ return ""; //$NON-NLS-1$
+ }
+
+ /*
+ * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnImage(java.lang.Object, int)
+ */
+ public Image getColumnImage(Object arg0, int arg1) {
+ return null;
+ }
+ }
+
+ public static void refresh() {
+ if(viewer != null)
+ viewer.refresh();
+ }
+
+ private class OfflineReportsViewContentProvider implements IStructuredContentProvider {
+
+ private List results;
+
+ /**
+ * The constructor.
+ */
+ public OfflineReportsViewContentProvider() {
+ // no setup needed
+ }
+
+ /**
+ * Returns the elements to display in the viewer
+ * when its input is set to the given element.
+ * These elements can be presented as rows in a table, items in a list, etc.
+ * The result is not modified by the viewer.
+ *
+ * @param inputElement the input element
+ * @return the array of elements to display in the viewer
+ */
+ public Object[] getElements(Object inputElement) {
+ if (results != null) {
+ return results.toArray();
+ }
+ else return null;
+ }
+
+ /**
+ * Notifies this content provider that a given viewer's input has been changed.
+ *
+ * @see IContentProvider#inputChanged
+ */
+ public void inputChanged(Viewer inputViewer, Object oldInput, Object newInput) {
+ this.results = (List) newInput;
+ }
+
+ public void dispose() {
+ if (results != null)
+ results = null;
+ }
+ }
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/ViewBugzillaAction.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/ViewBugzillaAction.java
new file mode 100644
index 000000000..1c6373829
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/ViewBugzillaAction.java
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * Copyright (c) 2003 - 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.bugzilla.ui;
+
+import java.io.IOException;
+import java.util.Iterator;
+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.jface.dialogs.MessageDialog;
+import org.eclipse.mylar.bugzilla.core.BugzillaPlugin;
+import org.eclipse.mylar.bugzilla.core.IBugzillaConstants;
+import org.eclipse.mylar.bugzilla.ui.editor.AbstractBugEditor;
+import org.eclipse.mylar.bugzilla.ui.editor.ExistingBugEditorInput;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.progress.UIJob;
+
+
+/**
+ * Action performed when the bugs are supposed to be displayed in the editor window
+ * from the favorites list
+ */
+public class ViewBugzillaAction extends UIJob
+{
+
+ /** List of bugs to be displayed */
+ private List<BugzillaOpenStructure> bugs;
+
+ /**
+ * Constructor
+ * @param name The job name
+ * @param bugs List of bugs to be displayed
+ */
+ public ViewBugzillaAction(String name, List<BugzillaOpenStructure> bugs)
+ {
+ super(name);
+ this.bugs = bugs;
+ }
+
+ @Override
+ public IStatus runInUIThread(IProgressMonitor monitor) {
+ IWorkbenchPage page = BugzillaPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getActivePage();
+
+ // go through each bug and get its id
+ for (Iterator<BugzillaOpenStructure> it = bugs.iterator(); it.hasNext(); ) {
+ BugzillaOpenStructure bos = it.next();
+ Integer bugId = bos.getBugId();
+ Integer commentNumber = bos.getCommentNumber();
+
+ try {
+ // try to open a new editor on the bug
+ ExistingBugEditorInput editorInput = new ExistingBugEditorInput(bugId.intValue());
+
+ // if the bug could not be found, then tell the user that the server settings are wrong
+ if (editorInput.getBug() == null) {
+ MessageDialog.openError(null, "Server Setting Error", "Incorrect server set for the bug.");
+ }
+ else {
+ AbstractBugEditor abe = (AbstractBugEditor)page.openEditor(editorInput, IBugzillaConstants.EXISTING_BUG_EDITOR_ID);
+ if(commentNumber == 0){
+ abe.selectDescription();
+ }else if(commentNumber == 1){
+ abe.select(commentNumber);
+ } else {
+ abe.select(commentNumber-1);
+ }
+ }
+ } catch (LoginException e) {
+ 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(e);
+ } catch (PartInitException e) {
+ BugzillaPlugin.log(e);
+ } catch (IOException e) {
+ BugzillaPlugin.getDefault().logAndShowExceptionDetailsDialog(e,
+ "occurred while opening the bug report.", "Bugzilla Error");
+ }
+ }
+ return new Status(IStatus.OK, IBugzillaConstants.PLUGIN_ID, IStatus.OK, "", null);
+ }
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/actions/BugzillaSortAction.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/actions/BugzillaSortAction.java
new file mode 100644
index 000000000..495dce05f
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/actions/BugzillaSortAction.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2003 - 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.bugzilla.ui.actions;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.mylar.bugzilla.ui.search.BugzillaSearchResultView;
+
+/**
+ * This class sorts Bugzilla search results by a supplied category.
+ */
+public class BugzillaSortAction extends Action {
+
+ /** The category that this class sorts Bugzilla search results by. */
+ private int bugSortOrder;
+
+ /** The view where the Bugzilla search results are displayed. */
+ private BugzillaSearchResultView bugPage;
+
+ /**
+ * Constructor
+ * @param label The string used as the text for the action, or null if there is no text
+ * @param page The view where the Bugzilla search results are displayed.
+ * @param sortOrder The category that this class sorts Bugzilla search results by
+ */
+ public BugzillaSortAction(String label, BugzillaSearchResultView page, int sortOrder) {
+ super(label);
+ bugPage= page;
+ bugSortOrder= sortOrder;
+ }
+
+ /**
+ * Reorder the Bugzilla search results.
+ */
+ @Override
+ public void run() {
+ bugPage.setSortOrder(bugSortOrder);
+ }
+
+ /**
+ * Returns the category that this class sorts Bugzilla search results by.
+ */
+ public int getSortOrder() {
+ return bugSortOrder;
+ }
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/actions/DeleteOfflineReportAction.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/actions/DeleteOfflineReportAction.java
new file mode 100644
index 000000000..93be254d1
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/actions/DeleteOfflineReportAction.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Copyright (c) 2003 - 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.bugzilla.ui.actions;
+
+import org.eclipse.mylar.bugzilla.core.offline.AbstractOfflineReportsAction;
+import org.eclipse.mylar.bugzilla.ui.OfflineView;
+
+/**
+ * Action of removing a bookmark
+ */
+public class DeleteOfflineReportAction extends AbstractOfflineReportsAction
+{
+ /** The instance of the offlineReports view */
+ private OfflineView view;
+
+ /** True if all of the bookmarks are to be deleted */
+ private boolean deleteAll;
+
+ /**
+ * Constructor
+ * @param offlineReportsView The offlineReports view being used
+ * @param deleteAllOfflineReports <code>true</code> if all of the offlineReports should be deleted, else <code>false</code>
+ */
+ public DeleteOfflineReportAction(OfflineView offlineReportsView, boolean deleteAllOfflineReports)
+ {
+ deleteAll = deleteAllOfflineReports;
+
+ // set the appropriate icons and tool tips for the action depending
+ // on whether it will delete all items or not
+ if (deleteAll)
+ {
+ setToolTipText("Remove all offline reports");
+ setText("Remove all");
+ setIcon("icons/remove-all.gif");
+ }
+ else
+ {
+ setToolTipText( "Remove selected offline reports" );
+ setText( "Remove" );
+ setIcon( "icons/remove.gif" );
+ }
+
+ view = offlineReportsView;
+ }
+
+ /**
+ * Delete the appropriate offline reports
+ * @see org.eclipse.jface.action.IAction#run()
+ */
+ @Override
+ public void run()
+ {
+ OfflineView.checkWindow();
+
+ // call the appropriate delete function
+ if (deleteAll)
+ view.deleteAllOfflineReports();
+ else
+ view.deleteSelectedOfflineReports();
+ OfflineView.updateActionEnablement();
+ }
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/actions/OpenBugsAction.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/actions/OpenBugsAction.java
new file mode 100644
index 000000000..b427d413e
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/actions/OpenBugsAction.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2003 - 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.bugzilla.ui.actions;
+
+import java.util.Iterator;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.mylar.bugzilla.core.BugzillaPlugin;
+import org.eclipse.mylar.bugzilla.core.IBugzillaConstants;
+import org.eclipse.mylar.bugzilla.ui.BugzillaUITools;
+import org.eclipse.mylar.bugzilla.ui.search.BugzillaSearchResultView;
+import org.eclipse.search.internal.ui.SearchMessages;
+import org.eclipse.search.internal.ui.util.ExceptionHandler;
+
+
+/**
+ * This class is used to open a bug report in an editor.
+ */
+public class OpenBugsAction extends Action {
+
+ /** The view this action works on */
+ private BugzillaSearchResultView resultView;
+
+ /**
+ * Constructor
+ * @param text The text for this action
+ * @param resultView The <code>BugzillaSearchResultView</code> this action works on
+ */
+ public OpenBugsAction(String text, BugzillaSearchResultView resultView) {
+ setText(text);
+ this.resultView = resultView;
+ }
+
+ /**
+ * Open the selected bug reports in their own editors.
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public void run() {
+
+ // Get the selected items
+ ISelection s = resultView.getViewer().getSelection();
+ if (s instanceof IStructuredSelection) {
+ IStructuredSelection selection = (IStructuredSelection) s;
+
+ // go through each of the selected items and show it in an editor
+ for (Iterator<IMarker> it = selection.iterator(); it.hasNext();) {
+ IMarker marker = it.next();
+ try {
+ Integer id = (Integer) marker.getAttribute(IBugzillaConstants.HIT_MARKER_ATTR_ID);
+ BugzillaUITools.show(id.intValue());
+ }
+ catch (CoreException e) {
+ // if an error occurs, handle and log it
+ ExceptionHandler.handle(e, SearchMessages.Search_Error_search_title, SearchMessages.Search_Error_search_message); //$NON-NLS-2$ //$NON-NLS-1$
+ BugzillaPlugin.log(e.getStatus());
+ }
+ }
+
+ }
+ }
+
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/actions/ViewOfflineReportAction.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/actions/ViewOfflineReportAction.java
new file mode 100644
index 000000000..b4bb30c5d
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/actions/ViewOfflineReportAction.java
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * Copyright (c) 2003 - 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.bugzilla.ui.actions;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.mylar.bugzilla.core.BugReport;
+import org.eclipse.mylar.bugzilla.core.BugzillaPlugin;
+import org.eclipse.mylar.bugzilla.core.IBugzillaBug;
+import org.eclipse.mylar.bugzilla.core.IBugzillaConstants;
+import org.eclipse.mylar.bugzilla.core.NewBugModel;
+import org.eclipse.mylar.bugzilla.core.offline.AbstractOfflineReportsAction;
+import org.eclipse.mylar.bugzilla.ui.OfflineView;
+import org.eclipse.mylar.bugzilla.ui.editor.ExistingBugEditorInput;
+import org.eclipse.mylar.bugzilla.ui.editor.NewBugEditorInput;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PartInitException;
+
+
+/**
+ * View a bug from the offlineReports menu
+ */
+public class ViewOfflineReportAction extends AbstractOfflineReportsAction
+{
+
+ /** The view to get the result to launch a viewer on */
+ private OfflineView view;
+
+ /**
+ * Constructor
+ * @param resultsView The view to launch a viewer on
+ */
+ public ViewOfflineReportAction(OfflineView resultsView )
+ {
+ setToolTipText( "View selected offline reports" );
+ setText( "View selected" );
+ setIcon( "icons/openresult.gif" );
+ view = resultsView;
+ }
+
+ /**
+ * View the selected bugs in the editor window
+ * @see org.eclipse.jface.action.IAction#run()
+ */
+ @Override
+ public void run()
+ {
+ OfflineView.checkWindow();
+ List<IBugzillaBug> selectedBugs = view.getSelectedBugs();
+
+ // if there are some selected bugs view the bugs in the editor window
+ if (!selectedBugs.isEmpty())
+ {
+ IWorkbenchPage page = BugzillaPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getActivePage();
+ for (Iterator<IBugzillaBug> it = selectedBugs.iterator(); it.hasNext(); ) {
+ IBugzillaBug bug = it.next();
+ if (bug instanceof BugReport) {
+ ExistingBugEditorInput editorInput = new ExistingBugEditorInput((BugReport)bug);
+ try {
+ page.openEditor(editorInput, IBugzillaConstants.EXISTING_BUG_EDITOR_ID);
+ } catch (PartInitException e) {
+ BugzillaPlugin.log(e);
+ }
+ continue;
+ }
+ if (bug instanceof NewBugModel) {
+ NewBugEditorInput editorInput = new NewBugEditorInput((NewBugModel)bug);
+ try {
+ page.openEditor(editorInput, IBugzillaConstants.NEW_BUG_EDITOR_ID);
+ } catch (PartInitException e) {
+ BugzillaPlugin.log(e);
+ }
+ continue;
+ }
+ }
+ }
+ }
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/editor/AbstractBugEditor.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/editor/AbstractBugEditor.java
new file mode 100644
index 000000000..e3107bc8d
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/editor/AbstractBugEditor.java
@@ -0,0 +1,1550 @@
+/*******************************************************************************
+ * Copyright (c) 2003 - 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.bugzilla.ui.editor;
+
+import java.net.MalformedURLException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.action.GroupMarker;
+import org.eclipse.jface.action.IMenuListener;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.resource.JFaceColors;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.ListenerList;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.jface.util.SafeRunnable;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.mylar.bugzilla.core.Attribute;
+import org.eclipse.mylar.bugzilla.core.BugPost;
+import org.eclipse.mylar.bugzilla.core.BugzillaPlugin;
+import org.eclipse.mylar.bugzilla.core.BugzillaPreferences;
+import org.eclipse.mylar.bugzilla.core.BugzillaTools;
+import org.eclipse.mylar.bugzilla.core.Comment;
+import org.eclipse.mylar.bugzilla.core.IBugzillaAttributeListener;
+import org.eclipse.mylar.bugzilla.core.IBugzillaBug;
+import org.eclipse.mylar.bugzilla.core.IBugzillaConstants;
+import org.eclipse.mylar.bugzilla.core.IBugzillaReportSelection;
+import org.eclipse.mylar.bugzilla.ui.OfflineView;
+import org.eclipse.mylar.bugzilla.ui.outline.BugzillaOutlineNode;
+import org.eclipse.mylar.bugzilla.ui.outline.BugzillaOutlinePage;
+import org.eclipse.mylar.bugzilla.ui.outline.BugzillaReportSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CLabel;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.ISelectionListener;
+import org.eclipse.ui.ISharedImages;
+import org.eclipse.ui.IWorkbenchActionConstants;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.actions.ActionFactory;
+import org.eclipse.ui.actions.RetargetAction;
+import org.eclipse.ui.internal.WorkbenchImages;
+import org.eclipse.ui.internal.WorkbenchMessages;
+import org.eclipse.ui.internal.help.WorkbenchHelpSystem;
+import org.eclipse.ui.internal.ide.IDEInternalWorkbenchImages;
+import org.eclipse.ui.part.EditorPart;
+import org.eclipse.ui.views.contentoutline.ContentOutline;
+import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
+
+
+/**
+ * Abstract base implementation for an editor to view a bugzilla report.
+ */
+public abstract class AbstractBugEditor extends EditorPart implements Listener {
+
+ protected Display display;
+
+ protected Font titleFont;
+
+ protected Font textFont;
+
+ protected Color background;
+
+ protected Color foreground;
+
+ protected AbstractBugEditorInput bugzillaInput;
+
+ /**
+ * Style option for function <code>newLayout</code>. This will create a
+ * plain-styled, selectable text label.
+ */
+ protected final String VALUE = "VALUE";
+
+ /**
+ * Style option for function <code>newLayout</code>. This will create a
+ * bolded, selectable header. It will also have an arrow image before the
+ * text (simply for decoration).
+ */
+ protected final String HEADER = "HEADER";
+
+ /**
+ * Style option for function <code>newLayout</code>. This will create a
+ * bolded, unselectable label.
+ */
+ protected final String PROPERTY = "PROPERTY";
+
+ protected final int HORZ_INDENT = 0;
+
+ protected Combo oSCombo;
+
+ protected Combo versionCombo;
+
+ protected Combo platformCombo;
+
+ protected Combo priorityCombo;
+
+ protected Combo severityCombo;
+
+ protected Combo milestoneCombo;
+
+ protected Combo componentCombo;
+
+ protected Text urlText;
+
+ protected Text summaryText;
+
+ protected Button submitButton;
+
+ protected Button saveButton;
+
+ protected int scrollIncrement;
+
+ protected int scrollVertPageIncrement;
+
+ protected int scrollHorzPageIncrement;
+
+ public boolean isDirty = false;
+
+ /** Manager controlling the context menu */
+ protected MenuManager contextMenuManager;
+
+ protected StyledText currentSelectedText;
+
+ protected static final String cutActionDefId = "org.eclipse.ui.edit.cut"; //$NON-NLS-1$
+
+ protected static final String copyActionDefId = "org.eclipse.ui.edit.copy"; //$NON-NLS-1$
+
+ protected static final String pasteActionDefId = "org.eclipse.ui.edit.paste"; //$NON-NLS-1$
+
+ protected RetargetAction cutAction;
+
+ protected BugzillaEditorCopyAction copyAction;
+
+ protected RetargetAction pasteAction;
+
+ protected Composite editorComposite;
+
+ protected CLabel titleLabel;
+
+ protected ScrolledComposite scrolledComposite;
+
+ protected Composite infoArea;
+
+ protected StyledText generalTitleText;
+
+ private List<IBugzillaAttributeListener> attributesListeners = new ArrayList<IBugzillaAttributeListener>();
+
+ protected final ISelectionProvider selectionProvider = new ISelectionProvider() {
+ public void addSelectionChangedListener(ISelectionChangedListener listener) {
+ selectionChangedListeners.add(listener);
+ }
+
+ public ISelection getSelection() {
+ return null;
+ }
+
+ public void removeSelectionChangedListener(ISelectionChangedListener listener) {
+ selectionChangedListeners.remove(listener);
+ }
+
+ public void setSelection(ISelection selection) {
+ // No implementation.
+ }
+ };
+
+ protected ListenerList selectionChangedListeners = new ListenerList();
+
+ protected HashMap<Combo, String> comboListenerMap = new HashMap<Combo, String>();
+
+ private IBugzillaReportSelection lastSelected = null;
+
+ protected final ISelectionListener selectionListener = new ISelectionListener() {
+ public void selectionChanged(IWorkbenchPart part, ISelection selection) {
+ if ((part instanceof ContentOutline) && (selection instanceof StructuredSelection)) {
+ Object select = ((StructuredSelection)selection).getFirstElement();
+ if(select instanceof BugzillaOutlineNode){
+ BugzillaOutlineNode n = (BugzillaOutlineNode) select;
+
+ if (n != null && lastSelected != null && BugzillaTools.getHandle(n).equals(BugzillaTools.getHandle(lastSelected))){
+ // we don't need to set the selection if it is alredy set
+ return;
+ }
+ lastSelected = n;
+
+ Object data = n.getData();
+ boolean highlight = true;
+ if(n.getKey().toLowerCase().equals("comments")){
+ highlight = false;
+ }
+ if(n.getKey().toLowerCase().equals("new comment")){
+ selectNewComment();
+ } else if(n.getKey().toLowerCase().equals("new description")){
+ selectNewDescription();
+ } else if (data != null){
+ select(data, highlight);
+ }
+ }
+ }
+ }
+ };
+
+ /**
+ * Creates a new <code>AbstractBugEditor</code>. Sets up the default fonts and
+ * cut/copy/paste actions.
+ */
+ public AbstractBugEditor() {
+ titleFont = JFaceResources.getHeaderFont();
+ textFont = JFaceResources.getDefaultFont();
+
+ // set the scroll increments so the editor scrolls normally with the scroll wheel
+ FontData[] fd = textFont.getFontData();
+ int cushion = 4;
+ scrollIncrement = fd[0].getHeight() + cushion;
+ scrollVertPageIncrement = 0;
+ scrollHorzPageIncrement = 0;
+
+ // set up actions for the context menu
+ cutAction = new RetargetAction(ActionFactory.CUT.getId(), WorkbenchMessages.Workbench_cut);
+ cutAction.setToolTipText(WorkbenchMessages.Workbench_cutToolTip);//WorkbenchMessages.getString("Workbench.cutToolTip")); //$NON-NLS-1$
+ cutAction.setImageDescriptor(WorkbenchImages.getImageDescriptor(
+ ISharedImages.IMG_TOOL_CUT));
+ cutAction.setHoverImageDescriptor(WorkbenchImages.getImageDescriptor(
+ ISharedImages.IMG_TOOL_CUT));
+ cutAction.setDisabledImageDescriptor(WorkbenchImages.getImageDescriptor(
+ ISharedImages.IMG_TOOL_CUT_DISABLED));
+ cutAction.setAccelerator(SWT.CTRL | 'x');
+ cutAction.setActionDefinitionId(cutActionDefId);
+
+ pasteAction = new RetargetAction(ActionFactory.PASTE.getId(), WorkbenchMessages.Workbench_paste);
+ pasteAction.setToolTipText(WorkbenchMessages.Workbench_pasteToolTip);//WorkbenchMessages.getString("Workbench.pasteToolTip")); //$NON-NLS-1$
+ pasteAction.setImageDescriptor(WorkbenchImages.getImageDescriptor(
+ ISharedImages.IMG_TOOL_PASTE));
+ pasteAction.setHoverImageDescriptor(WorkbenchImages.getImageDescriptor(
+ ISharedImages.IMG_TOOL_PASTE));
+ pasteAction.setDisabledImageDescriptor(WorkbenchImages.getImageDescriptor(
+ ISharedImages.IMG_TOOL_PASTE_DISABLED));
+ pasteAction.setAccelerator(SWT.CTRL | 'v');
+ pasteAction.setActionDefinitionId(pasteActionDefId);
+
+ copyAction = new BugzillaEditorCopyAction(this);
+ copyAction.setText(WorkbenchMessages.Workbench_copy);//WorkbenchMessages.getString("Workbench.copy"));
+ copyAction.setImageDescriptor(WorkbenchImages.getImageDescriptor(
+ ISharedImages.IMG_TOOL_COPY));
+ copyAction.setHoverImageDescriptor(WorkbenchImages.getImageDescriptor(
+ ISharedImages.IMG_TOOL_COPY));
+ copyAction.setDisabledImageDescriptor(WorkbenchImages.getImageDescriptor(
+ ISharedImages.IMG_TOOL_COPY_DISABLED));
+ copyAction.setAccelerator(SWT.CTRL | 'c');
+
+ copyAction.setEnabled(false);
+ }
+
+ /**
+ * @return The bug this editor is displaying.
+ */
+ public abstract IBugzillaBug getBug();
+
+ /**
+ * @return Any currently selected text.
+ */
+ protected StyledText getCurrentText() {
+ return currentSelectedText;
+ }
+
+ /**
+ * @return The action used to copy selected text from a bug editor to the clipboard.
+ */
+ protected BugzillaEditorCopyAction getCopyAction() {
+ return copyAction;
+ }
+
+ @Override
+ public void createPartControl(Composite parent) {
+ editorComposite = new Composite(parent, SWT.NONE);
+ GridLayout layout = new GridLayout();
+ layout.marginHeight = 0;
+ layout.marginWidth = 0;
+ layout.verticalSpacing = 0;
+ layout.horizontalSpacing = 0;
+ editorComposite.setLayout(layout);
+ // Create the title for the editor
+ createTitleArea(editorComposite);
+ Label titleBarSeparator =
+ new Label(editorComposite, SWT.HORIZONTAL | SWT.SEPARATOR);
+
+ background = JFaceColors.getBannerBackground(display);
+ foreground = JFaceColors.getBannerForeground(display);
+ GridData gd = new GridData(GridData.FILL_HORIZONTAL);
+ titleBarSeparator.setLayoutData(gd);
+
+ // Put the bug info onto the editor
+ createInfoArea(editorComposite);
+
+ WorkbenchHelpSystem.getInstance().setHelp(editorComposite, IBugzillaConstants.EDITOR_PAGE_CONTEXT);
+
+ infoArea.setMenu(contextMenuManager.createContextMenu(infoArea));
+
+ getSite().getPage().addSelectionListener(selectionListener);
+ getSite().setSelectionProvider(selectionProvider);
+ }
+
+ /**
+ * Creates the title label at the top of the editor.
+ *
+ * @param parent
+ * The composite to put the title label into.
+ * @return The title composite.
+ */
+ protected Composite createTitleArea(Composite parent) {
+ // Get the background color for the title area
+ display = parent.getDisplay();
+ background = JFaceColors.getBannerBackground(display);
+ foreground = JFaceColors.getBannerForeground(display);
+
+ // Create the title area which will contain
+ // a title, message, and image.
+ Composite titleArea = new Composite(parent, SWT.NO_FOCUS);
+ GridLayout layout = new GridLayout();
+ layout.marginHeight = 0;
+ layout.marginWidth = 0;
+ layout.verticalSpacing = 0;
+ layout.horizontalSpacing = 0;
+ layout.numColumns = 2;
+ titleArea.setLayout(layout);
+ titleArea.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ titleArea.setBackground(background);
+
+ // Message label
+ titleLabel = new CLabel(titleArea, SWT.LEFT);
+ JFaceColors.setColors(titleLabel, foreground, background);
+ titleLabel.setFont(titleFont);
+ final IPropertyChangeListener fontListener = new IPropertyChangeListener() {
+ public void propertyChange(PropertyChangeEvent event) {
+ if (JFaceResources.HEADER_FONT.equals(event.getProperty())) {
+ titleLabel.setFont(titleFont);
+ }
+ }
+ };
+ titleLabel.addDisposeListener(new DisposeListener() {
+ public void widgetDisposed(DisposeEvent event) {
+ JFaceResources.getFontRegistry().removeListener(fontListener);
+ }
+ });
+ JFaceResources.getFontRegistry().addListener(fontListener);
+ GridData gd = new GridData(GridData.FILL_BOTH);
+ titleLabel.setLayoutData(gd);
+
+ // Title image
+ Label titleImage = new Label(titleArea, SWT.LEFT);
+ titleImage.setBackground(background);
+ titleImage.setImage(
+ WorkbenchImages.getImage(
+ IDEInternalWorkbenchImages.IMG_OBJS_WELCOME_BANNER));
+ gd = new GridData();
+ gd.horizontalAlignment = GridData.END;
+ titleImage.setLayoutData(gd);
+
+ return titleArea;
+ }
+
+ /**
+ * Creates the part of the editor that contains the information about the
+ * the bug.
+ *
+ * @param parent
+ * The composite to put the info area into.
+ * @return The info area composite.
+ */
+ protected Composite createInfoArea(Composite parent) {
+
+ createContextMenu();
+
+ scrolledComposite =
+ new ScrolledComposite(parent, SWT.V_SCROLL | SWT.H_SCROLL);
+ scrolledComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
+ infoArea = new Composite(this.scrolledComposite, SWT.NONE);
+ scrolledComposite.setMinSize(
+ infoArea.computeSize(SWT.DEFAULT, SWT.DEFAULT));
+ GridLayout infoLayout = new GridLayout();
+ infoLayout.numColumns = 1;
+ infoLayout.verticalSpacing = 0;
+ infoLayout.horizontalSpacing = 0;
+ infoLayout.marginWidth = 0;
+ infoArea.setLayout(infoLayout);
+ infoArea.setBackground(background);
+ if (getBug() == null) {
+ MessageDialog.openError(infoArea.getShell(), "No such bug",
+ "No bug exists with this id");
+ return null;
+ }
+ createLayouts();
+
+ this.scrolledComposite.setContent(infoArea);
+ Point p = infoArea.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
+ this.scrolledComposite.setMinHeight(p.y);
+ this.scrolledComposite.setMinWidth(p.x);
+ this.scrolledComposite.setExpandHorizontal(true);
+ this.scrolledComposite.setExpandVertical(true);
+
+ // make the editor scroll properly with a scroll editor
+ scrolledComposite.addControlListener(new ControlListener() {
+ public void controlMoved(ControlEvent e) {
+ // don't care when the control moved
+ }
+
+ public void controlResized(ControlEvent e) {
+ scrolledComposite.getVerticalBar()
+ .setIncrement(scrollIncrement);
+ scrolledComposite.getHorizontalBar().setIncrement(
+ scrollIncrement);
+ scrollVertPageIncrement = scrolledComposite.getClientArea().height;
+ scrollHorzPageIncrement = scrolledComposite.getClientArea().width;
+ scrolledComposite.getVerticalBar().setPageIncrement(
+ scrollVertPageIncrement);
+ scrolledComposite.getHorizontalBar().setPageIncrement(
+ scrollHorzPageIncrement);
+ }
+ });
+
+ return infoArea;
+ }
+
+ /**
+ * Create a context menu for this editor.
+ */
+ protected void createContextMenu() {
+ contextMenuManager = new MenuManager("#BugEditor");
+ contextMenuManager.setRemoveAllWhenShown(true);
+ contextMenuManager.addMenuListener(new IMenuListener() {
+ public void menuAboutToShow(IMenuManager manager) {
+ manager.add(cutAction);
+ manager.add(copyAction);
+ manager.add(pasteAction);
+ manager.add(new Separator());
+ manager.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS));
+ if (currentSelectedText == null ||
+ currentSelectedText.getSelectionText().length() == 0) {
+
+ copyAction.setEnabled(false);
+ }
+ else {
+ copyAction.setEnabled(true);
+ }
+ }
+ });
+ getSite().registerContextMenu("#BugEditor", contextMenuManager,
+ getSite().getSelectionProvider());
+ }
+
+ /**
+ * Creates all of the layouts that display the information on
+ * the bug.
+ */
+ protected void createLayouts() {
+ createAttributeLayout();
+ createDescriptionLayout();
+ createCommentLayout();
+ createButtonLayouts();
+ }
+
+ /**
+ * Creates the attribute layout, which contains most of the basic attributes
+ * of the bug (some of which are editable).
+ */
+ protected void createAttributeLayout() {
+
+ String title = getTitleString();
+ String keywords = "";
+ String url = "";
+
+ // Attributes Composite- this holds all the combo fiels and text
+ // fields
+ Composite attributesComposite = new Composite(infoArea, SWT.NONE);
+ GridLayout attributesLayout = new GridLayout();
+ attributesLayout.numColumns = 4;
+ attributesLayout.horizontalSpacing = 14;
+ attributesLayout.verticalSpacing = 6;
+ attributesComposite.setLayout(attributesLayout);
+ GridData attributesData = new GridData(GridData.FILL_BOTH);
+ attributesData.horizontalSpan = 1;
+ attributesData.grabExcessVerticalSpace = false;
+ attributesComposite.setLayoutData(attributesData);
+ attributesComposite.setBackground(background);
+ // End Attributes Composite
+
+ // Attributes Title Area
+ Composite attributesTitleComposite =
+ new Composite(attributesComposite, SWT.NONE);
+ GridLayout attributesTitleLayout = new GridLayout();
+ attributesTitleLayout.horizontalSpacing = 0;
+ attributesTitleLayout.marginWidth = 0;
+ attributesTitleComposite.setLayout(attributesTitleLayout);
+ attributesTitleComposite.setBackground(background);
+ GridData attributesTitleData =
+ new GridData(GridData.HORIZONTAL_ALIGN_FILL);
+ attributesTitleData.horizontalSpan = 4;
+ attributesTitleData.grabExcessVerticalSpace = false;
+ attributesTitleComposite.setLayoutData(attributesTitleData);
+ // End Attributes Title
+
+ // Set the Attributes Title
+ newAttributesLayout(attributesTitleComposite);
+ titleLabel.setText(title);
+ bugzillaInput.setToolTipText(title);
+ int currentCol = 1;
+
+ // Populate Attributes
+ for (Iterator<Attribute> it = getBug().getAttributes().iterator(); it.hasNext();) {
+ Attribute attribute = it.next();
+ String key = attribute.getParameterName();
+ String name = attribute.getName();
+ String value = checkText(attribute.getValue());
+ Map<String, String> values = attribute.getOptionValues();
+
+ // make sure we don't try to display a hidden field
+ if (attribute.isHidden() || (key != null && key.equals("status_whiteboard")))
+ continue;
+
+ if (values == null)
+ values = new HashMap<String, String>();
+
+ if (key == null)
+ key = "";
+
+ GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
+ data.horizontalSpan = 1;
+ data.horizontalIndent = HORZ_INDENT;
+
+ if (key.equals("short_desc") || key.equals("keywords")) {
+ keywords = value;
+ }
+ else if (key.equals("newcc")) {
+ // we don't do anything with the cc yet
+ // add add an editable field as well as a table with the other cc's in it
+ // problem is is that we don't want to handle the error when the e-mail addr is incorrect - add a note saying that if wrong, no changes submitted
+ }
+ else if (key.equals("bug_file_loc")) {
+ url = value;
+ }
+ else if (key.equals("op_sys")) {
+ newLayout(attributesComposite, 1, name, PROPERTY);
+ oSCombo = new Combo(attributesComposite, SWT.NO_BACKGROUND
+ | SWT.MULTI | SWT.V_SCROLL | SWT.READ_ONLY);
+
+ oSCombo.setLayoutData(data);
+ oSCombo.setBackground(background);
+ Set<String> s = values.keySet();
+ String[] a = s.toArray(new String[s.size()]);
+ Arrays.sort(a);
+ for (int i = 0; i < a.length; i++) {
+ oSCombo.add(a[i]);
+ }
+ oSCombo.select(oSCombo.indexOf(value));
+ oSCombo.addListener(SWT.Modify, this);
+ comboListenerMap.put(oSCombo, name);
+ oSCombo.addListener(SWT.FocusIn, new GenericListener());
+ currentCol += 2;
+ }
+ else if (key.equals("version")) {
+ newLayout(attributesComposite, 1, name, PROPERTY);
+
+ versionCombo = new Combo(attributesComposite, SWT.NO_BACKGROUND
+ | SWT.MULTI | SWT.V_SCROLL | SWT.READ_ONLY);
+
+ versionCombo.setLayoutData(data);
+ versionCombo.setBackground(background);
+ Set<String> s = values.keySet();
+ String[] a = s.toArray(new String[s.size()]);
+ Arrays.sort(a);
+ for (int i = 0; i < a.length; i++) {
+ versionCombo.add(a[i]);
+ }
+ versionCombo.select(versionCombo.indexOf(value));
+ versionCombo.addListener(SWT.Modify, this);
+ versionCombo.addListener(SWT.FocusIn, new GenericListener());
+ comboListenerMap.put(versionCombo, name);
+ currentCol += 2;
+ }
+ else if (key.equals("priority")) {
+ newLayout(attributesComposite, 1, name, PROPERTY);
+
+ priorityCombo = new Combo(attributesComposite,
+ SWT.NO_BACKGROUND | SWT.MULTI | SWT.V_SCROLL
+ | SWT.READ_ONLY);
+
+ priorityCombo.setLayoutData(data);
+ priorityCombo.setBackground(background);
+ Set<String> s = values.keySet();
+ String[] a = s.toArray(new String[s.size()]);
+ Arrays.sort(a);
+ for (int i = 0; i < a.length; i++) {
+ priorityCombo.add(a[i]);
+ }
+ priorityCombo.select(priorityCombo.indexOf(value));
+ priorityCombo.addListener(SWT.Modify, this);
+ priorityCombo.addListener(SWT.FocusIn, new GenericListener());
+ comboListenerMap.put(priorityCombo, name);
+ currentCol += 2;
+ }
+ else if (key.equals("bug_severity")) {
+ newLayout(attributesComposite, 1, name, PROPERTY);
+ severityCombo =
+ new Combo(
+ attributesComposite,
+ SWT.NO_BACKGROUND
+ | SWT.MULTI
+ | SWT.V_SCROLL
+ | SWT.READ_ONLY);
+
+ severityCombo.setLayoutData(data);
+ severityCombo.setBackground(background);
+ Set<String> s = values.keySet();
+ String[] a = s.toArray(new String[s.size()]);
+ Arrays.sort(a);
+ for (int i = 0; i < a.length; i++) {
+ severityCombo.add(a[i]);
+ }
+ severityCombo.select(severityCombo.indexOf(value));
+ severityCombo.addListener(SWT.Modify, this);
+ severityCombo.addListener(SWT.FocusIn, new GenericListener());
+ comboListenerMap.put(severityCombo, name);
+ currentCol += 2;
+ }
+ else if (key.equals("target_milestone")) {
+ newLayout(attributesComposite, 1, name, PROPERTY);
+ milestoneCombo =
+ new Combo(
+ attributesComposite,
+ SWT.NO_BACKGROUND
+ | SWT.MULTI
+ | SWT.V_SCROLL
+ | SWT.READ_ONLY);
+
+ milestoneCombo.setLayoutData(data);
+ milestoneCombo.setBackground(background);
+ Set<String> s = values.keySet();
+ String[] a = s.toArray(new String[s.size()]);
+ Arrays.sort(a);
+ for (int i = 0; i < a.length; i++) {
+ milestoneCombo.add(a[i]);
+ }
+ milestoneCombo.select(milestoneCombo.indexOf(value));
+ milestoneCombo.addListener(SWT.Modify, this);
+ milestoneCombo.addListener(SWT.FocusIn, new GenericListener());
+ comboListenerMap.put(milestoneCombo, name);
+ currentCol += 2;
+ }
+ else if (key.equals("rep_platform")) {
+ newLayout(attributesComposite, 1, name, PROPERTY);
+ platformCombo =
+ new Combo(
+ attributesComposite,
+ SWT.NO_BACKGROUND
+ | SWT.MULTI
+ | SWT.V_SCROLL
+ | SWT.READ_ONLY);
+
+ platformCombo.setLayoutData(data);
+ platformCombo.setBackground(background);
+ Set<String> s = values.keySet();
+ String[] a = s.toArray(new String[s.size()]);
+ Arrays.sort(a);
+ for (int i = 0; i < a.length; i++) {
+ platformCombo.add(a[i]);
+ }
+ platformCombo.select(platformCombo.indexOf(value));
+ platformCombo.addListener(SWT.Modify, this);
+ platformCombo.addListener(SWT.FocusIn, new GenericListener());
+ comboListenerMap.put(platformCombo, name);
+ currentCol += 2;
+ }
+ else if (key.equals("product")) {
+ newLayout(attributesComposite, 1, name, PROPERTY);
+ newLayout(attributesComposite, 1, value, VALUE).addListener(SWT.FocusIn, new GenericListener());
+ currentCol += 2;
+ }
+ else if (key.equals("component")) {
+ newLayout(attributesComposite, 1, name, PROPERTY);
+ componentCombo =
+ new Combo(
+ attributesComposite,
+ SWT.NO_BACKGROUND
+ | SWT.MULTI
+ | SWT.V_SCROLL
+ | SWT.READ_ONLY);
+
+ componentCombo.setLayoutData(data);
+ componentCombo.setBackground(background);
+ Set<String> s = values.keySet();
+ String[] a = s.toArray(new String[s.size()]);
+ Arrays.sort(a);
+ for (int i = 0; i < a.length; i++) {
+ componentCombo.add(a[i]);
+ }
+ componentCombo.select(componentCombo.indexOf(value));
+ componentCombo.addListener(SWT.Modify, this);
+ componentCombo.addListener(SWT.FocusIn, new GenericListener());
+ comboListenerMap.put(componentCombo, name);
+ currentCol += 2;
+ }
+ else if (name.equals("Summary")) {
+ // Don't show the summary here.
+ continue;
+ }
+ else if (values.isEmpty()) {
+ newLayout(attributesComposite, 1, name, PROPERTY);
+ newLayout(attributesComposite, 1, value, VALUE).addListener(SWT.FocusIn, new GenericListener());
+ currentCol += 2;
+ }
+ if (currentCol > attributesLayout.numColumns) {
+ currentCol -= attributesLayout.numColumns;
+ }
+ }
+ // End Populate Attributes
+
+ // make sure that we are in the first column
+ if (currentCol > 1) {
+ while (currentCol <= attributesLayout.numColumns) {
+ newLayout(attributesComposite, 1, "", PROPERTY);
+ currentCol++;
+ }
+ }
+
+ // URL, Keywords, Summary Text Fields
+ addUrlText(url, attributesComposite);
+
+ // keywords text field (not editable)
+ addKeywordsList(keywords, attributesComposite);
+ addSummaryText(attributesComposite);
+ // End URL, Keywords, Summary Text Fields
+ }
+
+ /**
+ * Adds a text field to display and edit the bug's URL attribute.
+ *
+ * @param url
+ * The URL attribute of the bug.
+ * @param attributesComposite
+ * The composite to add the text field to.
+ */
+ protected void addUrlText(String url, Composite attributesComposite) {
+ newLayout(attributesComposite, 1, "URL:", PROPERTY);
+ urlText =
+ new Text(attributesComposite, SWT.BORDER | SWT.SINGLE | SWT.WRAP);
+ GridData urlTextData = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
+ urlTextData.horizontalSpan = 3;
+ urlTextData.widthHint = 200;
+ urlText.setLayoutData(urlTextData);
+ urlText.setText(url);
+ urlText.addListener(SWT.FocusOut, new Listener() {
+ public void handleEvent(Event event) {
+ String sel = urlText.getText();
+ Attribute a = getBug().getAttribute("URL");
+ if (!(a.getNewValue().equals(sel))) {
+ a.setNewValue(sel);
+ changeDirtyStatus(true);
+ }
+ }
+ });
+ urlText.addListener(SWT.FocusIn, new GenericListener());
+ }
+
+ /**
+ * Adds a text field and selection list to display and edit the bug's
+ * keywords.
+ *
+ * @param keywords
+ * The current list of keywords for this bug.
+ * @param attributesComposite
+ * The composite to add the widgets to.
+ */
+ protected abstract void addKeywordsList(String keywords, Composite attributesComposite);
+
+ /**
+ * Adds a text field to display and edit the bug's summary.
+ *
+ * @param attributesComposite
+ * The composite to add the text field to.
+ */
+ protected void addSummaryText(Composite attributesComposite) {
+ newLayout(attributesComposite, 1, "Summary:", PROPERTY);
+ summaryText =
+ new Text(attributesComposite, SWT.BORDER | SWT.SINGLE | SWT.WRAP);
+ GridData summaryTextData = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
+ summaryTextData.horizontalSpan = 3;
+ summaryTextData.widthHint = 200;
+ summaryText.setLayoutData(summaryTextData);
+ summaryText.setText(getBug().getSummary());
+ summaryText.addListener(SWT.FocusOut, new SummaryListener());
+ summaryText.addListener(SWT.FocusIn, new GenericListener());
+ }
+
+ /**
+ * Creates the description layout, which displays and possibly edits the
+ * bug's description.
+ */
+ protected abstract void createDescriptionLayout();
+
+ /**
+ * Creates the comment layout, which displays the bug's comments and
+ * possibly lets the user enter a new one.
+ */
+ protected abstract void createCommentLayout();
+
+ /**
+ * Creates the button layout. This displays options and buttons at the
+ * bottom of the editor to allow actions to be performed on the bug.
+ */
+ protected void createButtonLayouts() {
+
+ Composite buttonComposite = new Composite(infoArea, SWT.NONE);
+ GridLayout buttonLayout = new GridLayout();
+ buttonLayout.numColumns = 4;
+ buttonComposite.setLayout(buttonLayout);
+ buttonComposite.setBackground(background);
+ GridData buttonData = new GridData(GridData.FILL_BOTH);
+ buttonData.horizontalSpan = 1;
+ buttonData.grabExcessVerticalSpace = false;
+ buttonComposite.setLayoutData(buttonData);
+
+ addRadioButtons(buttonComposite);
+ addActionButtons(buttonComposite);
+ }
+
+ /**
+ * Adds radio buttons to this composite.
+ * @param buttonComposite Composite to add the radio buttons to.
+ */
+ abstract protected void addRadioButtons(Composite buttonComposite);
+
+ /**
+ * Adds buttons to this composite.
+ * Subclasses can override this method to provide different/additional buttons.
+ * @param buttonComposite Composite to add the buttons to.
+ */
+ protected void addActionButtons(Composite buttonComposite) {
+ submitButton = new Button(buttonComposite, SWT.NONE);
+ GridData submitButtonData = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
+ submitButtonData.widthHint = 80;
+ submitButtonData.heightHint = 20;
+ submitButton.setText("Submit");
+ submitButton.setLayoutData(submitButtonData);
+ submitButton.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event e) {
+ submitBug();
+ }
+ });
+ submitButton.addListener(SWT.FocusIn, new GenericListener());
+
+ saveButton = new Button(buttonComposite, SWT.NONE);
+ GridData saveButtonData = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
+ saveButtonData.widthHint = 80;
+ saveButtonData.heightHint = 20;
+ saveButton.setText("Save Offline");
+ saveButton.setLayoutData(saveButtonData);
+ saveButton.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event e) {
+ saveBug();
+ updateEditor();
+ }
+ });
+ saveButton.addListener(SWT.FocusIn, new GenericListener());
+ }
+
+ /**
+ * Make sure that a String that is <code>null</code> is changed to a null
+ * string
+ *
+ * @param text
+ * The text to check if it is null or not
+ * @return If the text is <code>null</code>, then return the null string (<code>""</code>).
+ * Otherwise, return the text.
+ */
+ public String checkText(String text) {
+ if (text == null)
+ return "";
+ else
+ return text;
+ }
+
+ /**
+ * @return A string to use as a title for this editor.
+ */
+ protected abstract String getTitleString();
+
+ /**
+ * Creates an uneditable text field for displaying data.
+ *
+ * @param composite
+ * The composite to put this text field into. Its layout style
+ * should be a grid with columns.
+ * @param colSpan
+ * The number of columns that this text field should span.
+ * @param text
+ * The text that for this text field.
+ * @param style
+ * The style for this text field. See below for valid values
+ * (default is HEADER).
+ * @return The new styled text.
+ * @see VALUE
+ * @see PROPERTY
+ * @see HEADER
+ */
+ protected StyledText newLayout(Composite composite, int colSpan, String text,
+ String style) {
+ GridData data = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
+ data.horizontalSpan = colSpan;
+
+ StyledText stext;
+ if (style.equalsIgnoreCase(VALUE)) {
+ StyledText styledText =
+ new StyledText(composite, SWT.MULTI | SWT.READ_ONLY);
+ styledText.setFont(textFont);
+ styledText.setText(checkText(text));
+ styledText.setBackground(background);
+ data.horizontalIndent = HORZ_INDENT;
+ styledText.setLayoutData(data);
+ styledText.setEditable(false);
+ styledText.getCaret().setVisible(false);
+
+ styledText.addSelectionListener(new SelectionAdapter() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ StyledText c = (StyledText) e.widget;
+ if (c != null && c.getSelectionCount() > 0) {
+ if (currentSelectedText != null) {
+ if (!c.equals(currentSelectedText)) {
+ currentSelectedText.setSelectionRange(0, 0);
+ }
+ }
+ }
+ currentSelectedText = c;
+ }
+ });
+
+ styledText.setMenu(
+ contextMenuManager.createContextMenu(styledText));
+ stext = styledText;
+ }
+ else if (style.equalsIgnoreCase(PROPERTY)) {
+ StyledText styledText =
+ new StyledText(composite, SWT.MULTI | SWT.READ_ONLY);
+ styledText.setFont(textFont);
+ styledText.setText(checkText(text));
+ styledText.setBackground(background);
+ data.horizontalIndent = HORZ_INDENT;
+ styledText.setLayoutData(data);
+ StyleRange sr =
+ new StyleRange(
+ styledText.getOffsetAtLine(0),
+ text.length(),
+ foreground,
+ background,
+ SWT.BOLD);
+ styledText.setStyleRange(sr);
+ styledText.getCaret().setVisible(false);
+ styledText.setEnabled(false);
+
+
+ styledText.setMenu(contextMenuManager.createContextMenu(styledText));
+ stext = styledText;
+ }
+ else {
+ Composite generalTitleGroup = new Composite(composite, SWT.NONE);
+ generalTitleGroup.setLayoutData(
+ new GridData(GridData.FILL_HORIZONTAL));
+ generalTitleGroup.setLayoutData(data);
+ GridLayout generalTitleLayout = new GridLayout();
+ generalTitleLayout.numColumns = 2;
+ generalTitleLayout.marginWidth = 0;
+ generalTitleLayout.marginHeight = 9;
+ generalTitleGroup.setLayout(generalTitleLayout);
+ generalTitleGroup.setBackground(background);
+
+ Label image = new Label(generalTitleGroup, SWT.NONE);
+ image.setBackground(background);
+ image.setImage(
+ WorkbenchImages.getImage(
+ IDEInternalWorkbenchImages.IMG_OBJS_WELCOME_ITEM));
+
+ GridData gd = new GridData(GridData.FILL_BOTH);
+ gd.verticalAlignment = GridData.VERTICAL_ALIGN_BEGINNING;
+ image.setLayoutData(gd);
+ StyledText titleText =
+ new StyledText(generalTitleGroup, SWT.MULTI | SWT.READ_ONLY);
+ titleText.setText(checkText(text));
+ titleText.setBackground(background);
+ StyleRange sr = new StyleRange(
+ titleText.getOffsetAtLine(0),
+ text.length(),
+ foreground,
+ background,
+ SWT.BOLD);
+ titleText.setStyleRange(sr);
+ titleText.getCaret().setVisible(false);
+ titleText.setEditable(false);
+ titleText.addSelectionListener(new SelectionAdapter() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ StyledText c = (StyledText) e.widget;
+ if (c != null && c.getSelectionCount() > 0) {
+ if (currentSelectedText != null) {
+ if (!c.equals(currentSelectedText)) {
+ currentSelectedText.setSelectionRange(0, 0);
+ }
+ }
+ }
+ currentSelectedText = c;
+ }
+ });
+ // create context menu
+ generalTitleGroup.setMenu(
+ contextMenuManager.createContextMenu(generalTitleGroup));
+ titleText.setMenu(
+ contextMenuManager.createContextMenu(titleText));
+ image.setMenu(
+ contextMenuManager.createContextMenu(image));
+ stext = titleText;
+ }
+ composite.setMenu(contextMenuManager.createContextMenu(composite));
+ return stext;
+ }
+
+ /**
+ * This creates the title header for the info area. Its style is similar to
+ * one from calling the function <code>newLayout</code> with the style
+ * <code>HEADER</code>.
+ *
+ * @param composite
+ * The composite to put this text field into. Its layout style
+ * should be a grid with columns.
+ */
+ protected void newAttributesLayout(Composite composite) {
+ GridData data = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
+ data.horizontalSpan = 4;
+ Composite generalTitleGroup = new Composite(composite, SWT.NONE);
+ generalTitleGroup.setLayoutData(
+ new GridData(GridData.FILL_HORIZONTAL));
+ generalTitleGroup.setLayoutData(data);
+ GridLayout generalTitleLayout = new GridLayout();
+ generalTitleLayout.numColumns = 2;
+ generalTitleLayout.marginWidth = 0;
+ generalTitleLayout.marginHeight = 9;
+ generalTitleGroup.setLayout(generalTitleLayout);
+ generalTitleGroup.setBackground(background);
+
+ Label image = new Label(generalTitleGroup, SWT.NONE);
+ image.setBackground(background);
+ image.setImage(
+ WorkbenchImages.getImage(
+ IDEInternalWorkbenchImages.IMG_OBJS_WELCOME_ITEM));
+
+ GridData gd = new GridData(GridData.FILL_BOTH);
+ gd.verticalAlignment = GridData.VERTICAL_ALIGN_BEGINNING;
+ image.setLayoutData(gd);
+ generalTitleText =
+ new StyledText(generalTitleGroup, SWT.MULTI | SWT.READ_ONLY);
+ setGeneralTitleText();
+ generalTitleText.setBackground(background);
+ generalTitleText.getCaret().setVisible(false);
+ generalTitleText.setEditable(false);
+ generalTitleText.addSelectionListener(new SelectionAdapter() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ StyledText c = (StyledText) e.widget;
+ if (c != null && c.getSelectionCount() > 0) {
+ if (currentSelectedText != null) {
+ if (!c.equals(currentSelectedText)) {
+ currentSelectedText.setSelectionRange(0, 0);
+ }
+ }
+ }
+ currentSelectedText = c;
+ }
+ });
+ // create context menu
+ generalTitleGroup.setMenu(
+ contextMenuManager.createContextMenu(generalTitleGroup));
+ generalTitleText.setMenu(
+ contextMenuManager.createContextMenu(generalTitleText));
+ image.setMenu(
+ contextMenuManager.createContextMenu(image));
+ composite.setMenu(contextMenuManager.createContextMenu(composite));
+ }
+
+ /**
+ * This refreshes the text in the title label of the info area (it contains
+ * elements which can change).
+ */
+ protected void setGeneralTitleText() {
+ String text = getTitleString();
+ generalTitleText.setText(text);
+ StyleRange sr = new StyleRange(
+ generalTitleText.getOffsetAtLine(0),
+ text.length(),
+ foreground,
+ background,
+ SWT.BOLD);
+ generalTitleText.setStyleRange(sr);
+ generalTitleText.addListener(SWT.FocusIn, new GenericListener());
+
+ // Resize the composite, in case the new summary is longer than the
+ // previous one.
+ // Then redraw it to show the changes.
+ generalTitleText.getParent().pack(true);
+ generalTitleText.redraw();
+ }
+
+ /**
+ * Creates some blank space underneath the supplied composite.
+ *
+ * @param parent
+ * The composite to add the blank space to.
+ */
+ protected void createSeparatorSpace(Composite parent) {
+ GridData separatorData = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
+ separatorData.verticalSpan = 1;
+ separatorData.grabExcessVerticalSpace = false;
+
+ Composite separatorComposite = new Composite(parent, SWT.NONE);
+ GridLayout separatorLayout = new GridLayout();
+ separatorLayout.marginHeight = 0;
+ separatorLayout.verticalSpacing = 0;
+ separatorComposite.setLayout(separatorLayout);
+ separatorComposite.setBackground(background);
+ separatorComposite.setLayoutData(separatorData);
+ newLayout(separatorComposite, 1, "", VALUE);
+ }
+
+ /**
+ * Submit the changes to the bug to the bugzilla server.
+ */
+ protected abstract void submitBug();
+
+ /**
+ * If there is no locally saved copy of the current bug, then it saved
+ * offline. Otherwise, any changes are updated in the file.
+ */
+ public void saveBug() {
+ updateBug();
+ changeDirtyStatus(false);
+ OfflineView.saveOffline(getBug());
+ }
+
+ /**
+ * Updates the <code>IBugzillaBug</code> object to contain the latest data
+ * entered in the data fields.
+ */
+ protected abstract void updateBug();
+
+ /**
+ * Resets the data fields to contain the data currently in the
+ * <code>IBugzillaBug</code> object.
+ */
+ protected abstract void restoreBug();
+
+ /**
+ * Refreshes any text labels in the editor that contain information that
+ * might change.
+ */
+ protected void updateEditor() {
+ // Reset all summary occurrences, since it might have
+ // been edited.
+ String title = getTitleString();
+ titleLabel.setText(title);
+ setGeneralTitleText();
+ }
+
+ /**
+ * Break text up into lines of about 80 characters so that it
+ * is displayed properly in bugzilla
+ * @param origText The string to be formatted
+ * @return The formatted text
+ */
+ protected String formatText(String origText)
+ {
+ String [] textArray = new String[(origText.length()/80 + 1) *2];
+ for(int i = 0; i < textArray.length; i++)
+ textArray[i] = null;
+ int j = 0;
+ while(true) {
+ int spaceIndex = origText.indexOf(" ", 75);
+ if (spaceIndex == origText.length() || spaceIndex == -1) {
+ textArray[j] = origText;
+ break;
+ }
+ textArray[j] = origText.substring(0, spaceIndex);
+ origText = origText.substring(spaceIndex + 1, origText.length());
+ j++;
+ }
+
+ String newText = "";
+
+ for (int i = 0; i < textArray.length; i++) {
+ if(textArray[i] == null)
+ break;
+ newText += textArray[i] + "\n";
+ }
+ return newText;
+ }
+
+ /**
+ * function to set the url to post the bug to
+ * @param form A reference to a BugPost that the bug is going to be posted to
+ * @param formName The form that we wish to use to submit the bug
+ */
+ protected void setURL(BugPost form, String formName) {
+ String baseURL = BugzillaPlugin.getDefault().getServerName();
+ if (!baseURL.endsWith("/"))
+ baseURL += "/";
+ try {
+ form.setURL(baseURL + formName);
+ }
+ catch (MalformedURLException e){
+ // we should be ok here
+ }
+
+ // add the login information to the bug post
+ form.add("Bugzilla_login", BugzillaPreferences.getUserName());
+ form.add("Bugzilla_password", BugzillaPreferences.getPassword());
+ }
+
+ @Override
+ public void setFocus() {
+ scrolledComposite.setFocus();
+ }
+
+ @Override
+ public boolean isDirty() {
+ return false;
+ }
+
+ /**
+ * Updates the dirty status of this editor page. The dirty status is true if
+ * the bug report has been modified but not saved. The title of the editor
+ * is also updated to reflect the status.
+ *
+ * @param newDirtyStatus
+ * is true when the bug report has been modified but not saved
+ */
+ public void changeDirtyStatus(boolean newDirtyStatus) {
+ isDirty = newDirtyStatus;
+ updateEditorTitle();
+ }
+
+ /**
+ * Updates the title of the editor to reflect dirty status.
+ * If the bug report has been modified but not saved, then
+ * an indicator will appear in the title.
+ */
+ protected void updateEditorTitle() {
+ String prefix = (isDirty) ? "*" : "" ;
+ setPartName(prefix + bugzillaInput.getName());
+ }
+
+ @Override
+ public boolean isSaveAsAllowed() {
+ return false;
+ }
+
+ @Override
+ public void doSave(IProgressMonitor monitor) {
+ // we don't save, so no need to implement
+ }
+
+ @Override
+ public void doSaveAs() {
+ // we don't save, so no need to implement
+ }
+
+ /**
+ * @return The composite for the whole editor.
+ */
+ public Composite getEditorComposite() {
+ return editorComposite;
+ }
+
+ @Override
+ public void dispose() {
+ super.dispose();
+ isDisposed = true;
+ getSite().getPage().removeSelectionListener(selectionListener);
+ }
+
+ public void handleEvent(Event event) {
+ if (event.widget instanceof Combo) {
+ Combo combo = (Combo)event.widget;
+ if (comboListenerMap.containsKey(combo)) {
+ String sel = combo.getItem(combo.getSelectionIndex());
+ Attribute a = getBug().getAttribute(comboListenerMap.get(combo));
+ if (!(a.getNewValue().equals(sel))) {
+ a.setNewValue(sel);
+ for(IBugzillaAttributeListener client : attributesListeners) {
+ client.attributeChanged(a.getName(), sel);
+ }
+ changeDirtyStatus(true);
+ }
+ }
+ }
+ }
+
+ /**
+ * Fires a <code>SelectionChangedEvent</code> to all listeners registered
+ * under <code>selectionChangedListeners</code>.
+ *
+ * @param event
+ * The selection event.
+ */
+ protected void fireSelectionChanged(final SelectionChangedEvent event) {
+ Object[] listeners = selectionChangedListeners.getListeners();
+ for (int i = 0; i < listeners.length; i++) {
+ final ISelectionChangedListener l = (ISelectionChangedListener) listeners[i];
+ SafeRunnable.run(new SafeRunnable() {
+ public void run() {
+ l.selectionChanged(event);
+ }
+ });
+ }
+ }
+
+ /**
+ * A generic listener for selection of unimportant items. The default
+ * selection item sent out is the entire bug object.
+ */
+ protected class GenericListener implements Listener {
+ public void handleEvent(Event event) {
+ IBugzillaBug bug = getBug();
+ fireSelectionChanged(new SelectionChangedEvent(selectionProvider, new StructuredSelection(new BugzillaReportSelection(bug.getId(), bug.getServer(), bug.getLabel(), false, bug.getSummary()))));
+ }
+ }
+
+ /**
+ * A listener to check if the summary field was modified.
+ */
+ protected class SummaryListener implements Listener {
+ public void handleEvent(Event event) {
+ handleSummaryEvent();
+ }
+ }
+
+ /**
+ * Check if the summary field was modified, and update it if necessary.
+ */
+ public abstract void handleSummaryEvent();
+
+ /*----------------------------------------------------------*
+ * CODE TO SCROLL TO A COMMENT OR OTHER PIECE OF TEXT
+ *----------------------------------------------------------*/
+
+ /** List of the StyledText's so that we can get the previous and the next */
+ protected ArrayList<StyledText> texts = new ArrayList<StyledText>();
+
+ protected HashMap<Object, StyledText> textHash = new HashMap<Object, StyledText>();
+
+ /** Index into the styled texts*/
+ protected int textsindex = 0;
+
+ protected Text addCommentsTextBox = null;
+
+ protected Text descriptionTextBox = null;
+
+ private StyledText previousText = null;
+
+ /**
+ * Selects the given object in the editor.
+ *
+ * @param commentNumber
+ * The comment number to be selected
+ */
+ public void select(int commentNumber){
+ if(commentNumber == -1)
+ return;
+
+ for(Object o: textHash.keySet()){
+ if(o instanceof Comment){
+ if(((Comment)o).getNumber() == commentNumber){
+ select(o, true);
+ }
+ }
+ }
+ }
+
+ /**
+ * Selects the given object in the editor.
+ *
+ * @param o
+ * The object to be selected.
+ * @param highlight
+ * Whether or not the object should be highlighted.
+ */
+ public void select(Object o, boolean highlight){
+ if(textHash.containsKey(o)){
+ StyledText t = textHash.get(o);
+ if(t != null){
+ focusOn(t, highlight);
+ }
+ } else if(o instanceof IBugzillaBug){
+ focusOn(null, highlight);
+ }
+ }
+
+ public void selectDescription(){
+ for(Object o: textHash.keySet()){
+ if(o.equals(bugzillaInput.getBug().getDescription())){
+ select(o, true);
+ }
+ }
+ }
+
+
+ public void selectNewComment(){
+ focusOn(addCommentsTextBox, false);
+ }
+
+ public void selectNewDescription(){
+ focusOn(descriptionTextBox, false);
+ }
+
+ /**
+ * Scroll to a specified piece of text
+ * @param selectionComposite The StyledText to scroll to
+ */
+ private void focusOn(Control selectionComposite, boolean highlight) {
+
+ int pos = 0;
+ if(previousText != null && !previousText.isDisposed()) {
+ previousText.setSelection(0);
+ }
+
+ if(selectionComposite instanceof StyledText)
+ previousText = (StyledText) selectionComposite;
+
+ if (selectionComposite != null) {
+
+ if(highlight && selectionComposite instanceof StyledText && !selectionComposite.isDisposed())
+ ((StyledText)selectionComposite).setSelection(0, ((StyledText)selectionComposite).getText().length());
+
+ // get the position of the text in the composite
+ pos = 0;
+ Control s = selectionComposite;
+ s.setEnabled(true);
+ s.setFocus();
+ s.forceFocus();
+ while(s != null && s != getEditorComposite()) {
+ if(!s.isDisposed()){
+ pos += s.getLocation().y;
+ s = s.getParent();
+ }
+ }
+
+ pos = scrolledComposite.getOrigin().y + pos - 60;
+ }
+ if(!scrolledComposite.isDisposed())
+ scrolledComposite.setOrigin(0, pos);
+ }
+
+ private BugzillaOutlinePage outlinePage = null;
+
+ @Override
+ public Object getAdapter(Class adapter) {
+ if (IContentOutlinePage.class.equals(adapter)) {
+ if (outlinePage == null && bugzillaInput != null) {
+ outlinePage = new BugzillaOutlinePage(model);
+ }
+ return outlinePage;
+ }
+ return super.getAdapter(adapter);
+ }
+
+ protected BugzillaOutlineNode model = null;
+
+ public BugzillaOutlineNode getModel(){
+ return model;
+ }
+
+ public BugzillaOutlinePage getOutline(){
+ return outlinePage;
+ }
+
+ private boolean isDisposed = false;
+
+ public boolean isDisposed(){
+ return isDisposed;
+ }
+
+ public void close() {
+ Display activeDisplay= getSite().getShell().getDisplay();
+ activeDisplay.asyncExec(new Runnable() {
+ public void run() {
+ if(getSite() != null && getSite().getPage() != null && !AbstractBugEditor.this.isDisposed())
+ getSite().getPage().closeEditor(AbstractBugEditor.this, false);
+ }
+ });
+ }
+
+ public void addAttributeListener(IBugzillaAttributeListener listener) {
+ attributesListeners.add(listener);
+ }
+
+ public void removeAttributeListener(IBugzillaAttributeListener listener) {
+ attributesListeners.remove(listener);
+ }
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/editor/AbstractBugEditorInput.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/editor/AbstractBugEditorInput.java
new file mode 100644
index 000000000..1cef2a0d0
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/editor/AbstractBugEditorInput.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2003 - 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.bugzilla.ui.editor;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.mylar.bugzilla.core.IBugzillaBug;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IPersistableElement;
+
+
+/**
+ * Abstract base implementation of an <code>IEditorInput</code> for a subclass of <code>AbstractBugEditor</code>.
+ */
+public abstract class AbstractBugEditorInput implements IEditorInput {
+
+ protected String toolTipText = "";
+
+ /**
+ * Sets the tool tip text for this editor input.
+ * @param str The new tool tip text.
+ */
+ protected void setToolTipText(String str) {
+ // 03-20-03 Allows editor to store title (once it is known)
+ toolTipText = str;
+ }
+
+ public boolean exists() {
+ return true;
+ }
+
+ /**
+ * @return The <code>IBugzillaBug</code> object for this editor input.
+ */
+ public abstract IBugzillaBug getBug();
+
+ public ImageDescriptor getImageDescriptor() {
+ return null;
+ }
+
+ public IPersistableElement getPersistable() {
+ return null;
+ }
+
+ public String getToolTipText() {
+ return toolTipText;
+ }
+
+ public Object getAdapter(Class adapter) {
+ return null;
+ }
+
+ /**
+ * @return <code>true</code> if the argument is an editor input on the
+ * same bug.
+ */
+ @Override
+ public abstract boolean equals(Object o);
+
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/editor/BugzillaEditorCopyAction.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/editor/BugzillaEditorCopyAction.java
new file mode 100644
index 000000000..38df0c8da
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/editor/BugzillaEditorCopyAction.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2003 - 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.bugzilla.ui.editor;
+
+import org.eclipse.jface.action.Action;
+
+/**
+ * Action used to copy selected text from a bug editor to the clipboard.
+ */
+public class BugzillaEditorCopyAction extends Action
+{
+ /** The editor to copy text selections from. */
+ private AbstractBugEditor bugEditor;
+
+ /**
+ * Creates a new <code>BugzillaEditorCopyAction</code>.
+ *
+ * @param editor
+ * The editor that this action is copying text selections from.
+ */
+ public BugzillaEditorCopyAction(AbstractBugEditor editor) {
+ bugEditor = editor;
+ setText("AbstractBugEditor.copy.text");
+ }
+
+ @Override
+ public void run() {
+ bugEditor.getCurrentText().copy();
+ }
+
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/editor/ExistingBugEditor.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/editor/ExistingBugEditor.java
new file mode 100644
index 000000000..19f1dc193
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/editor/ExistingBugEditor.java
@@ -0,0 +1,745 @@
+/*******************************************************************************
+ * Copyright (c) 2003 - 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.bugzilla.ui.editor;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.StringTokenizer;
+
+import javax.security.auth.login.LoginException;
+
+import org.eclipse.compare.CompareConfiguration;
+import org.eclipse.compare.CompareUI;
+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.action.GroupMarker;
+import org.eclipse.jface.action.IMenuListener;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.resource.JFaceColors;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.mylar.bugzilla.core.Attribute;
+import org.eclipse.mylar.bugzilla.core.BugPost;
+import org.eclipse.mylar.bugzilla.core.BugReport;
+import org.eclipse.mylar.bugzilla.core.BugzillaException;
+import org.eclipse.mylar.bugzilla.core.BugzillaPlugin;
+import org.eclipse.mylar.bugzilla.core.BugzillaRepository;
+import org.eclipse.mylar.bugzilla.core.Comment;
+import org.eclipse.mylar.bugzilla.core.IBugzillaBug;
+import org.eclipse.mylar.bugzilla.core.IBugzillaConstants;
+import org.eclipse.mylar.bugzilla.core.Operation;
+import org.eclipse.mylar.bugzilla.core.compare.BugzillaCompareInput;
+import org.eclipse.mylar.bugzilla.ui.OfflineView;
+import org.eclipse.mylar.bugzilla.ui.favorites.actions.AddToFavoritesAction;
+import org.eclipse.mylar.bugzilla.ui.outline.BugzillaOutlineNode;
+import org.eclipse.mylar.bugzilla.ui.outline.BugzillaReportSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IEditorSite;
+import org.eclipse.ui.IPartListener;
+import org.eclipse.ui.ISharedImages;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchActionConstants;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.internal.Workbench;
+
+
+/**
+ * An editor used to view a bug report that exists on a server. It uses a
+ * <code>BugReport</code> object to store the data.
+ */
+public class ExistingBugEditor extends AbstractBugEditor
+{
+
+ protected BugzillaCompareInput compareInput;
+ protected Button compareButton;
+ protected Button[] radios;
+ protected Combo[] radioOptions;
+ protected List keyWordsList;
+ protected Text keywordsText;
+ protected Text addCommentsText;
+ protected BugReport bug;
+
+
+ public String getNewCommentText(){
+ return addCommentsTextBox.getText();
+ }
+
+ /**
+ * Creates a new <code>ExistingBugEditor</code>.
+ */
+ public ExistingBugEditor() {
+ super();
+
+ // get the workbench page and add a listener so we can detect when it closes
+ IWorkbench wb = BugzillaPlugin.getDefault().getWorkbench();
+ IWorkbenchWindow aw = wb.getActiveWorkbenchWindow();
+ IWorkbenchPage ap = aw.getActivePage();
+ BugzillaEditorListener listener = new BugzillaEditorListener();
+ ap.addPartListener(listener);
+
+ // Set up the input for comparing the bug report to the server
+ CompareConfiguration config = new CompareConfiguration();
+ config.setLeftEditable(false);
+ config.setRightEditable(false);
+ config.setLeftLabel("Local Bug Report");
+ config.setRightLabel("Remote Bug Report");
+ config.setLeftImage(PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_ELEMENT));
+ config.setRightImage(PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_ELEMENT));
+ compareInput = new BugzillaCompareInput(config);
+ }
+
+ @Override
+ public void init(IEditorSite site, IEditorInput input) throws PartInitException {
+ if (!(input instanceof ExistingBugEditorInput))
+ throw new PartInitException("Invalid Input: Must be ExistingBugEditorInput");
+ ExistingBugEditorInput ei = (ExistingBugEditorInput) input;
+ setSite(site);
+ setInput(input);
+ bugzillaInput = ei;
+ model = BugzillaOutlineNode.parseBugReport(bugzillaInput.getBug());
+ bug = ei.getBug();
+ restoreBug();
+ updateEditorTitle();
+ }
+
+ /**
+ * This overrides the existing implementation in order to add
+ * an "add to favorites" option to the context menu.
+ *
+ * @see org.eclipse.mylar.bugzilla.ui.AbstractBugEditor#createContextMenu()
+ */
+ @Override
+ protected void createContextMenu() {
+ contextMenuManager = new MenuManager("#BugEditor");
+ contextMenuManager.setRemoveAllWhenShown(true);
+ contextMenuManager.addMenuListener(new IMenuListener() {
+ public void menuAboutToShow(IMenuManager manager) {
+ manager.add(new AddToFavoritesAction(ExistingBugEditor.this));
+ manager.add(new Separator());
+ manager.add(cutAction);
+ manager.add(copyAction);
+ manager.add(pasteAction);
+ manager.add(new Separator());
+ manager.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS));
+ if (currentSelectedText == null ||
+ currentSelectedText.getSelectionText().length() == 0) {
+
+ copyAction.setEnabled(false);
+ }
+ else {
+ copyAction.setEnabled(true);
+ }
+ }
+ });
+ getSite().registerContextMenu("#BugEditor", contextMenuManager,
+ getSite().getSelectionProvider());
+ }
+
+ @Override
+ protected void addRadioButtons(Composite buttonComposite) {
+ int i = 0;
+ radios = new Button[bug.getOperations().size()];
+ radioOptions = new Combo[bug.getAttributes().size()];
+ for (Iterator<Operation> it = bug.getOperations().iterator(); it.hasNext(); ) {
+ Operation o = it.next();
+ radios[i] = new Button(buttonComposite, SWT.RADIO);
+ GridData radioData = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
+ if (!o.hasOptions())
+ radioData.horizontalSpan = 4;
+ else
+ radioData.horizontalSpan = 3;
+ radioData.heightHint = 20;
+ radios[i].setText(o.getOperationName());
+ radios[i].setLayoutData(radioData);
+ radios[i].setBackground(background);
+ radios[i].addSelectionListener(new RadioButtonListener());
+ radios[i].addListener(SWT.FocusIn, new GenericListener());
+ if (i == 0 || o.isChecked()) {
+ radios[i].setSelection(true);
+ bug.setSelectedOperation(o);
+ }
+
+ if (o.hasOptions()) {
+ radioData = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
+ radioData.horizontalSpan = 1;
+ radioData.heightHint = 20;
+ radioData.widthHint = 80;
+ radioOptions[i] = new Combo(
+ buttonComposite,
+ SWT.NO_BACKGROUND
+ | SWT.MULTI
+ | SWT.V_SCROLL
+ | SWT.READ_ONLY);
+
+ radioOptions[i].setLayoutData(radioData);
+ radioOptions[i].setBackground(background);
+
+ Object [] a = o.getOptionNames().toArray();
+ Arrays.sort(a);
+ for (int j = 0; j < a.length; j++) {
+ radioOptions[i].add((String) a[j]);
+ }
+ radioOptions[i].select(0);
+ radioOptions[i].addSelectionListener(new RadioButtonListener());
+ }
+ i++;
+ }
+ }
+
+ @Override
+ protected void addActionButtons(Composite buttonComposite) {
+ super.addActionButtons(buttonComposite);
+
+ compareButton = new Button(buttonComposite, SWT.NONE);
+ GridData compareButtonData = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
+ compareButtonData.widthHint = 80;
+ compareButtonData.heightHint = 20;
+ compareButton.setText("Compare");
+ compareButton.setLayoutData(compareButtonData);
+ compareButton.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event e) {
+ OpenCompareEditorJob compareJob = new OpenCompareEditorJob("Comparing bug with remote server...");
+ compareJob.schedule();
+ }
+ });
+ compareButton.addListener(SWT.FocusIn, new GenericListener());
+ }
+
+ /**
+ * @return Returns the compareInput.
+ */
+ public BugzillaCompareInput getCompareInput() {
+ return compareInput;
+ }
+
+ @Override
+ public IBugzillaBug getBug() {
+ return bug;
+ }
+
+ @Override
+ protected String getTitleString() {
+ return bug.getLabel() + ": " + checkText(bug.getAttribute("Summary").getNewValue());
+ }
+
+ @Override
+ protected void submitBug() {
+ BugPost form = new BugPost();
+
+ // set the url for the bug to be submitted to
+ setURL(form, "process_bug.cgi");
+
+ // go through all of the attributes and add them to the bug post
+ for (Iterator<Attribute> it = bug.getAttributes().iterator(); it.hasNext(); ) {
+ Attribute a = it.next();
+ if (a != null && a.getParameterName() != null && a.getParameterName().compareTo("") != 0 && !a.isHidden()) {
+ String value = a.getNewValue();
+
+ // add the attribute to the bug post
+ form.add(a.getParameterName(), checkText(value));
+ }
+ else if(a != null && a.getParameterName() != null && a.getParameterName().compareTo("") != 0 && a.isHidden()) {
+ // we have a hidden attribute and we should send it back.
+ form.add(a.getParameterName(), a.getValue());
+ }
+ }
+
+ // make sure that the comment is broken up into 80 character lines
+ bug.setNewNewComment(formatText(bug.getNewNewComment()));
+
+ // add the summary to the bug post
+ form.add("short_desc", bug.getAttribute("Summary").getNewValue());
+
+ // add the operation to the bug post
+ Operation o = bug.getSelectedOperation();
+ if (o == null)
+ form.add("knob", "none");
+ else {
+ form.add("knob", o.getKnobName());
+ if(o.hasOptions()) {
+ String sel = o.getOptionValue(o.getOptionSelection());
+ form.add(o.getOptionName(), sel);
+ }
+ }
+ form.add("form_name", "process_bug");
+
+
+ // add the new comment to the bug post if there is some text in it
+ if(bug.getNewNewComment().length() != 0) {
+ form.add("comment", bug.getNewNewComment());
+ }
+
+ try {
+ form.post();
+
+ changeDirtyStatus(false);
+ BugzillaPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getActivePage().closeEditor(this, false);
+ OfflineView.removeReport(bug);
+ } catch (BugzillaException e) {
+ BugzillaPlugin.getDefault().logAndShowExceptionDetailsDialog(e, "occurred while posting the bug.", "I/O Error");
+ }
+ catch (LoginException e) {
+ MessageDialog.openError(null, "Login Error",
+ "Bugzilla could not post your bug since your login name or password is incorrect.\nPlease check your settings in the bugzilla preferences. ");
+ }
+ }
+
+ @Override
+ protected void createDescriptionLayout() {
+ // Description Area
+ Composite descriptionComposite = new Composite(infoArea, SWT.NONE);
+ GridLayout descriptionLayout = new GridLayout();
+ descriptionLayout.numColumns = 4;
+ descriptionComposite.setLayout(descriptionLayout);
+ descriptionComposite.setBackground(background);
+ GridData descriptionData = new GridData(GridData.FILL_BOTH);
+ descriptionData.horizontalSpan = 1;
+ descriptionData.grabExcessVerticalSpace = false;
+ descriptionComposite.setLayoutData(descriptionData);
+ // End Description Area
+
+ StyledText t = newLayout(descriptionComposite, 4, "Description:", HEADER);
+ t.addListener(SWT.FocusIn, new DescriptionListener());
+ t = newLayout(descriptionComposite, 4, bug.getDescription(), VALUE);
+ t.addListener(SWT.FocusIn, new DescriptionListener());
+
+ texts.add(textsindex, t);
+ textHash.put(bug.getDescription(), t);
+ textsindex++;
+
+ }
+
+ @Override
+ protected void createCommentLayout() {
+ SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm");
+
+ // Additional (read-only) Comments Area
+ Composite addCommentsComposite = new Composite(infoArea, SWT.NONE);
+ GridLayout addCommentsLayout = new GridLayout();
+ addCommentsLayout.numColumns = 4;
+ addCommentsComposite.setLayout(addCommentsLayout);
+ addCommentsComposite.setBackground(background);
+ GridData addCommentsData = new GridData(GridData.FILL_BOTH);
+ addCommentsData.horizontalSpan = 1;
+ addCommentsData.grabExcessVerticalSpace = false;
+ addCommentsComposite.setLayoutData(addCommentsData);
+ // End Additional (read-only) Comments Area
+
+ StyledText t = null;
+ for (Iterator<Comment> it = bug.getComments().iterator(); it.hasNext(); ) {
+ Comment comment = it.next();
+ String commentHeader = "Additional comment #" + comment.getNumber() + " from "
+ + comment.getAuthorName() + " on "
+ + df.format(comment.getCreated());
+ t = newLayout(addCommentsComposite, 4, commentHeader, HEADER);
+ t.addListener(SWT.FocusIn, new CommentListener(comment));
+ t = newLayout(addCommentsComposite, 4, comment.getText(), VALUE);
+ t.addListener(SWT.FocusIn, new CommentListener(comment));
+
+ //code for outline
+ texts.add(textsindex, t);
+ textHash.put(comment, t);
+ textsindex++;
+ }
+
+ // Additional Comments Text
+ Composite addCommentsTitleComposite =
+ new Composite(addCommentsComposite, SWT.NONE);
+ GridLayout addCommentsTitleLayout = new GridLayout();
+ addCommentsTitleLayout.horizontalSpacing = 0;
+ addCommentsTitleLayout.marginWidth = 0;
+ addCommentsTitleComposite.setLayout(addCommentsTitleLayout);
+ addCommentsTitleComposite.setBackground(background);
+ GridData addCommentsTitleData =
+ new GridData(GridData.HORIZONTAL_ALIGN_FILL);
+ addCommentsTitleData.horizontalSpan = 4;
+ addCommentsTitleData.grabExcessVerticalSpace = false;
+ addCommentsTitleComposite.setLayoutData(addCommentsTitleData);
+ newLayout(
+ addCommentsTitleComposite,
+ 4,
+ "New Additional Comment:",
+ HEADER).addListener(SWT.FocusIn, new NewCommentListener());
+ addCommentsText =
+ new Text(
+ addCommentsComposite,
+ SWT.BORDER | SWT.MULTI | SWT.WRAP | SWT.V_SCROLL);
+ GridData addCommentsTextData =
+ new GridData(GridData.HORIZONTAL_ALIGN_FILL);
+ addCommentsTextData.horizontalSpan = 4;
+ addCommentsTextData.widthHint = 250;
+ addCommentsTextData.heightHint = 100;
+ addCommentsText.setLayoutData(addCommentsTextData);
+ addCommentsText.setText(bug.getNewComment());
+ addCommentsText.addListener(SWT.FocusOut, new Listener() {
+ public void handleEvent(Event event) {
+ String sel = addCommentsText.getText();
+ if (!(bug.getNewNewComment().equals(sel))) {
+ bug.setNewNewComment(sel);
+ changeDirtyStatus(true);
+ }
+ }
+ });
+ addCommentsText.addListener(SWT.FocusIn, new NewCommentListener());
+ // End Additional Comments Text
+
+ addCommentsTextBox = addCommentsText;
+
+ this.createSeparatorSpace(addCommentsComposite);
+ }
+
+ @Override
+ protected void addKeywordsList(String keywords, Composite attributesComposite) {
+ newLayout(attributesComposite, 1, "Keywords:", PROPERTY);
+ keywordsText = new Text(attributesComposite, SWT.BORDER);
+ keywordsText.setEditable(false);
+ keywordsText.setForeground(foreground);
+ keywordsText.setBackground(JFaceColors.getErrorBackground(display));
+ GridData keywordsData = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
+ keywordsData.horizontalSpan = 2;
+ keywordsData.widthHint = 200;
+ keywordsText.setLayoutData(keywordsData);
+ keywordsText.setText(keywords);
+ keywordsText.addListener(SWT.FocusIn, new GenericListener());
+ keyWordsList = new List(attributesComposite, SWT.MULTI | SWT.V_SCROLL | SWT.BORDER);
+ GridData keyWordsTextData =
+ new GridData(GridData.HORIZONTAL_ALIGN_FILL);
+ keyWordsTextData.horizontalSpan = 1;
+ keyWordsTextData.widthHint = 125;
+ keyWordsTextData.heightHint = 40;
+ keyWordsList.setLayoutData(keyWordsTextData);
+
+ // initialize the keywords list with valid values
+ java.util.List<String> keywordList = bug.getKeywords();
+ if (keywordList != null) {
+ for (Iterator<String> it = keywordList.iterator(); it.hasNext(); ) {
+ String keyword = it.next();
+ keyWordsList.add(keyword);
+ }
+
+ // get the selected keywords for the bug
+ StringTokenizer st = new StringTokenizer(keywords, ",", false);
+ ArrayList<Integer> indicies = new ArrayList<Integer>();
+ while (st.hasMoreTokens()) {
+ String s = st.nextToken().trim();
+ int index = keyWordsList.indexOf(s);
+ if (index != -1)
+ indicies.add(new Integer(index));
+ }
+
+ // select the keywords that were selected for the bug
+ int length = indicies.size();
+ int[] sel = new int[length];
+ for (int i = 0; i < length; i++) {
+ sel[i] = indicies.get(i).intValue();
+ }
+ keyWordsList.select(sel);
+ }
+
+ keyWordsList.addSelectionListener(new KeywordListener());
+ keyWordsList.addListener(SWT.FocusIn, new GenericListener());
+ }
+
+ @Override
+ protected void updateBug() {
+
+ // go through all of the attributes and update the main values to the new ones
+ for (Iterator<Attribute> it = bug.getAttributes().iterator(); it.hasNext(); ) {
+ Attribute a = it.next();
+ a.setValue(a.getNewValue());
+ }
+
+ // Update some other fields as well.
+ bug.setNewComment(bug.getNewNewComment());
+
+ }
+
+ @Override
+ protected void restoreBug() {
+
+ // go through all of the attributes and restore the new values to the main ones
+ for (Iterator<Attribute> it = bug.getAttributes().iterator(); it.hasNext(); ) {
+ Attribute a = it.next();
+ a.setNewValue(a.getValue());
+ }
+
+ // Restore some other fields as well.
+ bug.setNewNewComment(bug.getNewComment());
+ }
+
+ /**
+ * This job opens a compare editor to compare the current state of the bug
+ * in the editor with the bug on the server.
+ */
+ protected class OpenCompareEditorJob extends Job {
+
+ public OpenCompareEditorJob(String name) {
+ super(name);
+ }
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ final BugReport serverBug;
+ try {
+ serverBug = BugzillaRepository.getInstance().getBug(bug.getId());
+ // If no bug was found on the server, throw an exception so that the
+ // user gets the same message that appears when there is a problem reading the server.
+ if (serverBug == null)
+ throw new Exception();
+ } catch (Exception e) {
+ Workbench.getInstance().getDisplay().asyncExec(new Runnable() {
+ public void run() {
+ MessageDialog.openInformation(Workbench.getInstance().getActiveWorkbenchWindow().getShell(),
+ "Could not open bug.", "Bug #" + bug.getId() + " could not be read from the server.");
+ }
+ });
+ return new Status(IStatus.OK, IBugzillaConstants.PLUGIN_ID, IStatus.OK, "Could not get the bug report from the server.", null);
+ }
+ Workbench.getInstance().getDisplay().asyncExec(new Runnable() {
+ public void run() {
+ compareInput.setTitle("Bug #" + bug.getId());
+ compareInput.setLeft(bug);
+ compareInput.setRight(serverBug);
+ CompareUI.openCompareEditor(compareInput);
+ }
+ });
+ return new Status(IStatus.OK, IBugzillaConstants.PLUGIN_ID, IStatus.OK, "", null);
+ }
+
+ }
+
+ /**
+ * Class to listen for editor events.
+ */
+ protected class BugzillaEditorListener implements IPartListener
+ {
+
+ /**
+ * @see org.eclipse.ui.IPartListener#partActivated(org.eclipse.ui.IWorkbenchPart)
+ */
+ public void partActivated(IWorkbenchPart part) {
+ // no need to listen to this
+ }
+
+ /**
+ * @see org.eclipse.ui.IPartListener#partBroughtToTop(org.eclipse.ui.IWorkbenchPart)
+ */
+ public void partBroughtToTop(IWorkbenchPart part) {
+ // no need to listen to this
+ }
+
+ /**
+ * @see org.eclipse.ui.IPartListener#partClosed(org.eclipse.ui.IWorkbenchPart)
+ */
+ public void partClosed(IWorkbenchPart part) {
+
+ if (part instanceof ExistingBugEditor) {
+
+ ExistingBugEditor editor = (ExistingBugEditor)part;
+
+ // check if the bug editor needs to be saved
+ if (editor.isDirty) {
+ // ask the user whether they want to save it or not and perform the appropriate action
+ editor.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) {
+ editor.saveBug();
+ }
+ }
+
+ // get the active workbench page
+ IWorkbenchPage page = BugzillaPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getActivePage();
+
+ if (page != null) {
+ // Close the compare editor, if there is one
+ IEditorPart compareEditor = page.findEditor(getCompareInput());
+ if (compareEditor != null) {
+ page.closeEditor(compareEditor, false);
+ }
+ }
+
+ }
+ }
+
+ /**
+ * @see org.eclipse.ui.IPartListener#partDeactivated(org.eclipse.ui.IWorkbenchPart)
+ */
+ public void partDeactivated(IWorkbenchPart part) {
+ // no need to listen to this
+ }
+
+ /**
+ * @see org.eclipse.ui.IPartListener#partOpened(org.eclipse.ui.IWorkbenchPart)
+ */
+ public void partOpened(IWorkbenchPart part) {
+ // no need to listen to this
+ }
+ }
+
+ /**
+ * Class to handle the selection change of the keywords.
+ */
+ protected class KeywordListener implements SelectionListener {
+
+ /*
+ * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
+ */
+ public void widgetSelected(SelectionEvent arg0) {
+ changeDirtyStatus(true);
+
+ // get the selected keywords and create a string to submit
+ StringBuffer keywords = new StringBuffer();
+ String [] sel = keyWordsList.getSelection();
+
+ // allow unselecting 1 keyword when it is the only one selected
+ if(keyWordsList.getSelectionCount() == 1) {
+ int index = keyWordsList.getSelectionIndex();
+ String keyword = keyWordsList.getItem(index);
+ if (bug.getAttribute("Keywords").getNewValue().equals(keyword))
+ keyWordsList.deselectAll();
+ }
+
+ for(int i = 0; i < keyWordsList.getSelectionCount(); i++) {
+ keywords.append(sel[i]);
+ if (i != keyWordsList.getSelectionCount()-1) {
+ keywords.append(",");
+ }
+ }
+ bug.getAttribute("Keywords").setNewValue(keywords.toString());
+
+ // update the keywords text field
+ keywordsText.setText(keywords.toString());
+ }
+
+ /*
+ * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
+ */
+ public void widgetDefaultSelected(SelectionEvent arg0) {
+ // no need to listen to this
+ }
+
+ }
+
+ /**
+ * A listener for selection of the description field.
+ */
+ protected class DescriptionListener implements Listener {
+ public void handleEvent(Event event) {
+ fireSelectionChanged(new SelectionChangedEvent(selectionProvider, new StructuredSelection(new BugzillaReportSelection(bug.getId(), bug.getServer(), "Description", true, bug.getSummary()))));
+ }
+ }
+
+ /**
+ * A listener for selection of a comment.
+ */
+ protected class CommentListener implements Listener {
+
+ /** The comment that this listener is for. */
+ private Comment comment;
+
+ /**
+ * Creates a new <code>CommentListener</code>.
+ * @param comment The comment that this listener is for.
+ */
+ public CommentListener(Comment comment) {
+ this.comment = comment;
+ }
+
+ public void handleEvent(Event event) {
+ fireSelectionChanged(new SelectionChangedEvent(selectionProvider, new StructuredSelection(new BugzillaReportSelection(bug.getId(), bug.getServer(), comment.getCreated().toString(), comment, bug.getSummary()))));
+ }
+ }
+
+ /**
+ * A listener for selection of the textbox where a new comment is entered
+ * in.
+ */
+ protected class NewCommentListener implements Listener {
+ public void handleEvent(Event event) {
+ fireSelectionChanged(new SelectionChangedEvent(selectionProvider, new StructuredSelection(new BugzillaReportSelection(bug.getId(), bug.getServer(), "New Comment", false, bug.getSummary()))));
+ }
+ }
+
+ /**
+ * Class to handle the selection change of the radio buttons.
+ */
+ protected class RadioButtonListener implements SelectionListener {
+
+ public void widgetDefaultSelected(SelectionEvent e) {
+ widgetSelected(e);
+ }
+
+ public void widgetSelected(SelectionEvent e) {
+ Button selected = null;
+ for (int i = 0; i < radios.length; i++) {
+ if (radios[i].getSelection())
+ selected = radios[i];
+ }
+ // determine the operation to do to the bug
+ for (int i = 0; i < radios.length; i++) {
+ if (radios[i] != e.widget && radios[i] != selected)
+ radios[i].setSelection(false);
+ if (e.widget == radios[i]) {
+ Operation o = bug.getOperation(radios[i].getText());
+ bug.setSelectedOperation(o);
+ }
+ else if(e.widget == radioOptions[i]) {
+ Operation o = bug.getOperation(radios[i].getText());
+ o.setOptionSelection(radioOptions[i].getItem(radioOptions[i].getSelectionIndex()));
+ bug.setSelectedOperation(o);
+ radios[i].setSelection(true);
+ if(selected != null && selected != radios[i])
+ selected.setSelection(false);
+ }
+ }
+ if(addCommentsText.getText() == null || addCommentsText.getText().equals("")){
+ addCommentsText.setText("Resolved.");
+ }
+ }
+
+ }
+
+ @Override
+ public void handleSummaryEvent() {
+ String sel = summaryText.getText();
+ Attribute a = getBug().getAttribute("Summary");
+ if (!(a.getNewValue().equals(sel))) {
+ a.setNewValue(sel);
+ changeDirtyStatus(true);
+ }
+ }
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/editor/ExistingBugEditorInput.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/editor/ExistingBugEditorInput.java
new file mode 100644
index 000000000..02169c600
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/editor/ExistingBugEditorInput.java
@@ -0,0 +1,86 @@
+/*******************************************************************************
+ * Copyright (c) 2003 - 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.bugzilla.ui.editor;
+
+import java.io.IOException;
+
+import javax.security.auth.login.LoginException;
+
+import org.eclipse.mylar.bugzilla.core.BugReport;
+import org.eclipse.mylar.bugzilla.core.BugzillaRepository;
+
+
+/**
+ * The <code>IEditorInput</code> implementation for <code>ExistingBugEditor</code>.
+ */
+public class ExistingBugEditorInput extends AbstractBugEditorInput
+{
+ protected int bugId;
+
+ protected BugReport bug;
+
+ /**
+ * Creates a new <code>ExistingBugEditorInput</code>.
+ * @param bug The bug for this editor input.
+ */
+ public ExistingBugEditorInput(BugReport bug) {
+ this.bug = bug;
+ this.bugId = bug.getId();
+ }
+
+ /**
+ * Creates a new <code>ExistingBugEditorInput</code>. An exception is
+ * thrown if the bug could not be obtained from the server.
+ *
+ * @param bugId
+ * The id of the bug for this editor input.
+ * @throws LoginException
+ * @throws IOException
+ */
+ public ExistingBugEditorInput(int bugId) throws LoginException, IOException {
+ this.bugId = bugId;
+
+ // get the bug from the server if it exists
+ bug = BugzillaRepository.getInstance().getCurrentBug(bugId);
+ }
+
+ /*
+ * @see IEditorInput#getName()
+ */
+ public String getName() {
+ return bug.getLabel();
+ }
+
+ /**
+ * @return The id of the bug for this editor input.
+ */
+ public int getBugId() {
+ return bugId;
+ }
+
+ @Override
+ public BugReport getBug() {
+ return bug;
+ }
+
+ /**
+ * @return <code>true</code> if the argument is a bug report editor input
+ * on the same bug id.
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof ExistingBugEditorInput) {
+ ExistingBugEditorInput input = (ExistingBugEditorInput) o;
+ return getBugId() == input.getBugId();
+ }
+ return false;
+ }
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/editor/NewBugEditor.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/editor/NewBugEditor.java
new file mode 100644
index 000000000..375b840d7
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/editor/NewBugEditor.java
@@ -0,0 +1,361 @@
+/*******************************************************************************
+ * Copyright (c) 2003 - 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.bugzilla.ui.editor;
+
+import java.util.Iterator;
+
+import javax.security.auth.login.LoginException;
+
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.mylar.bugzilla.core.Attribute;
+import org.eclipse.mylar.bugzilla.core.BugPost;
+import org.eclipse.mylar.bugzilla.core.BugzillaException;
+import org.eclipse.mylar.bugzilla.core.BugzillaPlugin;
+import org.eclipse.mylar.bugzilla.core.IBugzillaBug;
+import org.eclipse.mylar.bugzilla.core.NewBugModel;
+import org.eclipse.mylar.bugzilla.ui.OfflineView;
+import org.eclipse.mylar.bugzilla.ui.outline.BugzillaOutlineNode;
+import org.eclipse.mylar.bugzilla.ui.outline.BugzillaReportSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.IEditorInput;
+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;
+
+
+/**
+ * An editor used to view a locally created bug that does not yet exist on a
+ * server. It uses a <code>NewBugModel</code> object to store the data.
+ */
+public class NewBugEditor extends AbstractBugEditor {
+
+ protected NewBugModel bug;
+ protected Text descriptionText;
+ protected String newSummary = "";
+ protected String newDescription = "";
+
+ /**
+ * Creates a new <code>NewBugEditor</code>.
+ */
+ public NewBugEditor() {
+ super();
+
+ // get the workbench page and add a listener so we can detect when it closes
+ IWorkbench wb = BugzillaPlugin.getDefault().getWorkbench();
+ IWorkbenchWindow aw = wb.getActiveWorkbenchWindow();
+ IWorkbenchPage ap = aw.getActivePage();
+ BugzillaEditorListener listener = new BugzillaEditorListener();
+ ap.addPartListener(listener);
+ }
+
+ @Override
+ public IBugzillaBug getBug() {
+ return bug;
+ }
+
+ @Override
+ protected void addKeywordsList(String keywords, Composite attributesComposite) {
+ // Since NewBugModels have no keywords, there is no
+ // GUI for them.
+ }
+
+ @Override
+ protected void createDescriptionLayout() {
+
+ // Description Area
+ Composite descriptionComposite = new Composite(infoArea, SWT.NONE);
+ GridLayout descriptionLayout = new GridLayout();
+ descriptionLayout.numColumns = 4;
+ descriptionComposite.setLayout(descriptionLayout);
+ descriptionComposite.setBackground(background);
+ GridData descriptionData = new GridData(GridData.FILL_BOTH);
+ descriptionData.horizontalSpan = 1;
+ descriptionData.grabExcessVerticalSpace = false;
+ descriptionComposite.setLayoutData(descriptionData);
+ // End Description Area
+
+ Composite descriptionTitleComposite =
+ new Composite(descriptionComposite, SWT.NONE);
+ GridLayout descriptionTitleLayout = new GridLayout();
+ descriptionTitleLayout.horizontalSpacing = 0;
+ descriptionTitleLayout.marginWidth = 0;
+ descriptionTitleComposite.setLayout(descriptionTitleLayout);
+ descriptionTitleComposite.setBackground(background);
+ GridData descriptionTitleData =
+ new GridData(GridData.HORIZONTAL_ALIGN_FILL);
+ descriptionTitleData.horizontalSpan = 4;
+ descriptionTitleData.grabExcessVerticalSpace = false;
+ descriptionTitleComposite.setLayoutData(descriptionTitleData);
+ newLayout(descriptionTitleComposite, 4, "Description:", HEADER).addListener(SWT.FocusIn, new DescriptionListener());
+
+ descriptionText =
+ new Text(descriptionComposite,
+ SWT.BORDER | SWT.MULTI | SWT.WRAP | SWT.V_SCROLL);
+ GridData descriptionTextData = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
+ descriptionTextData.horizontalSpan = 4;
+ descriptionTextData.widthHint = 250;
+ descriptionTextData.heightHint = 100;
+ descriptionText.setLayoutData(descriptionTextData);
+ descriptionText.setText(bug.getDescription());
+ descriptionText.addListener(SWT.FocusOut, new Listener() {
+ public void handleEvent(Event event) {
+ String sel = descriptionText.getText();
+ if (!(newDescription.equals(sel))) {
+ newDescription = sel;
+ changeDirtyStatus(true);
+ }
+ }
+ });
+ descriptionText.addListener(SWT.FocusIn, new DescriptionListener());
+
+ super.descriptionTextBox = descriptionText;
+
+ this.createSeparatorSpace(descriptionComposite);
+}
+
+ @Override
+ protected void createCommentLayout() {
+ // Since NewBugModels have no comments, there is no
+ // GUI for them.
+ }
+
+ @Override
+ protected void addRadioButtons(Composite buttonComposite) {
+ // Since NewBugModels have no special submitting actions,
+ // no radio buttons are required.
+ }
+
+ @Override
+ protected String getTitleString() {
+ return bug.getLabel();
+ }
+
+ @Override
+ protected void submitBug() {
+ BugPost form = new BugPost();
+ form.setPrefix("Bug ");
+ form.setPostfix1(" posted");
+ form.setPostfix2(" Submitted");
+ updateBug();
+
+ setURL(form, "post_bug.cgi");
+ // go through all of the attributes and add them to the bug post
+ Iterator<Attribute> itr = bug.getAttributes().iterator();
+ while (itr.hasNext()) {
+ Attribute a = itr.next();
+ if (a != null && a.getParameterName() != null
+ && a.getParameterName().compareTo("") != 0
+ && !a.isHidden()) {
+ String key = a.getName();
+ String value = null;
+
+ // get the values from the attribute
+ if (key.equalsIgnoreCase("OS")) {
+ value = a.getValue();
+ } else if (key.equalsIgnoreCase("Version")) {
+ value = a.getValue();
+ } else if (key.equalsIgnoreCase("Severity")) {
+ value = a.getValue();
+ } else if (key.equalsIgnoreCase("Platform")) {
+ value = a.getValue();
+ } else if (key.equalsIgnoreCase("Component")) {
+ value = a.getValue();
+ } else if (key.equalsIgnoreCase("Priority")) {
+ value = a.getValue();
+ } else if (key.equalsIgnoreCase("URL")) {
+ value = a.getValue();
+ }
+
+ // add the attribute to the bug post
+ if (value == null)
+ value = "";
+
+ form.add(a.getParameterName(), value);
+ } else if (a != null && a.getParameterName() != null
+ && a.getParameterName().compareTo("") != 0
+ && a.isHidden()) {
+ // we have a hidden attribute, add it to the posting
+ form.add(a.getParameterName(), a.getValue());
+
+ }
+
+ }
+
+ // set the summary, and description
+
+ // add the summary to the bug post
+ form.add("short_desc", bug.getSummary());
+
+ // format the description of the bug so that it is roughly in 80
+ // character lines
+ bug.setDescription(formatText(bug.getDescription()));
+
+ if (bug.getDescription().length() != 0) {
+ // add the new comment to the bug post if there is some text in
+ // it
+ form.add("comment", bug.getDescription());
+ }
+
+ // update the bug on the server
+ try {
+ String id = form.post();
+
+ // If the bug was successfully sent...
+ if (id != null) {
+ changeDirtyStatus(false);
+ BugzillaPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getActivePage().closeEditor(this, false);
+ OfflineView.removeReport(bug);
+ }
+
+ } catch (BugzillaException e) {
+ MessageDialog
+ .openError(
+ null,
+ "I/O Error",
+ "Bugzilla could not post your bug.");
+ BugzillaPlugin.log(e);
+ } catch (LoginException e) {
+ // if we had an error with logging in, display an error
+ MessageDialog
+ .openError(
+ null,
+ "Posting Error",
+ "Bugzilla could not post your bug since your login name or password is incorrect."
+ + "\nPlease check your settings in the bugzilla preferences. ");
+ }
+ }
+
+ @Override
+ protected void updateBug() {
+ // go through all of the attributes and update the main values to the new ones
+ for (Iterator<Attribute> it = bug.getAttributes().iterator(); it.hasNext(); ) {
+ Attribute a = it.next();
+ a.setValue(a.getNewValue());
+ }
+
+ // Update some other fields as well.
+ bug.setSummary(newSummary);
+ bug.setDescription(newDescription);
+ }
+
+ @Override
+ protected void restoreBug() {
+ // go through all of the attributes and restore the new values to the main ones
+ for (Iterator<Attribute> it = bug.getAttributes().iterator(); it.hasNext(); ) {
+ Attribute a = it.next();
+ a.setNewValue(a.getValue());
+ }
+ }
+
+ @Override
+ public void init(IEditorSite site, IEditorInput input) throws PartInitException {
+ if (!(input instanceof NewBugEditorInput))
+ throw new PartInitException("Invalid Input: Must be NewBugEditorInput");
+ NewBugEditorInput ei = (NewBugEditorInput) input;
+ setSite(site);
+ setInput(input);
+ bugzillaInput = ei;
+ model = BugzillaOutlineNode.parseBugReport(bugzillaInput.getBug());
+ bug = ei.getBug();
+ newSummary = bug.getSummary();
+ newDescription = bug.getDescription();
+ restoreBug();
+ updateEditorTitle();
+ }
+
+ /**
+ * Class to listen for editor events.
+ */
+ protected class BugzillaEditorListener implements IPartListener
+ {
+
+ /**
+ * @see org.eclipse.ui.IPartListener#partActivated(org.eclipse.ui.IWorkbenchPart)
+ */
+ public void partActivated(IWorkbenchPart part) {
+ // don't need to listen to this
+ }
+
+ /**
+ * @see org.eclipse.ui.IPartListener#partBroughtToTop(org.eclipse.ui.IWorkbenchPart)
+ */
+ public void partBroughtToTop(IWorkbenchPart part) {
+ // don't need to listen to this
+ }
+
+ /**
+ * @see org.eclipse.ui.IPartListener#partClosed(org.eclipse.ui.IWorkbenchPart)
+ */
+ public void partClosed(IWorkbenchPart part) {
+
+ if (part instanceof NewBugEditor) {
+
+ NewBugEditor editor = (NewBugEditor)part;
+
+ // check if the bug editor needs to be saved
+ if (editor.isDirty) {
+ // ask the user whether they want to save it or not and perform the appropriate action
+ editor.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) {
+ editor.saveBug();
+ }
+ }
+ }
+ }
+
+ /**
+ * @see org.eclipse.ui.IPartListener#partDeactivated(org.eclipse.ui.IWorkbenchPart)
+ */
+ public void partDeactivated(IWorkbenchPart part) {
+ // don't need to listen to this
+ }
+
+ /**
+ * @see org.eclipse.ui.IPartListener#partOpened(org.eclipse.ui.IWorkbenchPart)
+ */
+ public void partOpened(IWorkbenchPart part) {
+ // don't need to listen to this
+ }
+ }
+
+ /**
+ * A listener for selection of the description textbox.
+ */
+ protected class DescriptionListener implements Listener {
+ public void handleEvent(Event event) {
+ fireSelectionChanged(new SelectionChangedEvent(selectionProvider, new StructuredSelection(new BugzillaReportSelection(bug.getId(), bug.getServer(), "New Description", false, bug.getSummary()))));
+ }
+ }
+
+ @Override
+ public void handleSummaryEvent() {
+ String sel = summaryText.getText();
+ if (!(newSummary.equals(sel))) {
+ newSummary = sel;
+ changeDirtyStatus(true);
+ }
+ }
+
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/editor/NewBugEditorInput.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/editor/NewBugEditorInput.java
new file mode 100644
index 000000000..92fd3dc92
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/editor/NewBugEditorInput.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2003 - 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.bugzilla.ui.editor;
+
+import org.eclipse.mylar.bugzilla.core.NewBugModel;
+
+/**
+ * The <code>IEditorInput</code> implementation for <code>NewBugEditor</code>.
+ */
+public class NewBugEditorInput extends AbstractBugEditorInput {
+
+ protected NewBugModel bug;
+
+ /**
+ * Creates a new <code>NewBugEditorInput</code>.
+ * @param bug The bug for this editor input.
+ */
+ public NewBugEditorInput(NewBugModel bug) {
+ this.bug = bug;
+ }
+
+ public String getName() {
+ return bug.getLabel();
+ }
+
+ @Override
+ public NewBugModel getBug() {
+ return bug;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof NewBugEditorInput) {
+ NewBugEditorInput input = (NewBugEditorInput) o;
+ return input.getBug().equals(bug);
+ }
+ return false;
+ }
+
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/favorites/actions/AbstractFavoritesAction.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/favorites/actions/AbstractFavoritesAction.java
new file mode 100644
index 000000000..2caf881be
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/favorites/actions/AbstractFavoritesAction.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2003 - 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.bugzilla.ui.favorites.actions;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.mylar.bugzilla.core.BugzillaPlugin;
+import org.eclipse.swt.widgets.Shell;
+
+
+/**
+ * Class that contains shared functions for the favorites actions
+ */
+public class AbstractFavoritesAction extends Action
+{
+
+ /**
+ * Set the actions icon
+ * @param filename The icons file
+ */
+ protected void setIcon(String filename)
+ {
+ URL url = null;
+ try
+ {
+ // try to change the default icon
+ url = new URL(BugzillaPlugin.getDefault().getBundle().getEntry("/"), filename);
+ setImageDescriptor(ImageDescriptor.createFromURL(url));
+ }
+ catch (MalformedURLException e)
+ {
+ // Simply don't change the default icon
+ }
+ }
+
+ /**
+ * Convienience method for getting the current shell
+ * @return The current shell
+ */
+ protected Shell getShell()
+ {
+ return BugzillaPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getShell();
+ }
+
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/favorites/actions/AddFavoriteAction.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/favorites/actions/AddFavoriteAction.java
new file mode 100644
index 000000000..3b88ccc93
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/favorites/actions/AddFavoriteAction.java
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Copyright (c) 2003 - 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.bugzilla.ui.favorites.actions;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.mylar.bugzilla.core.BugzillaPlugin;
+import org.eclipse.mylar.bugzilla.core.IBugzillaConstants;
+import org.eclipse.mylar.bugzilla.core.favorites.Favorite;
+import org.eclipse.mylar.bugzilla.ui.FavoritesView;
+import org.eclipse.mylar.bugzilla.ui.search.BugzillaSearchResultView;
+
+
+/**
+ * Bookmark a returned Bugzilla Search result.
+ */
+public class AddFavoriteAction extends AbstractFavoritesAction {
+
+ /** Selected objects */
+ private List<Favorite> selected;
+
+ /** The view where the Bugzilla search results are displayed */
+ private BugzillaSearchResultView resultView;
+
+ /**
+ * Constructor
+ * @param text The label for the context menu item
+ * @param resultView The view where the Bugzilla search results are displayed
+ */
+ public AddFavoriteAction(String text, BugzillaSearchResultView resultView) {
+ setText(text);
+ setIcon("icons/elcl16/bug-favorite.gif");
+ this.resultView = resultView;
+ selected = null;
+ }
+
+ /**
+ * Bookmark the selected items
+ * @see org.eclipse.ui.actions.ActionDelegate#run(IAction)
+ */
+ @Override
+ public void run()
+ {
+ FavoritesView.checkWindow();
+
+ selected = new ArrayList<Favorite>();
+ buildSelection();
+
+ // go through each of the selected items and add its data to the favorites file
+ for (int i = 0; i < selected.size(); i++)
+ {
+ BugzillaPlugin.getDefault().getFavorites().add(selected.get(i));
+ }
+ FavoritesView.add();
+ FavoritesView.updateActionEnablement();
+ }
+
+ /**
+ * Gets the entire selection and puts each bug report into a list. Only the
+ * relevent parts of each bug report are put into the list.
+ */
+ @SuppressWarnings("unchecked")
+ private void buildSelection() {
+ ISelection s = resultView.getViewer().getSelection();
+ if (s instanceof IStructuredSelection) {
+ IStructuredSelection selection = (IStructuredSelection) s;
+ for (Iterator<IMarker> it = selection.iterator(); it.hasNext();) {
+ IMarker marker = it.next();
+
+ try
+ {
+ // try to get the attribute for the marker
+ Integer attributeId = (Integer) marker.getAttribute(IBugzillaConstants.HIT_MARKER_ATTR_ID);
+ // add the selected item to the list of items that are selected
+ String description = (String) marker.getAttribute(IBugzillaConstants.HIT_MARKER_ATTR_DESC);
+ String query = (String) marker.getAttribute(IBugzillaConstants.HIT_MARKER_ATTR_QUERY);
+
+ // create a map to add attributes to so that the favorites file can sort
+ HashMap<String, Object> attributes = new HashMap<String, Object>();
+ attributes.put(IBugzillaConstants.HIT_MARKER_ATTR_ID, attributeId);
+ attributes.put(IBugzillaConstants.HIT_MARKER_ATTR_PRIORITY, marker.getAttribute(IBugzillaConstants.HIT_MARKER_ATTR_PRIORITY));
+ attributes.put(IBugzillaConstants.HIT_MARKER_ATTR_SEVERITY, marker.getAttribute(IBugzillaConstants.HIT_MARKER_ATTR_SEVERITY));
+ attributes.put(IBugzillaConstants.HIT_MARKER_ATTR_STATE, marker.getAttribute(IBugzillaConstants.HIT_MARKER_ATTR_STATE));
+ attributes.put(IBugzillaConstants.HIT_MARKER_ATTR_RESULT, marker.getAttribute(IBugzillaConstants.HIT_MARKER_ATTR_RESULT));
+
+ Favorite favorite = new Favorite(BugzillaPlugin.getDefault().getServerName(), attributeId.intValue(), description, query, attributes);
+ selected.add(favorite);
+ }
+ catch (CoreException ignored)
+ {
+ // do nothing
+ }
+ }
+ }
+ }
+
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/favorites/actions/AddToFavoritesAction.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/favorites/actions/AddToFavoritesAction.java
new file mode 100644
index 000000000..524999d11
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/favorites/actions/AddToFavoritesAction.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2003 - 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.bugzilla.ui.favorites.actions;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.mylar.bugzilla.core.BugzillaImages;
+import org.eclipse.mylar.bugzilla.core.BugzillaPlugin;
+import org.eclipse.mylar.bugzilla.core.favorites.Favorite;
+import org.eclipse.mylar.bugzilla.ui.FavoritesView;
+import org.eclipse.mylar.bugzilla.ui.editor.ExistingBugEditorInput;
+import org.eclipse.ui.part.EditorPart;
+
+
+/**
+ * Action used to add the supplied editor's bug to the favorites list.
+ */
+public class AddToFavoritesAction extends Action {
+ private EditorPart editorPart;
+
+ /**
+ * Creates a new <code>AddToFavoritesAction</code>.
+ *
+ * @param editor
+ * The editor for the bug that is being added to the favorites
+ * list.
+ */
+ public AddToFavoritesAction(EditorPart editor) {
+ editorPart = editor;
+ setText("&Add to favorites");
+ setImageDescriptor(BugzillaImages.IMG_TOOL_ADD_TO_FAVORITES);
+ }
+
+ @Override
+ public void run() {
+ ExistingBugEditorInput input = (ExistingBugEditorInput) editorPart.getEditorInput();
+ Favorite entry = new Favorite(input.getBug());
+ FavoritesView.checkWindow();
+ BugzillaPlugin.getDefault().getFavorites().add(entry);
+ FavoritesView.add();
+ }
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/favorites/actions/DeleteFavoriteAction.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/favorites/actions/DeleteFavoriteAction.java
new file mode 100644
index 000000000..49829d002
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/favorites/actions/DeleteFavoriteAction.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 2003 - 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.bugzilla.ui.favorites.actions;
+
+import org.eclipse.mylar.bugzilla.ui.FavoritesView;
+
+/**
+ * Action of removing a bookmark
+ */
+public class DeleteFavoriteAction extends AbstractFavoritesAction
+{
+ /** The instance of the favorites view */
+ private FavoritesView view;
+
+ /** True if all of the bookmarks are to be deleted */
+ private boolean deleteAll;
+
+ /**
+ * Constructor
+ * @param favoritesView The favorites view being used
+ * @param deleteAllFavorites <code>true</code> if all of the favorites should be deleted, else <code>false</code>
+ */
+ public DeleteFavoriteAction(FavoritesView favoritesView, boolean deleteAllFavorites)
+ {
+ deleteAll = deleteAllFavorites;
+
+ // set the appropriate icons and tool tips for the action depending
+ // on whether it will delete all items or not
+ if (deleteAll)
+ {
+ setToolTipText("Remove all favorites");
+ setText("Remove all");
+ setIcon("icons/remove-all.gif");
+ }
+ else
+ {
+ setToolTipText( "Remove selected favorites" );
+ setText( "Remove" );
+ setIcon( "icons/remove.gif" );
+ }
+
+ view = favoritesView;
+ }
+
+ /**
+ * Delete the appropriate favorites
+ * @see org.eclipse.jface.action.IAction#run()
+ */
+ @Override
+ public void run()
+ {
+ FavoritesView.checkWindow();
+
+ // call the appropriate delete function
+ if (deleteAll)
+ view.deleteAllFavorites();
+ else
+ view.deleteSelectedFavorites();
+ FavoritesView.updateActionEnablement();
+ }
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/favorites/actions/ViewFavoriteAction.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/favorites/actions/ViewFavoriteAction.java
new file mode 100644
index 000000000..f83e5bb4b
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/favorites/actions/ViewFavoriteAction.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2003 - 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.bugzilla.ui.favorites.actions;
+
+import java.util.List;
+
+import org.eclipse.mylar.bugzilla.ui.BugzillaOpenStructure;
+import org.eclipse.mylar.bugzilla.ui.FavoritesView;
+import org.eclipse.mylar.bugzilla.ui.ViewBugzillaAction;
+
+
+/**
+ * View a bug from the favorites menu
+ */
+public class ViewFavoriteAction extends AbstractFavoritesAction
+{
+
+ /** The view to get the result to launch a viewer on */
+ private FavoritesView view;
+
+ /**
+ * Constructor
+ * @param resultsView The view to launch a viewer on
+ */
+ public ViewFavoriteAction( FavoritesView resultsView )
+ {
+ setToolTipText( "View selected favorites" );
+ setText( "View selected" );
+ setIcon( "icons/openresult.gif" );
+ view = resultsView;
+ }
+
+ /**
+ * View the selected bugs in the editor window
+ * @see org.eclipse.jface.action.IAction#run()
+ */
+ @Override
+ public void run()
+ {
+ FavoritesView.checkWindow();
+ List<BugzillaOpenStructure> selectedBugs = view.getBugIdsOfSelected();
+
+ // if there are some selected bugs view the bugs in the editor window
+ if (!selectedBugs.isEmpty())
+ {
+ ViewBugzillaAction viewBugs = new ViewBugzillaAction("Display bugs in editor", selectedBugs);
+ viewBugs.schedule();
+ }
+ }
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/outline/BugzillaOutlineComparer.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/outline/BugzillaOutlineComparer.java
new file mode 100644
index 000000000..80884eb7b
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/outline/BugzillaOutlineComparer.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2003 - 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.bugzilla.ui.outline;
+
+import org.eclipse.jface.viewers.IElementComparer;
+import org.eclipse.mylar.bugzilla.core.BugzillaTools;
+import org.eclipse.mylar.bugzilla.core.IBugzillaReportSelection;
+
+/**
+ * This class is used to compare two <code>IBugzillaReportSelection</code>
+ * objects.
+ *
+ * @see IElementComparer
+ * @see IBugzillaReportSelection
+ */
+public class BugzillaOutlineComparer implements IElementComparer {
+
+ public boolean equals(Object a, Object b) {
+ if ((a instanceof IBugzillaReportSelection) && (b instanceof IBugzillaReportSelection)) {
+ IBugzillaReportSelection s1 = (IBugzillaReportSelection)a;
+ IBugzillaReportSelection s2 = (IBugzillaReportSelection)b;
+
+ // An IBugzillaReportSelection is uniquely defined by its handle and its contents
+ return (
+ (BugzillaTools.getHandle(s1).equals(BugzillaTools.getHandle(s2)))
+ && ((s1.getContents() == null) ? (s2.getContents() == null)
+ : s1.getContents().equals(s2.getContents()))
+ );
+ }
+ return a.equals(b);
+ }
+
+ public int hashCode(Object element) {
+ if (element instanceof IBugzillaReportSelection) {
+ IBugzillaReportSelection sel = (IBugzillaReportSelection) element;
+
+ // An IBugzillaReportSelection is uniquely defined by its handle and its contents
+ return (BugzillaTools.getHandle(sel) + sel.getContents()).hashCode();
+ }
+ return element.hashCode();
+ }
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/outline/BugzillaOutlineNode.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/outline/BugzillaOutlineNode.java
new file mode 100644
index 000000000..4336e4e82
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/outline/BugzillaOutlineNode.java
@@ -0,0 +1,341 @@
+/*******************************************************************************
+ * Copyright (c) 2003 - 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.bugzilla.ui.outline;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.eclipse.mylar.bugzilla.core.BugReport;
+import org.eclipse.mylar.bugzilla.core.BugzillaImages;
+import org.eclipse.mylar.bugzilla.core.Comment;
+import org.eclipse.mylar.bugzilla.core.IBugzillaBug;
+import org.eclipse.mylar.bugzilla.core.IBugzillaReportSelection;
+import org.eclipse.mylar.bugzilla.core.NewBugModel;
+import org.eclipse.swt.graphics.Image;
+
+
+/**
+ * A node for the tree in the <code>BugzillaOutlinePage</code>.
+ */
+public class BugzillaOutlineNode implements IBugzillaReportSelection {
+
+ /** The id of the Bugzilla object that the selection was on. */
+ protected int id;
+
+ /** The server of the Bugzilla object that the selection was on. */
+ protected String server;
+
+ /** The label for this piece of data. */
+ private String key;
+
+ /** The children of this node. */
+ private ArrayList<BugzillaOutlineNode> nodeChildren;
+
+ /** The parent of this node or null if it is the bug report */
+ private BugzillaOutlineNode parent;
+
+ /** This node's image. */
+ private Image image;
+
+ private Object data = null;
+
+ private String bugSummary;
+
+ private boolean fromEditor = false;
+
+ private boolean isCommentHeader = false;
+ private boolean isDescription = false;
+
+ /**
+ * Creates a new <code>BugzillaOutlineNode</code>.
+ *
+ * @param id
+ * The id of the bug this outline is for.
+ * @param server
+ * The server of the bug this outline is for.
+ * @param key
+ * The label for this node.
+ * @param image
+ * The image that will be displayed by this node in the tree.
+ * @param data
+ * The data, if necessary, this node represents.
+ * @param parent
+ * The parent of this node
+ */
+ public BugzillaOutlineNode(int id, String server, String key, Image image, Object data, String summary) {
+ this.id = id;
+ this.server = server;
+ this.key = key;
+ this.nodeChildren = null;
+ this.image = image;
+ this.data = data;
+ this.parent = null;
+ this.bugSummary = summary;
+ }
+
+ public boolean isFromEditor(){
+ return fromEditor;
+ }
+
+ /**
+ * @return The children of this node, represented as an <code>Object</code> array.
+ */
+ public BugzillaOutlineNode[] getChildren() {
+ return (nodeChildren == null) ? new BugzillaOutlineNode[0] : nodeChildren.toArray(new BugzillaOutlineNode[nodeChildren.size()]);
+ }
+
+ /**
+ * Adds a node to this node's list of children.
+ * @param bugNode The new child.
+ */
+ public void addChild(BugzillaOutlineNode bugNode) {
+ if (nodeChildren == null) {
+ nodeChildren = new ArrayList<BugzillaOutlineNode>();
+ }
+ bugNode.setParent(this);
+ nodeChildren.add(bugNode);
+ }
+
+ /**
+ * @return The label of this node.
+ */
+ public String getKey() {
+ return key;
+ }
+
+ /**
+ * Set the label of this node.
+ * @param key The new label.
+ */
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ /**
+ * @return The decorator image for this node.
+ */
+ public Image getImage() {
+ return image;
+ }
+
+ /**
+ * Sets the decorator image for this node.
+ * @param newImage The new image.
+ */
+ public void setImage(Image newImage) {
+ this.image = newImage;
+ }
+
+ /**
+ * @return <code>true</code> if the given object is another node
+ * representing the same piece of data in the editor.
+ */
+ @Override
+ public boolean equals(Object arg0) {
+ if (arg0 instanceof BugzillaOutlineNode) {
+ BugzillaOutlineNode bugNode = (BugzillaOutlineNode) arg0;
+ return getKey().equals(bugNode.getKey());
+ }
+ return super.equals(arg0);
+ }
+
+ @Override
+ public int hashCode() {
+ return getKey().hashCode();
+ }
+
+
+ /**
+ * @return The name of this node.
+ */
+ public String getName() {
+ return getKey();
+ }
+
+ /**
+ * @return The data (where applicable) this node represents.
+ */
+ public Object getData() {
+ return data;
+ }
+
+
+ /**
+ * Sets the data that this node represents.
+ * @param data The new piece of data.
+ */
+ public void setData(Object data) {
+ this.data = data;
+ }
+
+ /**
+ * Parses the given <code>IBugzillaBug</code> into a tree of
+ * <code>BugzillaOutlineNode</code>'s suitable for use in the
+ * <code>BugzillaOutlinePage</code> view.
+ *
+ * @param bug
+ * The bug that needs parsing.
+ * @return The tree of <code>BugzillaOutlineNode</code>'s.
+ */
+ public static BugzillaOutlineNode parseBugReport(IBugzillaBug bug) {
+ // Choose the appropriate parsing function based on
+ // the type of IBugzillaBug.
+ if (bug instanceof NewBugModel) {
+ return parseBugReport((NewBugModel)bug);
+ }
+ else if (bug instanceof BugReport) {
+ return parseBugReport((BugReport)bug);
+ }
+ else {
+ return null;
+ }
+ }
+
+ /**
+ * Parses the given <code>NewBugModel</code> into a tree of
+ * <code>BugzillaOutlineNode</code>'s suitable for use in the
+ * <code>BugzillaOutlinePage</code> view.
+ *
+ * @param bug
+ * The <code>NewBugModel</code> that needs parsing.
+ * @return The tree of <code>BugzillaOutlineNode</code>'s.
+ */
+ protected static BugzillaOutlineNode parseBugReport(NewBugModel bug){
+ int bugId = bug.getId();
+ String bugServer = bug.getServer();
+ Image bugImage = BugzillaImages.getImage(BugzillaImages.BUG);
+ Image defaultImage = BugzillaImages.getImage(BugzillaImages.IMG_COMMENT);
+ BugzillaOutlineNode topNode = new BugzillaOutlineNode(bugId, bugServer, bug.getLabel(), bugImage, bug, bug.getSummary());
+
+ topNode.addChild(new BugzillaOutlineNode(bugId, bugServer, "New Description", defaultImage, null, bug.getSummary()));
+
+ BugzillaOutlineNode titleNode = new BugzillaOutlineNode(bugId, bugServer, "NewBugModel Object", defaultImage, null, bug.getSummary());
+ titleNode.addChild(topNode);
+
+ return titleNode;
+ }
+
+ /**
+ * Parses the given <code>BugReport</code> into a tree of
+ * <code>BugzillaOutlineNode</code>'s suitable for use in the
+ * <code>BugzillaOutlinePage</code> view.
+ *
+ * @param bug
+ * The <code>BugReport</code> that needs parsing.
+ * @return The tree of <code>BugzillaOutlineNode</code>'s.
+ */
+ protected static BugzillaOutlineNode parseBugReport(BugReport bug) {
+
+ int bugId = bug.getId();
+ String bugServer = bug.getServer();
+ Image bugImage = BugzillaImages.getImage(BugzillaImages.BUG);
+ Image defaultImage = BugzillaImages.getImage(BugzillaImages.IMG_COMMENT);
+ BugzillaOutlineNode topNode = new BugzillaOutlineNode(bugId, bugServer, bug.getLabel(), bugImage, bug, bug.getSummary());
+
+ BugzillaOutlineNode desc = new BugzillaOutlineNode(bugId, bugServer, "Description", defaultImage, bug.getDescription(), bug.getSummary());
+ desc.setIsDescription(true);
+
+ topNode.addChild(desc);
+
+
+ BugzillaOutlineNode comments = null;
+ for (Iterator<Comment> iter = bug.getComments().iterator(); iter.hasNext();) {
+ Comment comment = iter.next();
+
+ if(comments == null){
+ comments = new BugzillaOutlineNode(bugId, bugServer, "Comments", defaultImage, comment, bug.getSummary());
+ comments.setIsCommentHeader(true);
+ }
+ comments.addChild(new BugzillaOutlineNode(bugId, bugServer, comment.getCreated().toString(), defaultImage, comment, bug.getSummary()));
+ }
+ if(comments != null){
+ topNode.addChild(comments);
+ }
+
+ topNode.addChild(new BugzillaOutlineNode(bugId, bugServer, "New Comment", defaultImage, null, bug.getSummary()));
+
+ BugzillaOutlineNode titleNode = new BugzillaOutlineNode(bugId, bugServer, "BugReport Object", defaultImage, null, bug.getSummary());
+ titleNode.addChild(topNode);
+
+ return titleNode;
+ }
+
+ public boolean hasComment() {
+ // If the comment category was selected, then the comment object is
+ // not the intended selection (it is just used to help find the correct
+ // location in the editor).
+ return (data instanceof Comment) && !(key.toLowerCase().equals("comments"));
+ }
+
+ public Comment getComment() {
+ return (hasComment()) ? (Comment)data : null;
+ }
+
+ public void setComment(Comment comment) {
+ data = comment;
+ }
+
+ public String getContents() {
+ return key;
+ }
+
+ public void setContents(String contents) {
+ key = contents;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getServer() {
+ return server;
+ }
+
+ public void setServer(String server) {
+ this.server = server;
+ }
+
+ public boolean isEmpty() {
+ return (server == null) || ((getContents() == null) && (getComment() == null));
+ }
+
+ public BugzillaOutlineNode getParent() {
+ return parent;
+ }
+
+ public void setParent(BugzillaOutlineNode parent) {
+ this.parent = parent;
+ }
+
+ public boolean isCommentHeader() {
+ return isCommentHeader;
+ }
+
+ public boolean isDescription() {
+ return isDescription;
+ }
+
+ public void setIsCommentHeader(boolean isCommentHeader) {
+ this.isCommentHeader = isCommentHeader;
+ }
+
+ public void setIsDescription(boolean isDescription) {
+ this.isDescription = isDescription;
+ }
+
+ public String getBugSummary() {
+ return bugSummary;
+ }
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/outline/BugzillaOutlinePage.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/outline/BugzillaOutlinePage.java
new file mode 100644
index 000000000..4a8048eef
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/outline/BugzillaOutlinePage.java
@@ -0,0 +1,157 @@
+/*******************************************************************************
+ * Copyright (c) 2003 - 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.bugzilla.ui.outline;
+
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.mylar.bugzilla.core.BugzillaTools;
+import org.eclipse.mylar.bugzilla.core.IBugzillaReportSelection;
+import org.eclipse.mylar.bugzilla.ui.editor.AbstractBugEditor;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.ISelectionListener;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.views.contentoutline.ContentOutlinePage;
+
+
+/**
+ * An outline page for a <code>BugEditor</code>.
+ */
+public class BugzillaOutlinePage extends ContentOutlinePage{
+
+ private BugzillaOutlineNode topTreeNode;
+
+ protected final ISelectionListener selectionListener = new ISelectionListener() {
+ public void selectionChanged(IWorkbenchPart part, ISelection selection) {
+ if ((part instanceof AbstractBugEditor) && (selection instanceof IStructuredSelection)) {
+ if (((IStructuredSelection)selection).getFirstElement() instanceof IBugzillaReportSelection) {
+ if(((IStructuredSelection)getSelection()).getFirstElement() instanceof IBugzillaReportSelection){
+ IBugzillaReportSelection brs1 = (IBugzillaReportSelection)((IStructuredSelection)getSelection()).getFirstElement();
+ IBugzillaReportSelection brs2 = ((IBugzillaReportSelection)((IStructuredSelection)selection).getFirstElement());
+ if(BugzillaTools.getHandle(brs1).compareTo(BugzillaTools.getHandle(brs2)) == 0){
+ // don't need to make a selection for the same element
+ return;
+ }
+ }
+ getTreeViewer().setSelection(selection, true);
+ }
+ }
+ }
+ };
+
+ private TreeViewer viewer;
+
+ /**
+ * Creates a new <code>BugzillaOutlinePage</code>.
+ *
+ * @param topTreeNode
+ * The top data node of the tree for this view.
+ * @param editor
+ * The editor this outline page is for.
+ */
+ public BugzillaOutlinePage(BugzillaOutlineNode topTreeNode) {
+ super();
+ this.topTreeNode = topTreeNode;
+ }
+
+ @Override
+ public void createControl(Composite parent) {
+ super.createControl(parent);
+ viewer = getTreeViewer();
+ viewer.setContentProvider(new BugTaskOutlineContentProvider());
+ viewer.setLabelProvider(new LabelProvider() {
+ @Override
+ public Image getImage(Object element) {
+ if (element instanceof BugzillaOutlineNode) {
+ return ((BugzillaOutlineNode)element).getImage();
+ }
+ else {
+ return super.getImage(element);
+ }
+ }
+
+ @Override
+ public String getText(Object element) {
+ if (element instanceof BugzillaOutlineNode) {
+ return ((BugzillaOutlineNode)element).getName();
+ }
+ else {
+ return super.getText(element);
+ }
+ }
+ });
+ viewer.setInput(topTreeNode);
+ viewer.setComparer(new BugzillaOutlineComparer());
+ viewer.expandAll();
+ getSite().getPage().addSelectionListener(selectionListener);
+ }
+
+ @Override
+ public void dispose() {
+ super.dispose();
+ getSite().getPage().removeSelectionListener(selectionListener);
+ }
+
+ public TreeViewer getOutlineTreeViewer(){
+ return viewer;
+ }
+
+ /**
+ * A content provider for the tree for this view.
+ * @see ITreeContentProvider
+ */
+ protected class BugTaskOutlineContentProvider implements ITreeContentProvider {
+
+ public Object[] getChildren(Object parentElement) {
+ if (parentElement instanceof BugzillaOutlineNode) {
+ Object[] children = ((BugzillaOutlineNode)parentElement).getChildren();
+ if (children.length > 0) {
+ return children;
+ }
+ }
+ return new Object[0];
+ }
+
+ public Object getParent(Object element) {
+ return null;
+ }
+
+ public boolean hasChildren(Object element) {
+ if (element instanceof BugzillaOutlineNode) {
+ return ((BugzillaOutlineNode)element).getChildren().length > 0;
+ }
+ return false;
+ }
+
+ public Object[] getElements(Object inputElement) {
+ if (inputElement instanceof BugzillaOutlineNode) {
+ Object[] children = ((BugzillaOutlineNode)inputElement).getChildren();
+ if (children.length > 0) {
+ return children;
+ }
+ }
+ return new Object[0];
+ }
+
+ public void dispose() {
+ // don't care when we are disposed
+ }
+
+ public void inputChanged(Viewer viewerChanged, Object oldInput, Object newInput) {
+ // don't care when the input changes
+ }
+ }
+
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/outline/BugzillaReportSelection.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/outline/BugzillaReportSelection.java
new file mode 100644
index 000000000..fe18a93f4
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/outline/BugzillaReportSelection.java
@@ -0,0 +1,170 @@
+/*******************************************************************************
+ * Copyright (c) 2003 - 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.bugzilla.ui.outline;
+
+import org.eclipse.mylar.bugzilla.core.Comment;
+import org.eclipse.mylar.bugzilla.core.IBugzillaReportSelection;
+
+/**
+ * A selection of a Bugzilla element in a view.
+ */
+public class BugzillaReportSelection implements IBugzillaReportSelection {
+
+ /**
+ * The id of the Bugzilla object that the selection was on.
+ */
+ protected int id;
+
+ /** The server of the Bugzilla object that the selection was on. */
+ protected String server;
+
+ /** The contents of the selection. */
+ protected String contents;
+
+ protected String bugSummary;
+
+ /**
+ * The comment, if a comment was selected. If the selection was not a
+ * comment, then this is <code>null</code>.
+ */
+ protected Comment comment;
+
+ /**
+ * Creates a new <code>BugzillaReportSelection</code> with no supplied
+ * contents or comment.
+ *
+ * @param id
+ * The id of the Bugzilla object that the selection was on.
+ * @param server
+ * The server of the Bugzilla object that the selection was on.
+ */
+ public BugzillaReportSelection(int id, String server, String summary) {
+ this(id, server, null, null, summary);
+ }
+
+ /**
+ * Creates a new <code>BugzillaReportSelection</code> with no supplied
+ * comment.
+ *
+ * @param id
+ * The id of the Bugzilla object that the selection was on.
+ * @param server
+ * The server of the Bugzilla object that the selection was on.
+ * @param contents
+ * The contents of the selection.
+ */
+ public BugzillaReportSelection(int id, String server, String contents, boolean isDescription, String summary) {
+ this(id, server, contents, null, summary);
+ this.isDescription = isDescription;
+ }
+
+ /**
+ * Creates a new <code>BugzillaReportSelection</code> with no supplied
+ * contents.
+ *
+ * @param id
+ * The id of the Bugzilla object that the selection was on.
+ * @param server
+ * The server of the Bugzilla object that the selection was on.
+ * @param comment
+ * The <code>Comment</code> object for this selection. If a
+ * comment was not selected, then this should be
+ * <code>null</code>.
+ */
+ public BugzillaReportSelection(int id, String server, Comment comment, String summary) {
+ this(id, server, null, comment, summary);
+ }
+
+ /**
+ * Creates a new <code>BugzillaReportSelection</code>.
+ *
+ * @param id
+ * The id of the Bugzilla object that the selection was on.
+ * @param server
+ * The server of the Bugzilla object that the selection was on.
+ * @param contents
+ * The contents of the selection.
+ * @param comment
+ * The <code>Comment</code> object for this selection. If a
+ * comment was not selected, then this should be
+ * <code>null</code>.
+ */
+ public BugzillaReportSelection(int id, String server, String contents, Comment comment, String summary) {
+ this.id = id;
+ this.server = server;
+ this.contents = contents;
+ this.comment = comment;
+ this.bugSummary = summary;
+ }
+
+ public boolean hasComment() {
+ return comment != null;
+ }
+
+ public Comment getComment() {
+ return comment;
+ }
+
+ public void setComment(Comment comment) {
+ this.comment = comment;
+ }
+
+ public String getContents() {
+ return contents;
+ }
+
+ public void setContents(String contents) {
+ this.contents = contents;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getServer() {
+ return server;
+ }
+
+ public void setServer(String server) {
+ this.server = server;
+ }
+
+ public boolean isEmpty() {
+ return (server == null) || ((contents == null) && (comment == null));
+ }
+
+ private boolean isCommentHeader = false;
+ private boolean isDescription = false;
+
+ public boolean isCommentHeader() {
+ return isCommentHeader;
+ }
+
+ public boolean isDescription() {
+ return isDescription;
+ }
+
+ public void setIsCommentHeader(boolean isCommentHeader) {
+ this.isCommentHeader = isCommentHeader;
+ }
+
+ public void setIsDescription(boolean isDescription) {
+ this.isDescription = isDescription;
+ }
+
+ public String getBugSummary() {
+ return bugSummary;
+ }
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/query/GetQueryDialog.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/query/GetQueryDialog.java
new file mode 100644
index 000000000..70a2feac4
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/query/GetQueryDialog.java
@@ -0,0 +1,203 @@
+/*******************************************************************************
+ * Copyright (c) 2003 - 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.bugzilla.ui.query;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.mylar.bugzilla.ui.search.BugzillaSearchPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.Shell;
+
+
+/**
+ * Dialog to display, manage and run stored queries.
+ */
+public class GetQueryDialog extends Dialog
+{
+
+ /** The Ok button. */
+ private Button okButton;
+
+ /** The title of the dialog. */
+ private String title;
+
+ private SavedQueryFile input;
+
+ public GetQueryDialog(Shell parentShell, String dialogTitle, SavedQueryFile in) {
+ super(parentShell);
+ this.title = dialogTitle;
+ input = in;
+ setShellStyle(SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL);
+ }
+
+ @Override
+ protected void configureShell(Shell shell) {
+ super.configureShell(shell);
+ shell.setText(title);
+ }
+
+ @Override
+ protected void createButtonsForButtonBar(Composite parent) {
+ // create OK and Details buttons
+ okButton = createButton(parent, IDialogConstants.OK_ID, "Run", true);
+ okButton.setEnabled(false);
+ createButton(parent, IDialogConstants.CANCEL_ID, "Close", false);
+ }
+
+ /**
+ * Creates the list widget to display stored queries.
+ */
+ @Override
+ final protected Control createDialogArea(Composite parent) {
+ Composite composite = (Composite)super.createDialogArea(parent);
+
+ createMainDialogArea(composite);
+
+ return composite;
+ }
+
+ protected void createMainDialogArea(Composite parent) {
+ Label label = new Label(parent, SWT.LEFT);
+ label.setText("Select a saved query:");
+ rememberPattern = new List(parent, SWT.SINGLE | SWT.V_SCROLL | SWT.BORDER);
+ ArrayList<String> names = input.getNames();
+ int pos = 0;
+
+ for (Iterator<String> it = names.iterator(); it.hasNext(); ) {
+ rememberPattern.add(it.next(), pos);
+ pos++;
+ }
+
+ GridData gd = new GridData(GridData.FILL_HORIZONTAL);
+ gd.horizontalSpan = 5;
+ gd.heightHint = 60;
+
+ rememberPattern.setLayoutData(gd);
+ rememberPattern.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent event) {
+ selIndex = rememberPattern.getSelectionIndex();
+ okButton.setEnabled(selIndex >= 0);
+ }
+ });
+ rememberPattern.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseDoubleClick(MouseEvent e) {
+ okPressed();
+ }
+ });
+
+ // Configure the context menu
+ MenuManager menuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$
+ menuMgr.add(new RunQueryAction("&Run query"));
+ menuMgr.add(new Separator());
+ menuMgr.add(new RemoveAction("Remo&ve"));
+ menuMgr.add(new RemoveAllAction("Remove &all"));
+ Menu menu = menuMgr.createContextMenu(rememberPattern);
+ rememberPattern.setMenu(menu);
+
+ }
+
+ final protected void setPageComplete(boolean complete) {
+ if (okButton != null) {
+ okButton.setEnabled(complete);
+ }
+ }
+
+ private String queryNameText;
+
+ private List rememberPattern;
+
+ public String getText() {
+ return queryNameText;
+ }
+
+ /**
+ * Deletes a selected named query.
+ */
+ private void remove() {
+ int index = rememberPattern.getSelectionIndex();
+ if(index != -1)
+ BugzillaSearchPage.getInput().remove(new int []{index});
+ rememberPattern.remove(index);
+ rememberPattern.setSelection(-1);
+ selIndex = -1;
+ okButton.setEnabled(false);
+ }
+
+ private void removeAll() {
+ BugzillaSearchPage.getInput().removeAll();
+ rememberPattern.removeAll();
+ rememberPattern.setSelection(-1);
+ selIndex = -1;
+ okButton.setEnabled(false);
+ }
+
+ /** Index of the selected query, or -1 if none. */
+ int selIndex = -1;
+
+ /**
+ * Returns index of the selected query or -1 if none are selected.
+ */
+ public int getSelected() {
+ return selIndex;
+ }
+
+ private class RunQueryAction extends Action {
+ RunQueryAction(String text) {
+ super(text);
+ }
+
+ @Override
+ public void run() {
+ GetQueryDialog.this.okPressed();
+ }
+ }
+
+ private class RemoveAction extends Action {
+ RemoveAction(String text) {
+ super(text);
+ }
+
+ @Override
+ public void run() {
+ GetQueryDialog.this.remove();
+ }
+ }
+
+ private class RemoveAllAction extends Action {
+ RemoveAllAction(String text) {
+ super(text);
+ }
+
+ @Override
+ public void run() {
+ GetQueryDialog.this.removeAll();
+ }
+ }
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/query/SaveQueryDialog.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/query/SaveQueryDialog.java
new file mode 100644
index 000000000..e8788c59c
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/query/SaveQueryDialog.java
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * Copyright (c) 2003 - 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.bugzilla.ui.query;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * Dialog for naming a saved query.
+ */
+public class SaveQueryDialog extends Dialog
+{
+ private Text queryName;
+
+ /**
+ * The Ok button.
+ */
+ private Button okButton;
+
+ /**
+ * The title of the dialog.
+ */
+ private String title;
+
+ public SaveQueryDialog(Shell parentShell, String dialogTitle) {
+ super(parentShell);
+ this.title = dialogTitle;
+ setShellStyle(SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL);
+ }
+
+ @Override
+ protected void configureShell(Shell shell) {
+ super.configureShell(shell);
+ shell.setText(title);
+ }
+
+ @Override
+ protected void createButtonsForButtonBar(Composite parent) {
+ // create OK and cancel buttons
+ okButton = createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true);
+ createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false);
+ okButton.setEnabled(false);
+ }
+
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ // create composite
+ Composite composite = (Composite)super.createDialogArea(parent);
+
+ createMainDialogArea(composite);
+ return composite;
+ }
+
+ protected void createMainDialogArea(Composite parent) {
+ queryName = new Text(parent, SWT.SINGLE | SWT.BORDER);
+ queryName.setLayoutData(new GridData(GridData.BEGINNING | GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL));
+ queryName.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ if (queryName.getText().compareTo("") != 0 && queryName.getText() != null)
+ okButton.setEnabled(true);
+ else
+ okButton.setEnabled(false);
+ queryNameText = queryName.getText();
+ }
+ });
+ }
+
+ String queryNameText;
+
+ public String getText() {
+ return queryNameText;
+ }
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/query/SavedQueryFile.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/query/SavedQueryFile.java
new file mode 100644
index 000000000..11da9d2df
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/query/SavedQueryFile.java
@@ -0,0 +1,249 @@
+/*******************************************************************************
+ * Copyright (c) 2003 - 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.bugzilla.ui.query;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.mylar.bugzilla.core.BugzillaPlugin;
+
+
+/**
+ * This class manages accessing and persisting named Bugzilla queries.
+ */
+public class SavedQueryFile
+{
+ /** The file that the queries are written to */
+ private File file;
+
+ /** The directory to where the file is located */
+ private File directory;
+
+ /** A list of remembered queries */
+ private ArrayList<String> list = new ArrayList<String>();
+ private ArrayList<String> nameList = new ArrayList<String>();
+ private ArrayList<String> sumList = new ArrayList<String>();
+
+ /**
+ * Constructor
+ * @param dirPath The path to the directory where the favorites file should be written to
+ * @param fileName The file that the favorites should be written to
+ */
+ public SavedQueryFile(String dirPath, String fileName)
+ {
+ // create a new file and if it exists, read the data from the file
+ // else create the file and directories if they dont exist
+ file = new File(dirPath + fileName);
+ if (file.exists()) {
+ readFile();
+ }
+ else {
+ directory = new File(dirPath);
+ if (!directory.exists())
+ directory.mkdirs();
+ writeFile();
+ }
+ }
+
+ /**
+ * Add a query to the list
+ * @param entry The query to add
+ */
+ public int add(String entry, String name, String sum)
+ {
+ // add the entry to the list and write the file to disk
+ int index = find(name);
+ if (index == -1) {
+ list.add(entry);
+ nameList.add(name);
+ sumList.add(sum);
+ writeFile();
+ }
+ else {
+ boolean isDuplicate = MessageDialog.openConfirm(BugzillaPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getShell(),
+ "Save Bugzilla Query",
+ name + " already exists. Do you want to replace it?");
+ if (isDuplicate) {
+ list.add(index, entry);
+ nameList.add(index,name);
+ sumList.add(index,sum);
+ list.remove(index+1);
+ nameList.remove(index+1);
+ sumList.remove(index+1);
+ writeFile();
+ }
+ }
+
+ index = find(entry);
+ return index;
+ }
+
+
+ /**
+ * Find a bug in the query list
+ * @param name The name of the query that we are looking for
+ * @return The index of the query in the array if it exists, else -1
+ */
+ public int find(String name)
+ {
+ for (int i = 0; i < list.size(); i++) {
+ String str = nameList.get(i);
+ if (name.compareTo(str) == 0)
+ return i;
+ }
+ return -1;
+ }
+
+ /**
+ * Get the list of queries
+ * @return The list of queries
+ */
+ public ArrayList<String> elements()
+ {
+ return list;
+ }
+
+ /**
+ * Write the queries to disk
+ */
+ private void writeFile()
+ {
+ try {
+ OutputStream os = new FileOutputStream(file);
+ DataOutputStream data = new DataOutputStream(os);
+
+ // Write the size of the list so that we can read it back in easier
+ data.writeInt(list.size());
+
+ // write each element in the array list
+ for (int i = 0; i < list.size(); i++) {
+ String item = list.get(i);
+ String itemName = nameList.get(i);
+ String summary = sumList.get(i);
+
+ // write the string in a machine independant manner
+ data.writeUTF(item);
+ data.writeUTF(itemName);
+ data.writeUTF(summary);
+ }
+
+ // close the file
+ data.close();
+ }
+ catch (IOException e) {
+ // put up a message and log the error if there is a problem writing to the file
+ BugzillaPlugin.getDefault().logAndShowExceptionDetailsDialog(e, "occurred while saving your Bugzilla queries", "I/O Error");
+ }
+ }
+
+ /**
+ * Read the queries in from the file on disk
+ */
+ private void readFile()
+ {
+ try {
+ InputStream is = new FileInputStream(file);
+ DataInputStream data = new DataInputStream(is);
+
+ // get the number of favorites in the file
+ int size = data.readInt();
+
+ // read in each of the favorites in the file
+ for (int nX = 0; nX < size; nX++) {
+ String item, name, summary;
+
+ // get the data from disk in a machine independant way
+ item = data.readUTF();
+ name = data.readUTF();
+ summary = data.readUTF();
+
+ // add the favorite to the favorites list
+ list.add(item);
+ nameList.add(name);
+ sumList.add(summary);
+ }
+
+ // close the input stream
+ data.close();
+ }
+ catch (IOException e) {
+ // put up a message and log the error if there is a problem reading from the file
+ BugzillaPlugin.getDefault().logAndShowExceptionDetailsDialog(e, "occurred while restoring saved Bugzilla queries.", "I/O Error");
+ }
+ }
+
+ /**
+ * Remove some queries from the list
+ * @param indicesToRemove An array of the indicies of the queries to be removed
+ */
+ public void remove(int[] indicesToRemove)
+ {
+ int timesShifted = 0;
+
+ // remove each of the indicated items from the array
+ for (int i = 0; i < indicesToRemove.length; i++) {
+ list.remove(indicesToRemove[i] - timesShifted);
+ nameList.remove(indicesToRemove[i] - timesShifted);
+ sumList.remove(indicesToRemove[i] - timesShifted);
+ timesShifted++;
+ }
+
+ // rewrite the file so that the data is persistant
+ writeFile();
+
+ // remove the items from the combo box
+ timesShifted = 0;
+ }
+
+ /**
+ * Remove all of the items in the favortes menu
+ */
+ public void removeAll() {
+ // remove every element from the favorites list
+ while (list.size() > 0) {
+ list.remove(0);
+ nameList.remove(0);
+ sumList.remove(0);
+ }
+
+ // rewrite the file so that the data is persistant
+ writeFile();
+ }
+
+ /**
+ * Get the query parameters of the currently selected remembered query
+ * @return The query url
+ */
+ public String getQueryParameters(int index) {
+ return list.get(index);
+ }
+
+ /**
+ * Get the summary text of the currently selected remembered query
+ * @return The summary text
+ */
+ public String getSummaryText(int index) {
+ return sumList.get(index);
+
+ }
+
+ public ArrayList<String> getNames() {
+ return nameList;
+ }
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/search/BugzillaSearchPage.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/search/BugzillaSearchPage.java
new file mode 100644
index 000000000..a586a662f
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/search/BugzillaSearchPage.java
@@ -0,0 +1,984 @@
+/*******************************************************************************
+ * Copyright (c) 2003 - 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.bugzilla.ui.search;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+
+import javax.security.auth.login.LoginException;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.dialogs.DialogPage;
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.dialogs.InputDialog;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.mylar.bugzilla.core.BugzillaPlugin;
+import org.eclipse.mylar.bugzilla.core.BugzillaPreferences;
+import org.eclipse.mylar.bugzilla.core.IBugzillaConstants;
+import org.eclipse.mylar.bugzilla.core.search.BugzillaSearchOperation;
+import org.eclipse.mylar.bugzilla.core.search.BugzillaSearchQuery;
+import org.eclipse.mylar.bugzilla.core.search.BugzillaSearchResultCollector;
+import org.eclipse.mylar.bugzilla.core.search.IBugzillaSearchOperation;
+import org.eclipse.mylar.bugzilla.core.search.IBugzillaSearchResultCollector;
+import org.eclipse.mylar.bugzilla.ui.BugzillaUITools;
+import org.eclipse.mylar.bugzilla.ui.query.GetQueryDialog;
+import org.eclipse.mylar.bugzilla.ui.query.SaveQueryDialog;
+import org.eclipse.mylar.bugzilla.ui.query.SavedQueryFile;
+import org.eclipse.search.ui.ISearchPage;
+import org.eclipse.search.ui.ISearchPageContainer;
+import org.eclipse.search.ui.NewSearchUI;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.internal.help.WorkbenchHelpSystem;
+
+
+/**
+ * Bugzilla search page
+ */
+public class BugzillaSearchPage extends DialogPage implements ISearchPage {
+ protected Combo summaryPattern = null;
+ private static ArrayList<BugzillaSearchData> previousSummaryPatterns = new ArrayList<BugzillaSearchData>(20);
+ private static ArrayList<BugzillaSearchData> previousEmailPatterns = new ArrayList<BugzillaSearchData>(20);
+ private static ArrayList<BugzillaSearchData> previousCommentPatterns = new ArrayList<BugzillaSearchData>(20);
+ protected ISearchPageContainer scontainer = null;
+ private boolean firstTime = true;
+
+ private IDialogSettings fDialogSettings;
+
+ private static final String [] patternOperationText = {"all words", "any word", "regexp"};
+ private static final String [] patternOperationValues = {"allwordssubstr", "anywordssubstr", "regexp"};
+ private static final String [] emailOperationText = {"substring", "exact", "regexp"};
+ private static final String [] emailOperationValues = {"substring", "exact", "regexp"};
+ private static final String [] emailRoleValues = {"emailassigned_to1", "emailreporter1", "emailcc1", "emaillongdesc1"};
+
+ protected IPreferenceStore prefs = BugzillaPlugin.getDefault().getPreferenceStore();
+ private String [] statusValues = BugzillaPreferences.queryOptionsToArray(prefs.getString(IBugzillaConstants.STATUS_VALUES));
+ private String [] preselectedStatusValues = BugzillaPreferences.queryOptionsToArray(prefs.getString(IBugzillaConstants.PRESELECTED_STATUS_VALUES));
+ private String [] resolutionValues = BugzillaPreferences.queryOptionsToArray(prefs.getString(IBugzillaConstants.RESOLUTION_VALUES));
+ private String [] severityValues = BugzillaPreferences.queryOptionsToArray(prefs.getString(IBugzillaConstants.SEVERITY_VALUES));
+ private String [] priorityValues = BugzillaPreferences.queryOptionsToArray(prefs.getString(IBugzillaConstants.PRIORITY_VALUES));
+ private String [] hardwareValues = BugzillaPreferences.queryOptionsToArray(prefs.getString(IBugzillaConstants.HARDWARE_VALUES));
+ private String [] osValues = BugzillaPreferences.queryOptionsToArray(prefs.getString(IBugzillaConstants.OS_VALUES));
+
+ private String [] productValues = BugzillaPreferences.queryOptionsToArray(prefs.getString(IBugzillaConstants.PRODUCT_VALUES));
+ private String [] componentValues = BugzillaPreferences.queryOptionsToArray(prefs.getString(IBugzillaConstants.COMPONENT_VALUES));
+ private String [] versionValues = BugzillaPreferences.queryOptionsToArray(prefs.getString(IBugzillaConstants.VERSION_VALUES));
+ private String [] targetValues = BugzillaPreferences.queryOptionsToArray(prefs.getString(IBugzillaConstants.TARGET_VALUES));
+
+ private static class BugzillaSearchData {
+ /** Pattern to match on */
+ String pattern;
+ /** Pattern matching criterion */
+ int operation;
+
+ BugzillaSearchData(String pattern, int operation) {
+ this.pattern = pattern;
+ this.operation = operation;
+ }
+ }
+
+ /**
+ * The constructor.
+ */
+ public BugzillaSearchPage() {
+ super();
+ }
+
+ /**
+ * Insert the method's description here.
+ * @see DialogPage#createControl
+ */
+ public void createControl(Composite parent) {
+ readConfiguration();
+
+ Composite control = new Composite(parent, SWT.NONE);
+ GridLayout layout = new GridLayout(2, false);
+ control.setLayout(layout);
+ GridData gd = new GridData(GridData.FILL_BOTH);
+ control.setLayoutData(gd);
+
+ createTextSearchComposite(control);
+ createComment(control);
+ createProductAttributes(control);
+ createLists(control);
+ createLastDays(control);
+ createEmail(control);
+ createSaveQuery(control);
+ input = new SavedQueryFile(BugzillaPlugin.getDefault().getStateLocation().toString(), "/queries");
+ createUpdate(control);
+
+
+ setControl(control);
+ WorkbenchHelpSystem.getInstance().setHelp(control, IBugzillaConstants.SEARCH_PAGE_CONTEXT);
+ }
+
+ protected Control createTextSearchComposite(Composite control) {
+ GridData gd;
+ Label label;
+
+ Composite group = new Composite(control, SWT.NONE);
+ GridLayout layout = new GridLayout(2, false);
+ group.setLayout(layout);
+ group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+ gd = new GridData(GridData.BEGINNING | GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL);
+ gd.horizontalSpan = 2;
+ group.setLayoutData(gd);
+
+ // Info text
+ label = new Label(group, SWT.LEFT);
+ label.setText("Bug id or summary search terms");
+ gd = new GridData(GridData.BEGINNING);
+ gd.horizontalSpan = 2;
+ label.setLayoutData(gd);
+
+ // Pattern combo
+ summaryPattern = new Combo(group, SWT.SINGLE | SWT.BORDER);
+ summaryPattern.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ scontainer.setPerformActionEnabled(canQuery());
+ }
+ });
+ summaryPattern.addSelectionListener(new SelectionAdapter() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ handleWidgetSelected(summaryPattern, summaryOperation, previousSummaryPatterns);
+ }
+ });
+ gd = new GridData(GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL);
+ summaryPattern.setLayoutData(gd);
+
+ summaryOperation = new Combo(group, SWT.SINGLE | SWT.READ_ONLY | SWT.BORDER);
+ summaryOperation.setItems(patternOperationText);
+ summaryOperation.setText(patternOperationText[0]);
+ summaryOperation.select(0);
+
+ return group;
+ }
+
+
+ private Control createComment(Composite control) {
+ GridData gd;
+ Label label;
+
+ Composite group = new Composite(control, SWT.NONE);
+ GridLayout layout = new GridLayout(3, false);
+ group.setLayout(layout);
+ group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+ gd = new GridData(GridData.BEGINNING | GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL);
+ gd.horizontalSpan = 2;
+ group.setLayoutData(gd);
+
+ // Info text
+ label = new Label(group, SWT.LEFT);
+ label.setText("Comment contains: ");
+ gd = new GridData(GridData.BEGINNING);
+ label.setLayoutData(gd);
+
+ commentOperation = new Combo(group, SWT.SINGLE | SWT.READ_ONLY | SWT.BORDER);
+ commentOperation.setItems(patternOperationText);
+ commentOperation.setText(patternOperationText[0]);
+ commentOperation.select(0);
+
+ // Comment pattern combo
+ commentPattern = new Combo(group, SWT.SINGLE | SWT.BORDER);
+ commentPattern.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ scontainer.setPerformActionEnabled(canQuery());
+ }
+ });
+ commentPattern.addSelectionListener(new SelectionAdapter() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ handleWidgetSelected(commentPattern, commentOperation, previousCommentPatterns);
+ }
+ });
+ gd = new GridData(GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL);
+ commentPattern.setLayoutData(gd);
+
+ return group;
+ }
+
+ /**
+ * Creates the area for selection on product/component/version.
+ */
+ protected Control createProductAttributes(Composite control) {
+ GridData gd;
+ GridLayout layout;
+
+ // Search expression
+ Group group = new Group(control, SWT.NONE);
+ layout = new GridLayout();
+ layout.numColumns = 4;
+ group.setLayout(layout);
+ gd = new GridData(GridData.FILL_HORIZONTAL);
+ gd.horizontalSpan = 5;
+ group.setLayoutData(gd);
+
+ // Labels
+ Label label = new Label(group, SWT.LEFT);
+ label.setText("Product");
+
+ label = new Label(group, SWT.LEFT);
+ label.setText("Component");
+
+ label = new Label(group, SWT.LEFT);
+ label.setText("Version");
+
+ label = new Label(group, SWT.LEFT);
+ label.setText("Milestone");
+
+ // Lists
+ product = new List(group, SWT.MULTI | SWT.V_SCROLL | SWT.BORDER);
+ gd = new GridData(GridData.FILL_HORIZONTAL);
+ gd.heightHint = 40;
+ product.setLayoutData(gd);
+
+ component = new List(group, SWT.MULTI | SWT.V_SCROLL | SWT.BORDER);
+ gd = new GridData(GridData.FILL_HORIZONTAL);
+ gd.heightHint = 40;
+ component.setLayoutData(gd);
+
+ version = new List(group, SWT.MULTI | SWT.V_SCROLL | SWT.BORDER);
+ gd = new GridData(GridData.FILL_HORIZONTAL);
+ gd.heightHint = 40;
+ version.setLayoutData(gd);
+
+ target = new List(group, SWT.MULTI | SWT.V_SCROLL | SWT.BORDER);
+ gd = new GridData(GridData.FILL_HORIZONTAL);
+ gd.heightHint = 40;
+ target.setLayoutData(gd);
+
+ return group;
+ }
+
+ /**
+ * Creates the area for selection of bug attributes (status, etc.)
+ */
+ protected Control createLists(Composite control) {
+ GridData gd;
+ GridLayout layout;
+
+ // Search expression
+ Group group = new Group(control, SWT.NONE);
+ layout = new GridLayout();
+ layout.numColumns = 6;
+ group.setLayout(layout);
+ gd = new GridData(GridData.FILL_HORIZONTAL);
+ gd.horizontalSpan = 5;
+ group.setLayoutData(gd);
+
+ // Labels
+ Label label = new Label(group, SWT.LEFT);
+ label.setText("Status");
+
+ label = new Label(group, SWT.LEFT);
+ label.setText("Resolution");
+
+ label = new Label(group, SWT.LEFT);
+ label.setText("Severity");
+
+ label = new Label(group, SWT.LEFT);
+ label.setText("Priority");
+
+ label = new Label(group, SWT.LEFT);
+ label.setText("Hardware");
+
+ label = new Label(group, SWT.LEFT);
+ label.setText("OS");
+
+ // Lists
+ status = new List(group, SWT.MULTI | SWT.V_SCROLL | SWT.BORDER);
+ gd = new GridData(GridData.FILL_HORIZONTAL);
+ gd.heightHint = 40;
+ status.setLayoutData(gd);
+
+ resolution = new List(group, SWT.MULTI | SWT.V_SCROLL | SWT.BORDER);
+ gd = new GridData(GridData.FILL_HORIZONTAL);
+ gd.heightHint = 40;
+ resolution.setLayoutData(gd);
+
+ severity = new List(group, SWT.MULTI | SWT.V_SCROLL | SWT.BORDER);
+ gd = new GridData(GridData.FILL_HORIZONTAL);
+ gd.heightHint = 40;
+ severity.setLayoutData(gd);
+
+ priority = new List(group, SWT.MULTI | SWT.V_SCROLL | SWT.BORDER);
+ gd = new GridData(GridData.FILL_HORIZONTAL);
+ gd.heightHint = 40;
+ priority.setLayoutData(gd);
+
+ hardware = new List(group, SWT.MULTI | SWT.V_SCROLL | SWT.BORDER);
+ gd = new GridData(GridData.FILL_HORIZONTAL);
+ gd.heightHint = 40;
+ hardware.setLayoutData(gd);
+
+ os = new List(group, SWT.MULTI | SWT.V_SCROLL | SWT.BORDER);
+ gd = new GridData(GridData.FILL_HORIZONTAL);
+ gd.heightHint = 40;
+ os.setLayoutData(gd);
+
+ return group;
+ }
+
+ private Text daysText;
+
+ protected Control createLastDays(Composite control)
+ {
+ GridLayout layout;
+ GridData gd;
+
+ Group group = new Group(control, SWT.NONE);
+ layout = new GridLayout(3, false);
+ group.setLayout(layout);
+ group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ gd = new GridData(GridData.BEGINNING | GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL);
+ gd.horizontalSpan = 2;
+ group.setLayoutData(gd);
+
+ Label label = new Label(group, SWT.LEFT);
+ label.setText("Only bugs changed in the last ");
+
+ // operation combo
+ daysText = new Text(group, SWT.BORDER);
+ daysText.setTextLimit(5);
+ daysText.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ String days = daysText.getText();
+ if (days.length() == 0)
+ return;
+ for (int i = days.length() - 1; i >= 0; i--) {
+ try {
+ if (days.equals("") || Integer.parseInt(days) > -1) {
+ if (i == days.length() - 1)
+ return;
+ else
+ break;
+ }
+ } catch (NumberFormatException ex) {
+ days = days.substring(0, i);
+ }
+ }
+ daysText.setText(days);
+ }
+ });
+ label = new Label(group, SWT.LEFT);
+ label.setText(" Days.");
+
+
+ return group;
+ }
+
+ private static final String [] emailText = {"bug owner", "reporter", "CC list", "commenter"};
+ protected Control createEmail(Composite control) {
+ GridLayout layout;
+ GridData gd;
+
+ Group group = new Group(control, SWT.NONE);
+ layout = new GridLayout(3, false);
+ group.setLayout(layout);
+ group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ gd = new GridData(GridData.BEGINNING | GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL);
+ gd.horizontalSpan = 2;
+ group.setLayoutData(gd);
+
+ Composite buttons = new Composite(group, SWT.NONE);
+ layout = new GridLayout(4, false);
+ buttons.setLayout(layout);
+ buttons.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ gd = new GridData(GridData.BEGINNING);
+ gd.horizontalSpan = 3;
+ buttons.setLayoutData(gd);
+
+ emailButton = new Button[emailText.length];
+ for (int i = 0; i < emailButton.length; i++) {
+ Button button = new Button(buttons, SWT.CHECK);
+ button.setText(emailText[i]);
+ emailButton[i] = button;
+ }
+
+ Label label = new Label(group, SWT.LEFT);
+ label.setText("Email contains: ");
+
+ // operation combo
+ emailOperation = new Combo(group, SWT.SINGLE | SWT.READ_ONLY | SWT.BORDER);
+ emailOperation.setItems(emailOperationText);
+ emailOperation.setText(emailOperationText[0]);
+ emailOperation.select(0);
+
+ // pattern combo
+ emailPattern = new Combo(group, SWT.SINGLE | SWT.BORDER);
+ emailPattern.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ scontainer.setPerformActionEnabled(canQuery());
+ }
+ });
+ emailPattern.addSelectionListener(new SelectionAdapter() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ handleWidgetSelected(emailPattern, emailOperation, previousEmailPatterns);
+ }
+ });
+ gd = new GridData(GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL);
+ emailPattern.setLayoutData(gd);
+
+ return group;
+ }
+
+ /**
+ * Creates the buttons for remembering a query and accessing previously
+ * saved queries.
+ */
+ protected Control createSaveQuery(Composite control) {
+ GridLayout layout;
+ GridData gd;
+
+ Group group = new Group(control, SWT.NONE);
+ layout = new GridLayout(3, false);
+ group.setLayout(layout);
+ group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ gd = new GridData(GridData.BEGINNING | GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL);
+ gd.horizontalSpan = 2;
+ group.setLayoutData(gd);
+
+ loadButton = new Button(group, SWT.PUSH | SWT.LEFT);
+ loadButton.setText("Saved Queries...");
+ final BugzillaSearchPage bsp = this;
+ loadButton.addSelectionListener(new SelectionAdapter() {
+
+ @Override
+ public void widgetSelected(SelectionEvent event) {
+ GetQueryDialog qd = new GetQueryDialog(getShell(),
+ "Saved Queries", input);
+ if (qd.open() == InputDialog.OK) {
+ selIndex = qd.getSelected();
+ if (selIndex != -1) {
+ rememberedQuery = true;
+ performAction();
+ bsp.getShell().close();
+ }
+ }
+ }
+ });
+ loadButton.setEnabled(true);
+ loadButton.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING));
+
+ saveButton = new Button(group, SWT.PUSH | SWT.LEFT);
+ saveButton.setText("Remember...");
+ saveButton.addSelectionListener(new SelectionAdapter() {
+
+ @Override
+ public void widgetSelected(SelectionEvent event) {
+ SaveQueryDialog qd = new SaveQueryDialog(getShell(),
+ "Remember Query");
+ if (qd.open() == InputDialog.OK) {
+ String qName = qd.getText();
+ if (qName != null && qName.compareTo("") != 0) {
+ try {
+ input.add(getQueryParameters().toString(), qName, summaryPattern.getText());
+ }
+ catch (UnsupportedEncodingException e) {
+ /*
+ * Do nothing. Every implementation of the Java platform is required
+ * to support the standard charset "UTF-8"
+ */
+ }
+ }
+ }
+ }
+ });
+ saveButton.setEnabled(true);
+ saveButton.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING));
+
+ return group;
+ }
+
+ public static SavedQueryFile getInput() {
+ return input;
+ }
+
+ protected Control createUpdate(final Composite control) {
+ GridData gd;
+ Label label;
+
+ Composite group = new Composite(control, SWT.NONE);
+ GridLayout layout = new GridLayout(2, false);
+ group.setLayout(layout);
+ group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+ gd = new GridData(GridData.BEGINNING);
+ gd.horizontalSpan = 2;
+ group.setLayoutData(gd);
+
+ // Info text
+ label = new Label(group, SWT.LEFT);
+ label.setText("Update search options from server (may take several seconds):");
+ gd = new GridData(GridData.BEGINNING);
+ label.setLayoutData(gd);
+
+ updateButton = new Button(group, SWT.LEFT | SWT.PUSH);
+ updateButton.setText("Update");
+
+ updateButton.setLayoutData(new GridData());
+
+ updateButton.addMouseListener(new MouseAdapter() {
+
+ @Override
+ public void mouseUp(MouseEvent e) {
+
+ monitorDialog.open();
+ IProgressMonitor monitor = monitorDialog.getProgressMonitor();
+ monitor.beginTask("Updating search options...", 55);
+
+ try {
+ BugzillaPreferences.updateQueryOptions(monitor);
+
+ product.setItems(BugzillaPreferences.queryOptionsToArray(prefs.getString(IBugzillaConstants.PRODUCT_VALUES)));
+ monitor.worked(1);
+ component.setItems(BugzillaPreferences.queryOptionsToArray(prefs.getString(IBugzillaConstants.COMPONENT_VALUES)));
+ monitor.worked(1);
+ version.setItems(BugzillaPreferences.queryOptionsToArray(prefs.getString(IBugzillaConstants.VERSION_VALUES)));
+ monitor.worked(1);
+ target.setItems(BugzillaPreferences.queryOptionsToArray(prefs.getString(IBugzillaConstants.TARGET_VALUES)));
+ monitor.worked(1);
+ status.setItems(BugzillaPreferences.queryOptionsToArray(prefs.getString(IBugzillaConstants.STATUS_VALUES)));
+ monitor.worked(1);
+ status.setSelection(BugzillaPreferences.queryOptionsToArray(prefs.getString(IBugzillaConstants.PRESELECTED_STATUS_VALUES)));
+ monitor.worked(1);
+ resolution.setItems(BugzillaPreferences.queryOptionsToArray(prefs.getString(IBugzillaConstants.RESOLUTION_VALUES)));
+ monitor.worked(1);
+ severity.setItems(BugzillaPreferences.queryOptionsToArray(prefs.getString(IBugzillaConstants.SEVERITY_VALUES)));
+ monitor.worked(1);
+ priority.setItems(BugzillaPreferences.queryOptionsToArray(prefs.getString(IBugzillaConstants.PRIORITY_VALUES)));
+ monitor.worked(1);
+ hardware.setItems(BugzillaPreferences.queryOptionsToArray(prefs.getString(IBugzillaConstants.HARDWARE_VALUES)));
+ monitor.worked(1);
+ os.setItems(BugzillaPreferences.queryOptionsToArray(prefs.getString(IBugzillaConstants.OS_VALUES)));
+ monitor.worked(1);
+ }
+ catch (LoginException exception) {
+ // we had a problem that seems to have been caused from bad login info
+ 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(exception);
+ }
+ finally {
+ monitor.done();
+ monitorDialog.close();
+ }
+ }
+ });
+
+ return group;
+ }
+
+ private void handleWidgetSelected(Combo widget, Combo operation, ArrayList<BugzillaSearchData> history) {
+ if (widget.getSelectionIndex() < 0)
+ return;
+ int index = history.size() - 1 - widget.getSelectionIndex();
+ BugzillaSearchData patternData= history.get(index);
+ if (patternData == null || !widget.getText().equals(patternData.pattern))
+ return;
+ widget.setText(patternData.pattern);
+ operation.setText(operation.getItem(patternData.operation));
+ }
+
+ /**
+ * @see ISearchPage#performAction()
+ */
+ public boolean performAction() {
+ getPatternData(summaryPattern, summaryOperation, previousSummaryPatterns);
+ getPatternData(commentPattern, commentOperation, previousCommentPatterns);
+ getPatternData(this.emailPattern, emailOperation, previousEmailPatterns);
+
+ String summaryText;
+ String url;
+ if (rememberedQuery == true) {
+ url = getQueryURL(new StringBuffer(input.getQueryParameters(selIndex)));
+ summaryText = input.getSummaryText(selIndex);
+ }
+ else {
+ try {
+ StringBuffer params = getQueryParameters();
+ url = getQueryURL(params);
+ summaryText = summaryPattern.getText();
+ }
+ catch (UnsupportedEncodingException e) {
+ /*
+ * These statements should never be executed. Every implementation of
+ * the Java platform is required to support the standard charset
+ * "UTF-8"
+ */
+ url = "";
+ summaryText = "";
+ }
+ }
+
+ try {
+ // if the summary contains a single bug id, open the bug directly
+ int id = Integer.parseInt(summaryText);
+ return BugzillaUITools.show(id);
+ } catch (NumberFormatException ignored) {
+ // ignore this since this means that the text is not a bug id
+ }
+
+ // Don't activate the search result view until it is known that the
+ // user is not opening a bug directly -- there is no need to open
+ // the view if no searching is going to take place.
+ NewSearchUI.activateSearchResultView();
+
+ BugzillaPlugin.getDefault().getPreferenceStore().setValue(IBugzillaConstants.MOST_RECENT_QUERY, summaryText);
+
+ IBugzillaSearchResultCollector collector= new BugzillaSearchResultCollector();
+
+ IBugzillaSearchOperation op= new BugzillaSearchOperation(
+ url, collector);
+
+ BugzillaSearchQuery searchQuery = new BugzillaSearchQuery(op);
+ NewSearchUI.runQueryInBackground(searchQuery);
+
+ return true;
+ }
+
+ /**
+ * @see ISearchPage#setContainer(ISearchPageContainer)
+ */
+ public void setContainer(ISearchPageContainer container) {
+ scontainer = container;
+ }
+
+ @Override
+ public void setVisible(boolean visible) {
+ if (visible && summaryPattern != null) {
+ if (firstTime) {
+ firstTime = false;
+ // Set item and text here to prevent page from resizing
+ summaryPattern.setItems(getPreviousPatterns(previousSummaryPatterns));
+ commentPattern.setItems(getPreviousPatterns(previousCommentPatterns));
+ emailPattern.setItems(getPreviousPatterns(previousEmailPatterns));
+
+ product.setItems(productValues);
+ component.setItems(componentValues);
+ version.setItems(versionValues);
+ target.setItems(targetValues);
+
+ status.setItems(statusValues);
+ status.setSelection(preselectedStatusValues);
+ resolution.setItems(resolutionValues);
+ severity.setItems(severityValues);
+ priority.setItems(priorityValues);
+ hardware.setItems(hardwareValues);
+ os.setItems(osValues);
+ }
+ summaryPattern.setFocus();
+ scontainer.setPerformActionEnabled(canQuery());
+ }
+ super.setVisible(visible);
+ }
+
+ /**
+ * Returns <code>true</code> if at least some parameter is given to query on.
+ */
+ private boolean canQuery() {
+ return product.getSelectionCount() > 0 ||
+ component.getSelectionCount() > 0 ||
+ version.getSelectionCount() > 0 ||
+ target.getSelectionCount() > 0 ||
+ status.getSelectionCount() > 0 ||
+ resolution.getSelectionCount() > 0 ||
+ severity.getSelectionCount() > 0 ||
+ priority.getSelectionCount() > 0 ||
+ hardware.getSelectionCount() > 0 ||
+ os.getSelectionCount() > 0 ||
+ summaryPattern.getText().length() > 0 ||
+ commentPattern.getText().length() > 0 ||
+ emailPattern.getText().length() > 0;
+ }
+
+ /**
+ * Return search pattern data and update search history list.
+ * An existing entry will be updated or a new one created.
+ */
+ private BugzillaSearchData getPatternData(Combo widget, Combo operation, ArrayList<BugzillaSearchData> previousSearchQueryData) {
+ String pattern = widget.getText();
+ if (pattern == null || pattern.trim().equals("")) {
+ return null;
+ }
+ BugzillaSearchData match = null;
+ int i = previousSearchQueryData.size() - 1;
+ while (i >= 0) {
+ match = previousSearchQueryData.get(i);
+ if (pattern.equals(match.pattern)) {
+ break;
+ }
+ i--;
+ }
+ if (i >= 0) {
+ match.operation = operation.getSelectionIndex();
+ // remove - will be added last (see below)
+ previousSearchQueryData.remove(match);
+ } else {
+ match= new BugzillaSearchData(widget.getText(), operation.getSelectionIndex());
+ }
+ previousSearchQueryData.add(match);
+ return match;
+ }
+
+ /**
+ * Returns an array of previous summary patterns
+ */
+ private String [] getPreviousPatterns(ArrayList<BugzillaSearchData> patternHistory) {
+ int size = patternHistory.size();
+ String [] patterns = new String[size];
+ for (int i = 0; i < size; i++)
+ patterns[i]= (patternHistory.get(size - 1 - i)).pattern;
+ return patterns;
+ }
+
+
+ protected String getQueryURL(StringBuffer params) {
+ StringBuffer url = new StringBuffer(getQueryURLStart().toString());
+ url.append(params);
+ return url.toString();
+ }
+
+ /**
+ * Creates the bugzilla query URL start.
+ *
+ * Example: https://bugs.eclipse.org/bugs/buglist.cgi?
+ */
+ private 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(), "UTF-8") + "&Bugzilla_password=" + URLEncoder.encode(BugzillaPreferences.getPassword(), "UTF-8") + "&");
+ } catch (UnsupportedEncodingException e) {
+ /*
+ * Do nothing. Every implementation of the Java platform is required
+ * to support the standard charset "UTF-8"
+ */
+ }
+ }
+
+ return sb;
+ }
+
+ /**
+ * Goes through the query form and builds up the query parameters.
+ *
+ * Example: short_desc_type=substring&amp;short_desc=bla&amp; ...
+ * @throws UnsupportedEncodingException
+ */
+ protected StringBuffer getQueryParameters() throws UnsupportedEncodingException {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append("short_desc_type=");
+ sb.append(patternOperationValues[summaryOperation.getSelectionIndex()]);
+
+ sb.append("&short_desc=");
+ sb.append(URLEncoder.encode(summaryPattern.getText(), "UTF-8"));
+
+ int [] selected = product.getSelectionIndices();
+ for (int i = 0; i < selected.length; i++) {
+ sb.append("&product=");
+ sb.append(URLEncoder.encode(product.getItem(selected[i]), "UTF-8"));
+ }
+
+ selected = component.getSelectionIndices();
+ for (int i = 0; i < selected.length; i++) {
+ sb.append("&component=");
+ sb.append(URLEncoder.encode(component.getItem(selected[i]), "UTF-8"));
+ }
+
+ selected = version.getSelectionIndices();
+ for (int i = 0; i < selected.length; i++) {
+ sb.append("&version=");
+ sb.append(URLEncoder.encode(version.getItem(selected[i]), "UTF-8"));
+ }
+
+ selected = target.getSelectionIndices();
+ for (int i = 0; i < selected.length; i++) {
+ sb.append("&target_milestone=");
+ sb.append(URLEncoder.encode(target.getItem(selected[i]), "UTF-8"));
+ }
+
+ sb.append("&long_desc_type=");
+ sb.append(patternOperationValues[commentOperation.getSelectionIndex()]);
+ sb.append("&long_desc=");
+ sb.append(URLEncoder.encode(commentPattern.getText(), "UTF-8"));
+
+ selected = status.getSelectionIndices();
+ for (int i = 0; i < selected.length; i++) {
+ sb.append("&bug_status=");
+ sb.append(status.getItem(selected[i]));
+ }
+
+ selected = resolution.getSelectionIndices();
+ for (int i = 0; i < selected.length; i++) {
+ sb.append("&resolution=");
+ sb.append(resolution.getItem(selected[i]));
+ }
+
+ selected = severity.getSelectionIndices();
+ for (int i = 0; i < selected.length; i++) {
+ sb.append("&bug_severity=");
+ sb.append(severity.getItem(selected[i]));
+ }
+
+ selected = priority.getSelectionIndices();
+ for (int i = 0; i < selected.length; i++) {
+ sb.append("&priority=");
+ sb.append(priority.getItem(selected[i]));
+ }
+
+ selected = hardware.getSelectionIndices();
+ for (int i = 0; i < selected.length; i++) {
+ sb.append("&ref_platform=");
+ sb.append(URLEncoder.encode(hardware.getItem(selected[i]), "UTF-8"));
+ }
+
+ selected = os.getSelectionIndices();
+ for (int i = 0; i < selected.length; i++) {
+ sb.append("&op_sys=");
+ sb.append(URLEncoder.encode(os.getItem(selected[i]), "UTF-8"));
+ }
+
+ if (emailPattern.getText() != null) {
+ for (int i = 0; i < emailButton.length; i++) {
+ if (emailButton[i].getSelection()) {
+ sb.append("&");
+ sb.append(emailRoleValues[i]);
+ sb.append("=1");
+ }
+ }
+ sb.append("&emailtype1=");
+ sb.append(emailOperationValues[emailOperation.getSelectionIndex()]);
+ sb.append("&email1=");
+ sb.append(URLEncoder.encode(emailPattern.getText(), "UTF-8"));
+ }
+
+ if (daysText.getText() != null && !daysText.getText().equals("")) {
+ try
+ {
+ Integer.parseInt(daysText.getText());
+ sb.append("&changedin=");
+ sb.append(URLEncoder.encode(daysText.getText(), "UTF-8"));
+ }
+ catch(NumberFormatException ignored) {
+ // this means that the days is not a number, so don't worry
+ }
+ }
+
+ return sb;
+ }
+
+ //--------------- Configuration handling --------------
+
+ // Dialog store id constants
+ protected final static String PAGE_NAME = "BugzillaSearchPage"; //$NON-NLS-1$
+
+ protected Combo summaryOperation;
+
+ protected List product;
+
+ protected List os;
+
+ protected List hardware;
+
+ protected List priority;
+
+ protected List severity;
+
+ protected List resolution;
+
+ protected List status;
+
+ protected Combo commentOperation;
+
+ protected Combo commentPattern;
+
+ protected List component;
+
+ protected List version;
+
+ protected List target;
+
+ protected Combo emailOperation;
+
+ protected Combo emailPattern;
+
+ protected Button [] emailButton;
+
+ /** File containing saved queries */
+ protected static SavedQueryFile input;
+
+ /** "Remember query" button */
+ protected Button saveButton;
+ /** "Saved queries..." button */
+ protected Button loadButton;
+ /** Run a remembered query */
+ protected boolean rememberedQuery = false;
+ /** Index of the saved query to run */
+ protected int selIndex;
+
+ protected Button updateButton;
+
+ protected ProgressMonitorDialog monitorDialog = new ProgressMonitorDialog(BugzillaPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getShell());
+
+ /**
+ * Returns the page settings for this Java search page.
+ *
+ * @return the page settings to be used
+ */
+ private IDialogSettings getDialogSettings() {
+ IDialogSettings settings = BugzillaPlugin.getDefault().getDialogSettings();
+ fDialogSettings = settings.getSection(PAGE_NAME);
+ if (fDialogSettings == null)
+ fDialogSettings = settings.addNewSection(PAGE_NAME);
+ return fDialogSettings;
+ }
+
+ /**
+ * Initializes itself from the stored page settings.
+ */
+ private void readConfiguration() {
+ getDialogSettings();
+ }
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/search/BugzillaSearchResultView.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/search/BugzillaSearchResultView.java
new file mode 100644
index 000000000..08429d5ca
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/search/BugzillaSearchResultView.java
@@ -0,0 +1,229 @@
+/*******************************************************************************
+ * Copyright (c) 2003 - 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.bugzilla.ui.search;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.jface.viewers.DecoratingLabelProvider;
+import org.eclipse.jface.viewers.StructuredViewer;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.mylar.bugzilla.core.BugzillaPlugin;
+import org.eclipse.mylar.bugzilla.core.IBugzillaConstants;
+import org.eclipse.mylar.bugzilla.core.search.BugzillaContentProvider;
+import org.eclipse.mylar.bugzilla.core.search.BugzillaIdSearchSorter;
+import org.eclipse.mylar.bugzilla.core.search.BugzillaLabelProvider;
+import org.eclipse.mylar.bugzilla.core.search.BugzillaPrioritySearchSorter;
+import org.eclipse.mylar.bugzilla.core.search.BugzillaSeveritySearchSorter;
+import org.eclipse.mylar.bugzilla.core.search.BugzillaStateSearchSorter;
+import org.eclipse.mylar.bugzilla.ui.BugzillaTableContentProvider;
+import org.eclipse.mylar.bugzilla.ui.BugzillaUITools;
+import org.eclipse.mylar.bugzilla.ui.actions.BugzillaSortAction;
+import org.eclipse.mylar.bugzilla.ui.actions.OpenBugsAction;
+import org.eclipse.mylar.bugzilla.ui.favorites.actions.AddFavoriteAction;
+import org.eclipse.search.internal.ui.SearchMessages;
+import org.eclipse.search.internal.ui.SearchPlugin;
+import org.eclipse.search.internal.ui.SearchPreferencePage;
+import org.eclipse.search.internal.ui.util.ExceptionHandler;
+import org.eclipse.search.ui.IContextMenuConstants;
+import org.eclipse.search.ui.text.AbstractTextSearchViewPage;
+import org.eclipse.search.ui.text.Match;
+import org.eclipse.ui.IPageLayout;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.part.IShowInTargetList;
+
+
+/**
+ * Displays the results of a Bugzilla search.
+ * @see org.eclipse.search.ui.text.AbstractTextSearchViewPage
+ */
+public class BugzillaSearchResultView extends AbstractTextSearchViewPage implements IAdaptable {
+
+ // The categories to sort bug results by
+ public static final int ORDER_ID = 1;
+ public static final int ORDER_SEVERITY = 2;
+ public static final int ORDER_PRIORITY = 3;
+ public static final int ORDER_STATUS = 4;
+ public static final int ORDER_DEFAULT = ORDER_ID;
+
+ private static final String KEY_SORTING= IBugzillaConstants.PLUGIN_ID + ".search.resultpage.sorting"; //$NON-NLS-1$
+
+ private BugzillaContentProvider bugContentProvider;
+ private int bugCurrentSortOrder;
+ private BugzillaSortAction bugSortByIDAction;
+ private BugzillaSortAction bugSortBySeverityAction;
+ private BugzillaSortAction bugSortByPriorityAction;
+ private BugzillaSortAction bugSortByStatusAction;
+ private AddFavoriteAction addToFavoritesAction;
+ private OpenBugsAction openInEditorAction;
+
+ private static final String[] SHOW_IN_TARGETS= new String[] { IPageLayout.ID_RES_NAV };
+ private static final IShowInTargetList SHOW_IN_TARGET_LIST= new IShowInTargetList() {
+ public String[] getShowInTargetIds() {
+ return SHOW_IN_TARGETS;
+ }
+ };
+
+ private IPropertyChangeListener bugPropertyChangeListener;
+
+ /**
+ * Constructor
+ */
+ public BugzillaSearchResultView() {
+ // Only use the table layout.
+ super(FLAG_LAYOUT_FLAT);
+
+ bugSortByIDAction = new BugzillaSortAction("Bug ID", this, ORDER_ID);
+ bugSortBySeverityAction = new BugzillaSortAction("Bug severity", this, ORDER_SEVERITY);
+ bugSortByPriorityAction = new BugzillaSortAction("Bug priority", this, ORDER_PRIORITY);
+ bugSortByStatusAction = new BugzillaSortAction("Bug status", this, ORDER_STATUS);
+ bugCurrentSortOrder = ORDER_DEFAULT;
+
+ addToFavoritesAction = new AddFavoriteAction("Mark Result as Favorite", this);
+ openInEditorAction = new OpenBugsAction("Open Bug in Editor", this);
+
+ bugPropertyChangeListener= new IPropertyChangeListener() {
+ public void propertyChange(PropertyChangeEvent event) {
+ if (SearchPreferencePage.LIMIT_TABLE.equals(event.getProperty()) || SearchPreferencePage.LIMIT_TABLE_TO.equals(event.getProperty()))
+ if (getViewer() instanceof TableViewer) {
+ getViewPart().updateLabel();
+ getViewer().refresh();
+ }
+ }
+ };
+ SearchPlugin.getDefault().getPreferenceStore().addPropertyChangeListener(bugPropertyChangeListener);
+ }
+
+ @Override
+ protected void elementsChanged(Object[] objects) {
+ if (bugContentProvider != null) {
+ bugContentProvider.elementsChanged(objects);
+ }
+ }
+
+ @Override
+ protected void clear() {
+ if (bugContentProvider != null) {
+ bugContentProvider.clear();
+ }
+ }
+
+ // Allows the inherited method "getViewer" to be accessed publicly.
+ @Override
+ public StructuredViewer getViewer() {
+ return super.getViewer();
+ }
+
+ @Override
+ protected void configureTreeViewer(TreeViewer viewer) {
+ // The tree layout is not used, so this function does not need to do anything.
+ }
+
+ @Override
+ protected void configureTableViewer(TableViewer viewer) {
+ viewer.setUseHashlookup(true);
+ viewer.setLabelProvider(new DecoratingLabelProvider(new BugzillaLabelProvider(), PlatformUI.getWorkbench().getDecoratorManager().getLabelDecorator()));
+ viewer.setContentProvider(new BugzillaTableContentProvider(this));
+
+ // Set the order when the search view is loading so that the items are
+ // sorted right away
+ setSortOrder(bugCurrentSortOrder);
+
+ bugContentProvider= (BugzillaContentProvider) viewer.getContentProvider();
+ }
+
+ /**
+ * Sets the new sorting category, and reorders all of the bug reports.
+ * @param sortOrder The new category to sort bug reports by
+ */
+ public void setSortOrder(int sortOrder) {
+ bugCurrentSortOrder= sortOrder;
+ StructuredViewer viewer= getViewer();
+
+ switch (sortOrder) {
+ case ORDER_ID:
+ viewer.setSorter(new BugzillaIdSearchSorter());
+ break;
+ case ORDER_PRIORITY:
+ viewer.setSorter(new BugzillaPrioritySearchSorter());
+ break;
+ case ORDER_SEVERITY:
+ viewer.setSorter(new BugzillaSeveritySearchSorter());
+ break;
+ case ORDER_STATUS:
+ viewer.setSorter(new BugzillaStateSearchSorter());
+ break;
+ default:
+ // If the setting is not one of the four valid ones,
+ // use the default order setting.
+ sortOrder = ORDER_DEFAULT;
+ viewer.setSorter(new BugzillaIdSearchSorter());
+ break;
+ }
+
+ getSettings().put(KEY_SORTING, bugCurrentSortOrder);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
+ */
+ public Object getAdapter(Class adapter) {
+ if (IShowInTargetList.class.equals(adapter)) {
+ return SHOW_IN_TARGET_LIST;
+ }
+ return null;
+ }
+
+ @Override
+ protected void showMatch(Match match, int currentOffset, int currentLength, boolean activate) throws PartInitException {
+ try {
+ Object element = getCurrentMatch().getElement();
+ if (element instanceof IMarker) {
+ Integer id = (Integer) ((IMarker)element).getAttribute(IBugzillaConstants.HIT_MARKER_ATTR_ID);
+ BugzillaUITools.show(id.intValue());
+ }
+ }
+ catch (CoreException e) {
+ // if an error occurs, handle and log it
+ ExceptionHandler.handle(e, SearchMessages.Search_Error_search_title, SearchMessages.Search_Error_search_message); //$NON-NLS-2$ //$NON-NLS-1$
+ BugzillaPlugin.log(e.getStatus());
+ }
+ }
+
+ @Override
+ protected void fillContextMenu(IMenuManager mgr) {
+ super.fillContextMenu(mgr);
+
+ // Create the submenu for sorting
+ MenuManager sortMenu= new MenuManager(SearchMessages.SortDropDownAction_label); //$NON-NLS-1$
+ sortMenu.add(bugSortByIDAction);
+ sortMenu.add(bugSortByPriorityAction);
+ sortMenu.add(bugSortBySeverityAction);
+ sortMenu.add(bugSortByStatusAction);
+
+ // Check the right sort option
+ bugSortByIDAction.setChecked(bugCurrentSortOrder == bugSortByIDAction.getSortOrder());
+ bugSortByPriorityAction.setChecked(bugCurrentSortOrder == bugSortByPriorityAction.getSortOrder());
+ bugSortBySeverityAction.setChecked(bugCurrentSortOrder == bugSortBySeverityAction.getSortOrder());
+ bugSortByStatusAction.setChecked(bugCurrentSortOrder == bugSortByStatusAction.getSortOrder());
+
+ // Add the new context menu items
+ mgr.appendToGroup(IContextMenuConstants.GROUP_VIEWER_SETUP, sortMenu);
+ mgr.appendToGroup(IContextMenuConstants.GROUP_ADDITIONS, addToFavoritesAction);
+ mgr.appendToGroup(IContextMenuConstants.GROUP_OPEN, openInEditorAction);
+ }
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/wizard/AbstractBugWizard.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/wizard/AbstractBugWizard.java
new file mode 100644
index 000000000..76c07dffb
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/wizard/AbstractBugWizard.java
@@ -0,0 +1,351 @@
+/*******************************************************************************
+ * Copyright (c) 2003 - 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.bugzilla.ui.wizard;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.util.Iterator;
+
+import javax.security.auth.login.LoginException;
+
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.mylar.bugzilla.core.Attribute;
+import org.eclipse.mylar.bugzilla.core.BugPost;
+import org.eclipse.mylar.bugzilla.core.BugzillaException;
+import org.eclipse.mylar.bugzilla.core.BugzillaPlugin;
+import org.eclipse.mylar.bugzilla.core.BugzillaPreferences;
+import org.eclipse.mylar.bugzilla.core.IBugzillaConstants;
+import org.eclipse.mylar.bugzilla.core.NewBugModel;
+import org.eclipse.mylar.bugzilla.ui.editor.ExistingBugEditorInput;
+import org.eclipse.search.internal.ui.SearchMessages;
+import org.eclipse.search.internal.ui.util.ExceptionHandler;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.INewWizard;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.PartInitException;
+
+
+/**
+ * Class that contains shared functions for the wizards that submit bug reports.
+ *
+ * @author Eric Booth
+ */
+public abstract class AbstractBugWizard extends Wizard implements INewWizard {
+
+ /** The model used to store all of the data for the wizard */
+ protected NewBugModel model;
+
+ /**
+ * Flag to indicate if the wizard can be completed based on the attributes
+ * page
+ */
+ protected boolean attributeCompleted = false;
+
+ /** The workbench instance */
+ protected IWorkbench workbenchInstance;
+
+ /** The ID of the posted bug report. */
+ private String id;
+
+ /**
+ * Constructor for AbstractBugWizard
+ */
+ public AbstractBugWizard() {
+ super();
+ model = new NewBugModel();
+ id = null; // Since there is no bug posted yet.
+ super.setDefaultPageImageDescriptor(BugzillaPlugin.imageDescriptorFromPlugin(IBugzillaConstants.PLUGIN_ID, "icons/wizban/bug-wizard.gif"));
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.ui.IWorkbenchWizard#init(org.eclipse.ui.IWorkbench,
+ * org.eclipse.jface.viewers.IStructuredSelection)
+ */
+ public void init(IWorkbench workbench, IStructuredSelection selection) {
+ this.workbenchInstance = workbench;
+ }
+
+ @Override
+ public void addPages() {
+ try {
+ // check Bugzilla preferences to see if user has supplied a username
+ if (BugzillaPreferences.getUserName().equals(""))
+ throw new LoginException(
+ "A Bugzilla User Name has not been provided."
+ + " Please check your Bugzilla Preferences information.");
+ // Each wizard has its own way of creating and adding the page
+ addPagesHelper();
+ } catch (LoginException e) {
+ MessageDialog.openError(Display.getDefault().getActiveShell(), "Posting Error",
+ "Cannot proceed because your login name or password is incorrect."
+ + "\nPlease check your settings in the Bugzilla preferences. ");
+ } catch (Exception e) {
+ BugzillaPlugin.getDefault().logAndShowExceptionDetailsDialog(e, "occurred.", "Bugzilla Error");
+ }
+ }
+
+ /**
+ * A helper function for "addPages" that creates and adds the first page to
+ * the wizard
+ *
+ * @throws Exception
+ */
+ abstract protected void addPagesHelper() throws Exception;
+
+ /**
+ * A helper function for "canFinish" (implemented in the individual wizard
+ * classes)
+ *
+ * @param dataPage
+ * The first page in the wizard, casted as an
+ * AbstractWizardListPage.
+ * @return true if the wizard could be finished, and false otherwise
+ */
+ public boolean canFinishHelper(AbstractWizardListPage dataPage) {
+ // cannot complete the wizard from the first page
+ if (this.getContainer().getCurrentPage() == dataPage)
+ return false;
+ else
+ return attributeCompleted;
+ }
+
+ @Override
+ public boolean performFinish() {
+ if (getWizardDataPage().serverSelected()) {
+ // If the bug report is sent successfully,
+ // then close the wizard and open the bug in an editor
+ if (postBug()) {
+ openBugEditor();
+ return true;
+ }
+ // If the report was not sent, keep the wizard open
+ else {
+ return false;
+ }
+ }
+
+ if (getWizardDataPage().offlineSelected()) {
+ saveBugOffline();
+ return true;
+ }
+
+ // If no action was selected, keep the wizard open.
+ return false;
+ }
+
+ /**
+ * Attempts to post the bug on the Bugzilla server. If it fails, an error
+ * message pops up.
+ *
+ * @return true if the bug is posted successfully, and false otherwise
+ */
+ protected boolean postBug() {
+
+ BugPost form = new BugPost();
+ form.setPrefix("Bug ");
+ form.setPostfix1(" posted");
+ form.setPostfix2(" Submitted");
+
+ try {
+ setURL(form, "post_bug.cgi");
+ // go through all of the attributes and add them to the bug post
+ Iterator<Attribute> itr = model.getAttributes().iterator();
+ while (itr.hasNext()) {
+ Attribute a = itr.next();
+ if (a != null && a.getParameterName() != null
+ && a.getParameterName().compareTo("") != 0
+ && !a.isHidden()) {
+ String key = a.getName();
+ String value = null;
+
+ // get the values from the attribute
+ if (key.equalsIgnoreCase("OS")) {
+ value = a.getValue();
+ } else if (key.equalsIgnoreCase("Version")) {
+ value = a.getValue();
+ } else if (key.equalsIgnoreCase("Severity")) {
+ value = a.getValue();
+ } else if (key.equalsIgnoreCase("Platform")) {
+ value = a.getValue();
+ } else if (key.equalsIgnoreCase("Component")) {
+ value = a.getValue();
+ } else if (key.equalsIgnoreCase("Priority")) {
+ value = a.getValue();
+ } else if (key.equalsIgnoreCase("URL")) {
+ value = a.getValue();
+ }
+
+ // add the attribute to the bug post
+ if (value == null)
+ value = "";
+
+ form.add(a.getParameterName(), value);
+ } else if (a != null && a.getParameterName() != null
+ && a.getParameterName().compareTo("") != 0
+ && a.isHidden()) {
+ // we have a hidden attribute, add it to the posting
+ form.add(a.getParameterName(), a.getValue());
+
+ }
+
+ }
+
+ // set the summary, and description
+
+ // add the summary to the bug post
+ form.add("short_desc", model.getSummary());
+
+ // format the description of the bug so that it is roughly in 80
+ // character lines
+ formatDescription();
+
+ if (model.getDescription().length() != 0) {
+ // add the new comment to the bug post if there is some text in
+ // it
+ form.add("comment", model.getDescription());
+ }
+
+ // Flag to indicate if the bug was successfully sent
+ boolean sentSuccessfully = false;
+
+ // update the bug on the server
+ try {
+ id = form.post();
+
+ if (id != null) {
+ sentSuccessfully = true;
+ }
+
+ } catch (BugzillaException e) {
+ MessageDialog
+ .openError(
+ null,
+ "I/O Error",
+ "Bugzilla could not post your bug.");
+ BugzillaPlugin.log(e);
+ } catch (LoginException e) {
+ // if we had an error with logging in, display an error
+ MessageDialog
+ .openError(
+ null,
+ "Posting Error",
+ "Bugzilla could not post your bug since your login name or password is incorrect."
+ + "\nPlease check your settings in the bugzilla preferences. ");
+ }
+
+ return sentSuccessfully;
+ } catch (MalformedURLException e) {
+ MessageDialog
+ .openError(
+ null,
+ "Unsupported Protocol",
+ "The server that was specified for Bugzilla is not supported by your JVM."
+ + "\nPlease make sure that you are using a JDK that supports SSL.");
+ BugzillaPlugin.log(e);
+ return false; // The report was not sent
+ }
+ }
+
+ /**
+ * Try to open the editor with the newly created bug.
+ */
+ protected void openBugEditor() {
+
+ IEditorInput input = null;
+ try {
+ input = new ExistingBugEditorInput(Integer.parseInt(id));
+ BugzillaPlugin.getDefault().getWorkbench()
+ .getActiveWorkbenchWindow().getActivePage().openEditor(
+ input, IBugzillaConstants.EXISTING_BUG_EDITOR_ID, false);
+ } catch (LoginException e) {
+ // if we had an error with logging in, display an error
+ MessageDialog.openError(null, "Posting Error",
+ "Bugzilla could not access and display your bug in the editor because your login name or password is incorrect."
+ + "\nPlease check your settings in the bugzilla preferences. ");
+ } catch (PartInitException e) {
+ // if there was a problem, handle it and log it, then get out of
+ // here
+ ExceptionHandler.handle(e, SearchMessages.Search_Error_search_title, SearchMessages.Search_Error_search_message);
+ BugzillaPlugin.log(e.getStatus());
+ } catch (IOException e) {
+ BugzillaPlugin.getDefault().logAndShowExceptionDetailsDialog(e, "occurred while opening the bug report.", "Bugzilla Error");
+ }
+ }
+
+ /**
+ * Saves the bug report offline on the user's hard-drive. All offline bug
+ * reports are saved together in a single file in the plug-in's directory.
+ */
+ abstract protected void saveBugOffline();
+
+ /**
+ * Function to set the url to post the bug to.
+ *
+ * @param form
+ * A reference to a BugPost that the bug is going to be posted to
+ * @param formName
+ * The form that we wish to use to submit the bug
+ */
+ protected void setURL(BugPost form, String formName)
+ throws MalformedURLException {
+
+ String baseURL = BugzillaPlugin.getDefault().getServerName();
+ if (!baseURL.endsWith("/"))
+ baseURL += "/";
+ form.setURL(baseURL + formName);
+
+ // add the login information to the bug post
+ form.add("Bugzilla_login", BugzillaPreferences.getUserName());
+ form.add("Bugzilla_password", BugzillaPreferences.getPassword());
+ }
+
+ /**
+ * Format the description into lines of about 80 characters so that it is
+ * displayed properly in bugzilla
+ */
+ protected void formatDescription() {
+ String origDesc = model.getDescription();
+ String[] descArray = new String[(origDesc.length() / 80 + 1) * 2];
+ for (int i = 0; i < descArray.length; i++)
+ descArray[i] = null;
+ int j = 0;
+ while (true) {
+ int spaceIndex = origDesc.indexOf(" ", 75);
+ if (spaceIndex == origDesc.length() || spaceIndex == -1) {
+ descArray[j] = origDesc;
+ break;
+ }
+ descArray[j] = origDesc.substring(0, spaceIndex);
+ origDesc = origDesc.substring(spaceIndex + 1, origDesc.length());
+ j++;
+ }
+
+ String newDesc = "";
+
+ for (int i = 0; i < descArray.length; i++) {
+ if (descArray[i] == null)
+ break;
+ newDesc += descArray[i] + "\n";
+ }
+ model.setDescription(newDesc);
+
+ }
+
+ /**
+ * @return the last page of this wizard
+ */
+ abstract protected AbstractWizardDataPage getWizardDataPage();
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/wizard/AbstractWizardDataPage.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/wizard/AbstractWizardDataPage.java
new file mode 100644
index 000000000..777231a25
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/wizard/AbstractWizardDataPage.java
@@ -0,0 +1,621 @@
+/*******************************************************************************
+ * Copyright (c) 2003 - 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.bugzilla.ui.wizard;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.mylar.bugzilla.core.Attribute;
+import org.eclipse.mylar.bugzilla.core.NewBugModel;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.internal.WorkbenchImages;
+import org.eclipse.ui.internal.ide.IDEInternalWorkbenchImages;
+
+
+/**
+ * Class that contains shared functions for the last page of the wizards that
+ * submit bug reports. This page allows the user to set the bug report's
+ * attributes before submitting it.
+ */
+public abstract class AbstractWizardDataPage extends WizardPage implements
+ Listener {
+
+ /** The instance of the workbench */
+ protected IWorkbench workbench;
+
+ /** Text field for the bugs url */
+ protected Text urlText;
+
+ /** Text field for the description of the bug */
+ protected Text descriptionText;
+
+ /** Text field for the summary of the bug */
+ protected Text summaryText;
+
+ /** Radio button to select when sending the new bug report to the server */
+ protected Button serverButton;
+
+ /** Radio button to select when saving the new bug report offline */
+ protected Button offlineButton;
+
+ /** Combo box for the component that caused the bug */
+ protected Combo componentCombo;
+
+ /** Combo box for the priority of the bug */
+ protected Combo priorityCombo;
+
+ /** Combo box for the platform the bug occurred on */
+ protected Combo platformCombo;
+
+ /** Combo box for the severity of the bug */
+ protected Combo severityCombo;
+
+ /** Combo box for the products version */
+ protected Combo versionCombo;
+
+ /** Combo box for the OS that the bug occurred under */
+ protected Combo oSCombo;
+
+ /** Enum for value */
+ protected final String VALUE = "VALUE";
+
+ /** Enum for property */
+ protected final String PROPERTY = "PROPERTY";
+
+ /** Enum for header */
+ protected final String HEADER = "HEADER";
+
+ /** The horizontal indentation of the labels */
+ protected final int HORZ_INDENT = 0;
+
+ /** Status variable for the possible errors on this page */
+ protected IStatus attributeStatus;
+
+ /**
+ * Constructor for AbstractWizardDataPage
+ *
+ * @param pageName
+ * the name of the page
+ * @param title
+ * the title of the page
+ * @param description
+ * the description text for the page
+ * @param workbench
+ * the instance of the workbench
+ */
+ public AbstractWizardDataPage(String pageName, String title,
+ String description, IWorkbench workbench) {
+ super(pageName);
+ setTitle(title);
+ setDescription(description);
+ this.workbench = workbench;
+
+ // set the status for the page
+ attributeStatus = new Status(IStatus.OK, "not_used", 0, "", null);
+ }
+
+ /**
+ * Applies the status to the status line of a dialog page.
+ *
+ * @param status
+ * The status to apply to the status line
+ */
+ protected void applyToStatusLine(IStatus status) {
+ String message = status.getMessage();
+ if (message.length() == 0)
+ message = null;
+ switch (status.getSeverity()) {
+ case IStatus.OK:
+ setErrorMessage(null);
+ setMessage(message);
+ break;
+ case IStatus.WARNING:
+ setErrorMessage(null);
+ setMessage(message, WizardPage.WARNING);
+ break;
+ case IStatus.INFO:
+ setErrorMessage(null);
+ setMessage(message, WizardPage.INFORMATION);
+ break;
+ default:
+ setErrorMessage(null);
+ setMessage(message, WizardPage.ERROR);
+ break;
+ }
+ }
+
+ /**
+ * Make sure that a String that is <code>null</code> is changed to a null
+ * string
+ *
+ * @param text
+ * The text to check if it is null or not
+ * @return The string in its proper format
+ */
+ public String checkText(String text) {
+ if (text == null)
+ return "";
+ else
+ return text;
+ }
+
+ @Override
+ public boolean canFlipToNextPage() {
+ // no next page for this path through the wizard
+ return false;
+ }
+
+ /**
+ * Create a new layout for a component
+ *
+ * @param composite
+ * The parent composite
+ * @param colSpan
+ * The number of columns that this can span
+ * @param text
+ * The text to add to the control
+ * @param style
+ * The style that the control should have
+ */
+ public void newLayout(Composite composite, int colSpan, String text,
+ String style) {
+ GridData data = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
+ data.horizontalSpan = colSpan;
+
+ // create the proper layout for the style
+ if (style.equalsIgnoreCase(VALUE)) {
+ Label l = new Label(composite, SWT.NONE);
+ FontData fontData = l.getFont().getFontData()[0];
+ fontData.setStyle(SWT.BOLD | fontData.getStyle());
+ Font font = new Font(null, fontData);
+ l.setFont(font);
+ l.setText(checkText(text));
+
+ data.horizontalIndent = HORZ_INDENT;
+ l.setLayoutData(data);
+ } else if (style.equalsIgnoreCase(PROPERTY)) {
+ Label l = new Label(composite, SWT.NONE);
+ FontData fontData = l.getFont().getFontData()[0];
+ fontData.setStyle(SWT.BOLD | fontData.getStyle());
+ Font font = new Font(null, fontData);
+ l.setFont(font);
+ l.setText(checkText(text));
+
+ data.horizontalIndent = HORZ_INDENT;
+ l.setLayoutData(data);
+ } else {
+ Composite generalTitleGroup = new Composite(composite, SWT.NONE);
+ generalTitleGroup.setLayoutData(new GridData(
+ GridData.FILL_HORIZONTAL));
+ generalTitleGroup.setLayoutData(data);
+ GridLayout generalTitleLayout = new GridLayout();
+ generalTitleLayout.numColumns = 2;
+ generalTitleLayout.marginWidth = 0;
+ generalTitleLayout.marginHeight = 9;
+ generalTitleGroup.setLayout(generalTitleLayout);
+
+ Label image = new Label(generalTitleGroup, SWT.NONE);
+ image.setImage(WorkbenchImages.getImage(
+ IDEInternalWorkbenchImages.IMG_OBJS_WELCOME_ITEM));
+
+ GridData gd = new GridData(GridData.FILL_BOTH);
+ gd.verticalAlignment = GridData.VERTICAL_ALIGN_BEGINNING;
+ image.setLayoutData(gd);
+ Label l = new Label(composite, SWT.NONE);
+ FontData fontData = l.getFont().getFontData()[0];
+ fontData.setStyle(SWT.BOLD | fontData.getStyle());
+ Font font = new Font(null, fontData);
+ l.setFont(font);
+ l.setText(checkText(text));
+
+ data.horizontalIndent = HORZ_INDENT;
+ l.setLayoutData(data);
+ }
+ }
+
+ /**
+ * Determine if the page is complete when the summary is changed
+ *
+ * @param e
+ * The event which occurred
+ */
+ public void handleEvent(Event e) {
+ boolean pageComplete = isPageComplete();
+
+ // Initialize a variable with the no error status
+ Status status = new Status(IStatus.OK, "not_used", 0, "", null);
+
+ setPageComplete(pageComplete);
+
+ if (!pageComplete)
+ status = new Status(IStatus.ERROR, "not_used", 0,
+ "You must enter a summary and a description", null);
+
+ attributeStatus = status;
+
+ // Show the most serious error
+ applyToStatusLine(attributeStatus);
+
+ setPageComplete(pageComplete);
+ getWizard().getContainer().updateButtons();
+ }
+
+ @Override
+ public IWizardPage getNextPage() {
+ saveDataToModel();
+ return null;
+ }
+
+ /**
+ * Sets the completed field on the wizard class when all the needed
+ * information is entered and the wizard can be completed
+ *
+ * @return true if the wizard can be completed, false otherwise
+ */
+ @Override
+ public boolean isPageComplete() {
+ AbstractBugWizard wizard = (AbstractBugWizard) getWizard();
+ if (summaryText.getText() == null || summaryText.getText().equals("")
+ || descriptionText.getText() == null
+ || descriptionText.getText().equals("")) {
+ wizard.attributeCompleted = false;
+ return false;
+ }
+ saveDataToModel();
+ return true;
+ }
+
+ /**
+ * Save the data obtained from this point in the wizard to the model.
+ */
+ protected void saveDataToModel() {
+ // get the model that we are using
+ AbstractBugWizard wizard = (AbstractBugWizard) getWizard();
+ NewBugModel nbm = wizard.model;
+
+ nbm.setDescription(descriptionText.getText());
+ nbm.setSummary(summaryText.getText());
+
+ // go through each of the attributes and sync their values with the
+ // combo boxes
+ for (Iterator<Attribute> it = nbm.getAttributes().iterator(); it.hasNext();) {
+ Attribute attribute = it.next();
+ String key = attribute.getName();
+ Map<String, String> values = attribute.getOptionValues();
+
+ if (values == null)
+ values = new HashMap<String, String>();
+
+ if (key.equals("OS")) {
+ String os = oSCombo.getItem(oSCombo.getSelectionIndex());
+ attribute.setValue(os);
+ } else if (key.equals("Version")) {
+ String version = versionCombo.getItem(versionCombo
+ .getSelectionIndex());
+ attribute.setValue(version);
+ } else if (key.equals("Severity")) {
+ String severity = severityCombo.getItem(severityCombo
+ .getSelectionIndex());
+ attribute.setValue(severity);
+ } else if (key.equals("Platform")) {
+ String platform = platformCombo.getItem(platformCombo
+ .getSelectionIndex());
+ attribute.setValue(platform);
+ } else if (key.equals("Component")) {
+ String component = componentCombo.getItem(componentCombo
+ .getSelectionIndex());
+ attribute.setValue(component);
+ } else if (key.equals("Priority")) {
+ String priority = priorityCombo.getItem(priorityCombo
+ .getSelectionIndex());
+ attribute.setValue(priority);
+ } else if (key.equals("URL")) {
+ String url = urlText.getText();
+ if (url.equalsIgnoreCase("http://"))
+ url = "";
+ attribute.setValue(url);
+ } else {
+ // do nothing
+ }
+ }
+ wizard.attributeCompleted = true;
+ }
+
+ @Override
+ protected void setControl(Control c) {
+ super.setControl(c);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite)
+ */
+ public void createControl(Composite parent) {
+ // whether the priority exists or not
+ boolean priExist = false;
+
+ String url = null;
+
+ // get the model for the new bug
+ AbstractBugWizard wizard = (AbstractBugWizard) getWizard();
+ NewBugModel nbm = wizard.model;
+
+ // Attributes Composite- this holds all the combo fields and text
+ // fields
+ Composite attributesComposite = new Composite(parent, SWT.NONE);
+ GridLayout attributesLayout = new GridLayout();
+ attributesLayout.numColumns = 4;
+ attributesLayout.horizontalSpacing = 14;
+ attributesLayout.verticalSpacing = 6;
+ attributesComposite.setLayout(attributesLayout);
+
+ GridData attributesData = new GridData(GridData.FILL_BOTH);
+ attributesData.horizontalSpan = 1;
+ attributesData.grabExcessVerticalSpace = false;
+ attributesComposite.setLayoutData(attributesData);
+ // End Attributes Composite
+
+ GridLayout attributesTitleLayout = new GridLayout();
+ attributesTitleLayout.horizontalSpacing = 0;
+ attributesTitleLayout.marginWidth = 0;
+
+ GridData attributesTitleData = new GridData(
+ GridData.HORIZONTAL_ALIGN_FILL);
+ attributesTitleData.horizontalSpan = 4;
+
+ attributesTitleData.grabExcessVerticalSpace = false;
+
+ // Add the product to the composite
+ newLayout(attributesComposite, 1, "Product", PROPERTY);
+ newLayout(attributesComposite, 1, nbm.getProduct(), VALUE);
+
+ // Populate Attributes
+ for (Iterator<Attribute> it = nbm.getAttributes().iterator(); it.hasNext();) {
+ Attribute attribute = it.next();
+ String key = attribute.getParameterName();
+ String name = attribute.getName();
+ String value = checkText(attribute.getValue());
+ Map<String, String> values = attribute.getOptionValues();
+
+ // if it is a hidden field, don't try to display it
+ if (attribute.isHidden())
+ continue;
+
+ if (key == null)
+ key = "";
+
+ if (values == null)
+ values = new HashMap<String, String>();
+
+ GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
+ data.horizontalSpan = 1;
+ data.horizontalIndent = HORZ_INDENT;
+
+ // create and populate the combo fields for the attributes
+ if (key.equals("op_sys")) {
+ newLayout(attributesComposite, 1, name, PROPERTY);
+ oSCombo = new Combo(attributesComposite, SWT.NO_BACKGROUND
+ | SWT.MULTI | SWT.V_SCROLL | SWT.READ_ONLY);
+
+ oSCombo.setLayoutData(data);
+ Set<String> s = values.keySet();
+ String[] a = s.toArray(new String[s.size()]);
+ for (int i = 0; i < a.length; i++) {
+ oSCombo.add(a[i]);
+ }
+ int index;
+ if ((index = oSCombo.indexOf(value)) == -1)
+ index = 0;
+ oSCombo.select(index);
+ oSCombo.addListener(SWT.Modify, this);
+ } else if (key.equals("version")) {
+ newLayout(attributesComposite, 1, name, PROPERTY);
+
+ versionCombo = new Combo(attributesComposite, SWT.NO_BACKGROUND
+ | SWT.MULTI | SWT.V_SCROLL | SWT.READ_ONLY);
+
+ versionCombo.setLayoutData(data);
+ Set<String> s = values.keySet();
+ String[] a = s.toArray(new String[s.size()]);
+ for (int i = 0; i < a.length; i++) {
+ versionCombo.add(a[i]);
+ }
+ int index;
+ if ((index = versionCombo.indexOf(value)) == -1)
+ index = 0;
+ versionCombo.select(index);
+ versionCombo.addListener(SWT.Modify, this);
+ } else if (key.equals("bug_severity")) {
+ newLayout(attributesComposite, 1, name, PROPERTY);
+ severityCombo = new Combo(attributesComposite,
+ SWT.NO_BACKGROUND | SWT.MULTI | SWT.V_SCROLL
+ | SWT.READ_ONLY);
+
+ severityCombo.setLayoutData(data);
+ Set<String> s = values.keySet();
+ String[] a = s.toArray(new String[s.size()]);
+ for (int i = 0; i < a.length; i++) {
+ severityCombo.add(a[i]);
+ }
+ int index;
+ if ((index = severityCombo.indexOf(value)) == -1)
+ index = 0;
+ severityCombo.select(index);
+ severityCombo.addListener(SWT.Modify, this);
+
+ } else if (key.equals("rep_platform")) {
+ newLayout(attributesComposite, 1, name, PROPERTY);
+ platformCombo = new Combo(attributesComposite,
+ SWT.NO_BACKGROUND | SWT.MULTI | SWT.V_SCROLL
+ | SWT.READ_ONLY);
+
+ platformCombo.setLayoutData(data);
+ Set<String> s = values.keySet();
+ String[] a = s.toArray(new String[s.size()]);
+ for (int i = 0; i < a.length; i++) {
+ platformCombo.add(a[i]);
+ }
+ int index;
+ if ((index = platformCombo.indexOf(value)) == -1)
+ index = 0;
+ platformCombo.select(index);
+ platformCombo.addListener(SWT.Modify, this);
+ } else if (key.equals("component")) {
+ newLayout(attributesComposite, 1, name, PROPERTY);
+ componentCombo = new Combo(attributesComposite,
+ SWT.NO_BACKGROUND | SWT.MULTI | SWT.V_SCROLL
+ | SWT.READ_ONLY);
+
+ componentCombo.setLayoutData(data);
+ Set<String> s = values.keySet();
+ String[] a = s.toArray(new String[s.size()]);
+ for (int i = 0; i < a.length; i++) {
+ componentCombo.add(a[i]);
+ }
+ int index;
+ if ((index = componentCombo.indexOf(value)) == -1)
+ index = 0;
+ componentCombo.select(index);
+ componentCombo.addListener(SWT.Modify, this);
+ } else if (key.equals("priority")) {
+ newLayout(attributesComposite, 1, name, PROPERTY);
+ priorityCombo = new Combo(attributesComposite,
+ SWT.NO_BACKGROUND | SWT.MULTI | SWT.V_SCROLL
+ | SWT.READ_ONLY);
+
+ priorityCombo.setLayoutData(data);
+ Set<String> s = values.keySet();
+ String[] a = s.toArray(new String[s.size()]);
+ for (int i = 0; i < a.length; i++) {
+ priorityCombo.add(a[i]);
+ }
+ int index;
+ if ((index = priorityCombo.indexOf(value)) == -1)
+ index = 0;
+ priorityCombo.select(index);
+ priorityCombo.addListener(SWT.Modify, this);
+ priExist = true;
+ } else if (key.equals("bug_file_loc")) {
+ url = value;
+ } else {
+ // do nothing if it isn't a standard value to change
+ }
+ }
+
+ if (priExist) {
+ newLayout(attributesComposite, 1, "", PROPERTY);
+ newLayout(attributesComposite, 1, "", PROPERTY);
+ }
+
+ GridData summaryTextData;
+
+ if (url != null) {
+ // add the assigned to text field
+ newLayout(attributesComposite, 1, "URL", PROPERTY);
+ urlText = new Text(attributesComposite, SWT.BORDER | SWT.SINGLE
+ | SWT.WRAP);
+ summaryTextData = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
+
+ summaryTextData.horizontalSpan = 3;
+ summaryTextData.widthHint = 200;
+ urlText.setLayoutData(summaryTextData);
+ urlText.setText(url);
+ urlText.addListener(SWT.FocusOut, this);
+ }
+
+ // add the summary text field
+ newLayout(attributesComposite, 1, "Summary", PROPERTY);
+ summaryText = new Text(attributesComposite, SWT.BORDER | SWT.SINGLE
+ | SWT.WRAP);
+ summaryTextData = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
+
+ summaryTextData.horizontalSpan = 3;
+ summaryTextData.widthHint = 200;
+ summaryText.setLayoutData(summaryTextData);
+ summaryText.setText(nbm.getSummary());
+ summaryText.addListener(SWT.Modify, this);
+
+ // Description Text
+ Composite descriptionComposite = new Composite(attributesComposite,
+ SWT.NONE);
+
+ descriptionComposite.setLayout(attributesTitleLayout);
+
+ GridData descriptionData = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
+ descriptionData.horizontalSpan = 4;
+ descriptionData.grabExcessVerticalSpace = false;
+ descriptionComposite.setLayoutData(descriptionData);
+ newLayout(descriptionComposite, 4, "Description:", HEADER);
+
+ // add the description text field
+ descriptionText = new Text(attributesComposite, SWT.BORDER | SWT.MULTI
+ | SWT.WRAP | SWT.V_SCROLL);
+
+ GridData descriptionTextData = new GridData(
+ GridData.HORIZONTAL_ALIGN_FILL);
+ descriptionTextData.horizontalSpan = 4;
+ descriptionTextData.widthHint = 200;
+ descriptionTextData.heightHint = 100;
+ descriptionText.setLayoutData(descriptionTextData);
+ descriptionText.addListener(SWT.Modify, this);
+
+ serverButton = new Button(attributesComposite, SWT.RADIO);
+ serverButton.setText("Submit bug report to the server.");
+ GridData toServerButtonData = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
+ toServerButtonData.horizontalSpan = 4;
+ serverButton.setLayoutData(toServerButtonData);
+ serverButton.setSelection(true);
+
+ offlineButton = new Button(attributesComposite, SWT.RADIO);
+ offlineButton.setText("Save bug report offline.");
+ GridData offlineButtonData = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
+ offlineButtonData.horizontalSpan = 4;
+ offlineButton.setLayoutData(offlineButtonData);
+ offlineButton.setSelection(false);
+
+ setControl(attributesComposite);
+ return;
+ }
+
+ /**
+ * @return <code>true</code> if the radio button to submit the bug to the server is selected.
+ */
+ public boolean serverSelected() {
+ return (serverButton == null) ? false : serverButton.getSelection();
+ }
+
+ /**
+ * @return <code>true</code> if the radio button to save the bug offline is selected.
+ */
+ public boolean offlineSelected() {
+ return (offlineButton == null) ? false : offlineButton.getSelection();
+ }
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/wizard/AbstractWizardListPage.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/wizard/AbstractWizardListPage.java
new file mode 100644
index 000000000..47d4c3aa0
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/wizard/AbstractWizardListPage.java
@@ -0,0 +1,213 @@
+/*******************************************************************************
+ * Copyright (c) 2003 - 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.bugzilla.ui.wizard;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.ui.IWorkbench;
+
+/**
+ * Class that contains shared functions for the first page of the wizards that
+ * submit bug reports. This page provides the user with a list of items to
+ * choose from.
+ */
+public abstract class AbstractWizardListPage extends WizardPage implements
+ Listener {
+
+ /** The instance of the workbench */
+ protected IWorkbench workbench;
+
+ /** The list box for the list of items to choose from */
+ protected List listBox;
+
+ /** Status variable for the possible errors on this page */
+ protected IStatus listStatus;
+
+ /**
+ * Constructor for AbstractWizardListPage
+ *
+ * @param pageName
+ * the name of the page
+ * @param title
+ * the title of the page
+ * @param description
+ * the description text for the page
+ * @param workbench
+ * the instance of the workbench
+ */
+ public AbstractWizardListPage(String pageName, String title,
+ String description, IWorkbench workbench) {
+ super(pageName);
+ setTitle(title);
+ setDescription(description);
+ this.workbench = workbench;
+
+ // set the status for the page
+ listStatus = new Status(IStatus.OK, "not_used", 0, "", null);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite)
+ */
+ public void createControl(Composite parent) {
+ // create the composite to hold the widgets
+ GridData gd;
+ Composite composite = new Composite(parent, SWT.NULL);
+
+ // create the desired layout for this wizard page
+ GridLayout gl = new GridLayout();
+ int ncol = 1;
+ gl.numColumns = ncol;
+ composite.setLayout(gl);
+
+ // create the bug report label
+ Label label = new Label(composite, SWT.NONE);
+ label.setText(getTableName());
+ FontData fontData = label.getFont().getFontData()[0];
+ fontData.setStyle(SWT.BOLD | fontData.getStyle());
+ int height = (int) Math.abs(fontData.getHeight() * 1.25);
+ fontData.setHeight(height);
+ Font font = new Font(null, fontData);
+ label.setFont(font);
+
+ // create the list of bug reports
+ gd = new GridData(GridData.FILL_HORIZONTAL);
+ gd.heightHint = 200;
+ listBox = new List(composite, SWT.SINGLE | SWT.BORDER | SWT.READ_ONLY
+ | SWT.V_SCROLL);
+ listBox.setLayoutData(gd);
+
+ createLine(composite, ncol);
+
+ // Each wizard has different types of items to add to the list
+ populateList();
+
+ // set the composite as the control for this page
+ setControl(composite);
+ addListeners();
+ }
+
+ public abstract String getTableName();
+
+ /**
+ * Populate the list of items
+ */
+ abstract protected void populateList();
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
+ */
+ abstract public void handleEvent(Event event);
+
+ /**
+ * A helper function for "handleEvent"
+ *
+ * @param event
+ * the event which occurred
+ * @param errorMessage
+ * the error message unique to the wizard calling this function
+ */
+ protected void handleEventHelper(Event event, String errorMessage) {
+ // Initialize a variable with the no error status
+ Status status = new Status(IStatus.OK, "not_used", 0, "", null);
+
+ // If the event is triggered by the list of items, respond with the
+ // corresponding status
+ if (event.widget == listBox) {
+ if (listBox.getSelectionIndex() == -1)
+ status = new Status(IStatus.ERROR, "not_used", 0, errorMessage,
+ null);
+ listStatus = status;
+ }
+
+ // Show the most serious error
+ applyToStatusLine(listStatus);
+ getWizard().getContainer().updateButtons();
+ }
+
+ /**
+ * Applies the status to the status line of a dialog page.
+ *
+ * @param status
+ * The status to apply to the status line
+ */
+ protected void applyToStatusLine(IStatus status) {
+ String message = status.getMessage();
+ if (message.length() == 0)
+ message = null;
+ switch (status.getSeverity()) {
+ case IStatus.OK:
+ setErrorMessage(null);
+ setMessage(message);
+ break;
+ case IStatus.WARNING:
+ setErrorMessage(null);
+ setMessage(message, WizardPage.WARNING);
+ break;
+ case IStatus.INFO:
+ setErrorMessage(null);
+ setMessage(message, WizardPage.INFORMATION);
+ break;
+ default:
+ setErrorMessage(null);
+ setMessage(message, WizardPage.ERROR);
+ break;
+ }
+ }
+
+ /**
+ * Create a separator line in the dialog
+ *
+ * @param parent
+ * The composite to create the line on
+ * @param ncol
+ * The number of columns to span
+ */
+ protected void createLine(Composite parent, int ncol) {
+ Label line = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL
+ | SWT.BOLD);
+ GridData gridData = new GridData(GridData.FILL_HORIZONTAL);
+ gridData.horizontalSpan = ncol;
+ line.setLayoutData(gridData);
+ }
+
+ @Override
+ public boolean canFlipToNextPage() {
+ if (getErrorMessage() != null)
+ return false;
+ if (listBox.getSelectionIndex() != -1)
+ return true;
+ return false;
+ }
+
+ /**
+ * Add any listeners that we need
+ */
+ protected void addListeners() {
+ listBox.addListener(SWT.Selection, this);
+ }
+
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/wizard/NewBugWizard.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/wizard/NewBugWizard.java
new file mode 100644
index 000000000..48e3393db
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/wizard/NewBugWizard.java
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * Copyright (c) 2003 - 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.bugzilla.ui.wizard;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.mylar.bugzilla.core.BugzillaPlugin;
+import org.eclipse.mylar.bugzilla.core.BugzillaRepository;
+import org.eclipse.mylar.bugzilla.ui.OfflineView;
+
+
+/**
+ * The main wizard class for creating a new bug
+ *
+ * @author Shawn Minto
+ */
+public class NewBugWizard extends AbstractBugWizard {
+
+ /** The wizard page for where the product is selected */
+ WizardProductPage productPage;
+
+ /** The wizard page where the attributes are selected and the bug is submitted */
+ WizardAttributesPage attributePage;
+
+ @Override
+ protected void addPagesHelper() throws Exception {
+ // add only the product page for now if there are any products
+
+ // try to get the list of products from the server
+ if (!model.hasParsedProducts()) {
+ try {
+ WizardProductPage.products = BugzillaRepository.getInstance()
+ .getProductList();
+ model.setConnected(true);
+ model.setParsedProductsStatus(true);
+ } catch (Exception e) {
+ // determine if exception was problem connecting, or if
+ // something else
+ // if problem connecting, then set flag and use
+ // ProductConfiguration
+ // otherwise throw the exception to be dealt with
+
+ // problem connecting to Bugzilla server, so unable to get
+ // Products from product page
+ model.setConnected(false);
+
+ if (e instanceof IOException) {
+ // Dialog to inform user that the program could not connect
+ // to the Bugzilla server
+ MessageDialog
+ .openError(
+ null,
+ "Bugzilla Connect Error",
+ "Unable to connect to Bugzilla server.\n"
+ + "Product configuration will be read from the workspace.");
+
+ // use ProductConfiguration to get products instead
+ String[] products = BugzillaPlugin.getDefault()
+ .getProductConfiguration().getProducts();
+
+ // add products from ProductConfiguration to product page's
+ // product list
+ List<String> productList = new ArrayList<String>();
+ for (int i = 0; i < products.length; i++)
+ productList.add(i, products[i]);
+ WizardProductPage.products = productList;
+ model.setParsedProductsStatus(true);
+ } else
+ throw e;
+ }
+ }
+
+ if (WizardProductPage.products.size() != 0
+ && BugzillaPlugin.getDefault().getProductConfiguration()
+ .getProducts().length > 1) {
+ productPage = new WizardProductPage(workbenchInstance, this);
+ addPage(productPage);
+ } else {
+ // There wasn't a list of products so there must only be 1
+ if (!model.hasParsedAttributes()) {
+ if (model.isConnected())
+ BugzillaRepository.getInstance().getnewBugAttributes(model,
+ true);
+ else
+ BugzillaRepository.getInstance().getProdConfigAttributes(
+ model);
+ model.setParsedAttributesStatus(true);
+ }
+
+ // add the attributes page to the wizard
+ attributePage = new WizardAttributesPage(workbenchInstance);
+ addPage(attributePage);
+ }
+ }
+
+ @Override
+ public boolean canFinish() {
+ return super.canFinishHelper(productPage);
+ }
+
+ @Override
+ protected void saveBugOffline() {
+ // Since the bug report is new, it just needs to be added to the
+ // existing list of reports in the offline file.
+ OfflineView.saveOffline(model);
+ }
+
+ @Override
+ protected AbstractWizardDataPage getWizardDataPage() {
+ return attributePage;
+ }
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/wizard/WizardAttributesPage.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/wizard/WizardAttributesPage.java
new file mode 100644
index 000000000..10c31d5fc
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/wizard/WizardAttributesPage.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2003 - 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.bugzilla.ui.wizard;
+
+import org.eclipse.ui.IWorkbench;
+
+/**
+ * Wizard page shown when the user has chosen a product to log a bug for.
+ */
+public class WizardAttributesPage extends AbstractWizardDataPage {
+
+ /**
+ * Constructor for WizardAttributesPage
+ *
+ * @param workbench
+ * The instance of the workbench.
+ */
+ public WizardAttributesPage(IWorkbench workbench) {
+ super("Page2", "New Bug Report", "Enter Bug", workbench);
+ }
+
+}
diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/wizard/WizardProductPage.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/wizard/WizardProductPage.java
new file mode 100644
index 000000000..956a28f77
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/bugzilla/ui/wizard/WizardProductPage.java
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ * Copyright (c) 2003 - 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.bugzilla.ui.wizard;
+
+import java.util.Iterator;
+
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.mylar.bugzilla.core.BugzillaPlugin;
+import org.eclipse.mylar.bugzilla.core.BugzillaRepository;
+import org.eclipse.mylar.bugzilla.core.NewBugModel;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.ui.IWorkbench;
+
+
+/**
+ * @author Shawn Minto
+ *
+ * The first page of the new bug wizard where the user chooses the bug's product
+ */
+public class WizardProductPage extends AbstractWizardListPage {
+
+ /** The list of products to submit a bug report for */
+ static java.util.List<String> products = null;
+
+ /** Reference to the bug wizard which created this page so we can create the second page */
+ NewBugWizard bugWizard;
+
+ /** String to hold previous product; determines if attribute option values need to be updated */
+ private String prevProduct;
+
+ /**
+ * Constructor for WizardProductPage
+ *
+ * @param workbench
+ * The instance of the workbench
+ * @param bugWiz
+ * The bug wizard which created this page
+ */
+ public WizardProductPage(IWorkbench workbench, NewBugWizard bugWiz) {
+ super("Page1", "New Bug Report",
+ "Pick a product on which to enter a bug.", workbench);
+ this.bugWizard = bugWiz;
+ }
+
+ /**
+ * Populates the listBox with all available products.
+ */
+ @Override
+ protected void populateList() {
+ Iterator<String> itr = products.iterator();
+
+ while (itr.hasNext()) {
+ String prod = itr.next();
+ listBox.add(prod);
+ }
+ }
+
+ @Override
+ public void handleEvent(Event event) {
+ handleEventHelper(event, "You must select a product");
+ }
+
+ @Override
+ public IWizardPage getNextPage() {
+ // save the product information to the model
+ saveDataToModel();
+ NewBugWizard wizard = (NewBugWizard) getWizard();
+ NewBugModel model = wizard.model;
+
+ // try to get the attributes from the bugzilla server
+ try {
+ if (!model.hasParsedAttributes() || !prevProduct.equals(model.getProduct())) {
+ if (model.isConnected()) {
+ BugzillaRepository.getInstance().getnewBugAttributes(model, false);
+ }
+ else {
+ BugzillaRepository.getInstance().getProdConfigAttributes(model);
+ }
+ model.setParsedAttributesStatus(true);
+ if (prevProduct == null) {
+ bugWizard.attributePage = new WizardAttributesPage(workbench);
+ bugWizard.addPage(bugWizard.attributePage);
+ }
+ else {
+ // selected product has changed
+ bugWizard.attributePage.setControl(null); // will createControl again with new attributes in model
+ }
+ }
+ } catch (Exception e) {
+ BugzillaPlugin.getDefault().logAndShowExceptionDetailsDialog(e, "occurred.", "Bugzilla Error");
+ }
+ return super.getNextPage();
+ }
+
+ /**
+ * Save the currently selected product to the model when next is clicked
+ */
+ private void saveDataToModel() {
+ // Gets the model
+ NewBugModel model = bugWizard.model;
+
+ prevProduct = model.getProduct();
+ model.setProduct((listBox.getSelection())[0]);
+ }
+
+ @Override
+ public String getTableName() {
+ return "Product:";
+ }
+}
+
diff --git a/org.eclipse.mylyn.bugzilla.ui/toc.xml b/org.eclipse.mylyn.bugzilla.ui/toc.xml
new file mode 100644
index 000000000..0e7d5de97
--- /dev/null
+++ b/org.eclipse.mylyn.bugzilla.ui/toc.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?NLS TYPE="org.eclipse.help.toc"?>
+
+<toc label="Bugzilla Client User Guide" topic="docs/html/book.html">
+ <topic label="Getting Started" href="docs/html/start.html">
+ <topic label="Bugzilla Setup" href="docs/html/start/setup.html"/>
+ <topic label="Using Bugzilla" href="docs/html/start/use.html">
+ <topic label="Bugzilla Search" href="docs/html/start/bugzillaSearch.html"/>
+ <topic label="Bugzilla Update" href="docs/html/start/bugzillaUpdate.html"/>
+ <topic label="Bugzilla Favorites" href="docs/html/start/bugzillaFavorites.html"/>
+ <topic label="Save Query" href="docs/html/start/saveQuery.html"/>
+ <topic label="Bugzilla Editor" href="docs/html/start/bugEditor.html"/>
+ <topic label="Offline Reports" href="docs/html/start/offlineReports.html"/>
+ <topic label="New Bug Wizard" href="docs/html/start/bugWizard.html"/>
+ </topic>
+ </topic>
+</toc>

Back to the top