Modified bug museum to allow bugs to be sliced and diced more easily by
a) maintaining separate bug lists and customizable Museum entry points
b) adding a SuggestBox search ability
c) mandating bug summaries.
d) fixed css loading problem
e) added ability to check DOM after widgets are created
Desk check by:jlabanca
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2518 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/reference/code-museum/src/com/google/gwt/museum/client/common/AbstractIssue.java b/reference/code-museum/src/com/google/gwt/museum/client/common/AbstractIssue.java
new file mode 100644
index 0000000..fd002ce
--- /dev/null
+++ b/reference/code-museum/src/com/google/gwt/museum/client/common/AbstractIssue.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.museum.client.common;
+
+import com.google.gwt.core.client.EntryPoint;
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.LinkElement;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.RootPanel;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * An abstract issue that can be used in the code museum. Each
+ * {@link AbstractIssue} should address a single issue. If at all possible, that
+ * issue should be obvious from the initial ui state.
+ */
+public abstract class AbstractIssue implements EntryPoint {
+ /**
+ * Headline for this issue.
+ */
+ private String headline;
+
+ /**
+ * Creates the css associated with this issue.
+ *
+ * @return link with css
+ */
+ public LinkElement createCSS() {
+ String cssName;
+ if (hasCSS()) {
+ // Fetch the associated style sheet using an HTTP request
+ cssName = getClass().getName();
+ cssName = cssName.substring(cssName.lastIndexOf(".") + 1);
+ } else {
+ cssName = "Default";
+ }
+
+ LinkElement issueLinkElement = Document.get().createLinkElement();
+ issueLinkElement.setRel("stylesheet");
+ issueLinkElement.setType("text/css");
+ issueLinkElement.setHref("issues/" + cssName + ".css");
+ return issueLinkElement;
+ }
+
+ /**
+ * <p>
+ * Create a widget that illustrates the issue. Each issue should include a
+ * detailed description of the expected results and the observed results
+ * before the issue was fixed.
+ * </p>
+ * <p>
+ * Note that createIssue will may be called multiple times if the user
+ * refreshes the issue. If you save state within the instance, you must clear
+ * it out and reset the issue when createIssue is called again.
+ * </p>
+ *
+ * @return a widget that can reproduce the issue
+ */
+ public abstract Widget createIssue();
+
+ /**
+ * Returns the "<i>classname</i>: summary".
+ *
+ * @return a short summary of the issue, including the class name
+ */
+ public final String getHeadline() {
+ if (headline == null) {
+ String className = getClass().getName();
+ headline = className.substring(className.lastIndexOf(".") + 1) + ": "
+ + getSummary();
+ }
+ return headline;
+ }
+
+ /**
+ * Return a detailed description of what the user should expect to see. The
+ * description will be added above the example. You can also include
+ * instructions to reproduce the issue.
+ *
+ * @return instructions explaining what the user should see
+ */
+ public abstract String getInstructions();
+
+ /**
+ * Gets the summary for this test. All tests should include a summary so users
+ * can scan through them quickly.
+ */
+ public abstract String getSummary();
+
+ /**
+ * Does the test have css?
+ *
+ * @return true to load a CSS file of the same name, placed in the issues
+ * directory
+ */
+ public abstract boolean hasCSS();
+
+ /**
+ * Called immediately after the widget is attached.
+ */
+ public void onAttached() {
+ // By default do nothing.
+ }
+
+ public void onModuleLoad() {
+ if (hasCSS()) {
+ Utility.getHeadElement().appendChild(createCSS());
+ }
+
+ Window.setTitle(getHeadline());
+ RootPanel.get().add(new HTML(getInstructions()));
+ RootPanel.get().add(createIssue());
+ onAttached();
+ }
+
+}
diff --git a/reference/code-museum/src/com/google/gwt/museum/client/common/Utility.java b/reference/code-museum/src/com/google/gwt/museum/client/common/Utility.java
new file mode 100644
index 0000000..c4d4093
--- /dev/null
+++ b/reference/code-museum/src/com/google/gwt/museum/client/common/Utility.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.gwt.museum.client.common;
+
+import com.google.gwt.dom.client.HeadElement;
+
+/**
+ * Utility helper methods.
+ */
+public class Utility {
+ /**
+ * Convenience method for getting the document's head element.
+ *
+ * @return the document's head element
+ */
+ public static native HeadElement getHeadElement() /*-{
+ return $doc.getElementsByTagName("head")[0];
+ }-*/;
+}
diff --git a/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/DefaultMuseum.java b/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/DefaultMuseum.java
new file mode 100644
index 0000000..6484eab
--- /dev/null
+++ b/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/DefaultMuseum.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.gwt.museum.client.defaultmuseum;
+
+import com.google.gwt.core.client.EntryPoint;
+import com.google.gwt.museum.client.viewer.Museum;
+
+/**
+ * Default bug museum. Contains a list of all GWT issues reported in the system
+ * to date.
+ */
+public class DefaultMuseum extends Museum implements EntryPoint {
+ public DefaultMuseum() {
+ addIssue(new Issue2290());
+ addIssue(new Issue2307());
+ addIssue(new Issue2321());
+ }
+}
diff --git a/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/Issue2290.java b/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/Issue2290.java
new file mode 100644
index 0000000..8a0e016
--- /dev/null
+++ b/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/Issue2290.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.museum.client.defaultmuseum;
+
+import com.google.gwt.museum.client.common.AbstractIssue;
+import com.google.gwt.user.client.ui.Tree;
+import com.google.gwt.user.client.ui.TreeItem;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * <h1> gwt-TreeItem refers to the wrong element in TreeItem </h1>
+ *
+ * <p>
+ * gwt-TreeItem used to refer to the span that directly wrapped the text in a
+ * TreeItem. Now it refers to the table element that holds the expand/collapse
+ * image and the text. gwt-TreeItem-selected is still added to the span, so
+ * there is an inconsistency here.
+ * </p>
+ */
+public class Issue2290 extends AbstractIssue {
+
+ @Override
+ public Widget createIssue() {
+ Tree tree = new Tree();
+ TreeItem root = tree.addItem("Root Item");
+ root.addItem("Item1");
+ root.addItem("Item2");
+ root.addItem("Item3");
+ root.addItem("Item4");
+
+ root.setState(true);
+ tree.setSelectedItem(root);
+
+ return tree;
+ }
+
+ @Override
+ public String getInstructions() {
+ return "The background of the Root Item, when selected, should be "
+ + "completely red, with no visible green.";
+ }
+
+ @Override
+ public String getSummary() {
+ return "Tree background test";
+ }
+
+ @Override
+ public boolean hasCSS() {
+ return true;
+ }
+}
diff --git a/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/Issue2307.java b/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/Issue2307.java
new file mode 100644
index 0000000..10cb529
--- /dev/null
+++ b/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/Issue2307.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.museum.client.defaultmuseum;
+
+import com.google.gwt.museum.client.common.AbstractIssue;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.CaptionPanel;
+import com.google.gwt.user.client.ui.ClickListener;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.TextBox;
+import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * <h1>Normalize design of CaptionPanel</h1>
+ *
+ * <p>
+ * Methods should be set/getCaptionHTML, set/getCaptionText,
+ * set/getContentWidget. Write lots of unit tests.
+ * </p>
+ */
+public class Issue2307 extends AbstractIssue {
+
+ private CaptionPanel captionPanel;
+
+ /**
+ * A set of options used to set the caption and content in the caption panel.
+ */
+ private class ControlPanel extends Composite {
+ private final Grid grid = new Grid(3, 2);
+
+ public ControlPanel() {
+ initWidget(grid);
+
+ // Add option to set the text
+ final TextBox textBox = new TextBox();
+ textBox.setText("<b>CaptionPanel</b>");
+ grid.setWidget(0, 1, textBox);
+ grid.setWidget(0, 0, new Button("setCaptionText", new ClickListener() {
+ public void onClick(Widget sender) {
+ captionPanel.setCaptionText(textBox.getText());
+ }
+ }));
+
+ // Add option to set the html
+ final TextBox htmlBox = new TextBox();
+ htmlBox.setText("<b>CaptionPanel</b>");
+ grid.setWidget(1, 1, htmlBox);
+ grid.setWidget(1, 0, new Button("setCaptionHTML", new ClickListener() {
+ public void onClick(Widget sender) {
+ captionPanel.setCaptionHTML(htmlBox.getText());
+ }
+ }));
+
+ // Add option to set the content
+ final TextBox contentBox = new TextBox();
+ contentBox.setText("<b><i>I am a Button</i></b>");
+ grid.setWidget(2, 1, contentBox);
+ grid.setWidget(2, 0, new Button("setContentWidget", new ClickListener() {
+ public void onClick(Widget sender) {
+ captionPanel.setContentWidget(new Button(contentBox.getText()));
+ }
+ }));
+ }
+ }
+
+ @Override
+ public Widget createIssue() {
+ captionPanel = new CaptionPanel("CaptionPanel");
+ VerticalPanel p = new VerticalPanel();
+ p.setSpacing(6);
+ p.add(captionPanel);
+ p.add(new ControlPanel());
+ return p;
+ }
+
+ @Override
+ public String getInstructions() {
+ return "Verify the usage of various CaptionPanel methods.";
+ }
+
+ @Override
+ public String getSummary() {
+ return "CaptionPanel tests";
+ }
+
+ @Override
+ public boolean hasCSS() {
+ return false;
+ }
+}
diff --git a/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/Issue2321.java b/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/Issue2321.java
new file mode 100644
index 0000000..6e86956
--- /dev/null
+++ b/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/Issue2321.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.museum.client.defaultmuseum;
+
+import com.google.gwt.museum.client.common.AbstractIssue;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.ClickListener;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.DeckPanel;
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * <h1>DeckPanel children getOffsetWidth/Height() return 0 after r2060</h1>
+ *
+ * <p>
+ * Child widgets of DeckPanel used to be able to call getOffsetWidth/Height() in
+ * onLoad() to obtain the widget's offset dimensions. These methods now return 0
+ * (zero) in onLoad(), and are only correct later, e.g. in a deferred command.
+ * </p>
+ */
+public class Issue2321 extends AbstractIssue {
+ /**
+ * A set of options used to set the caption and content in the caption panel.
+ */
+ private class ControlPanel extends Composite {
+ private final Grid grid = new Grid(3, 2);
+
+ public ControlPanel() {
+ initWidget(grid);
+
+ // Add option to detach the deck panel
+ Button addWidgetButton = new Button("Add widget", new ClickListener() {
+ public void onClick(Widget sender) {
+ addWidgetToDeckPanel();
+ }
+ });
+ grid.setWidget(0, 0, addWidgetButton);
+
+ // Add option to retrieve the dimensions of the content
+ Button updateDimButton = new Button("Get Current Dimensions",
+ new ClickListener() {
+ public void onClick(Widget sender) {
+ updateContentDimensions();
+ }
+ });
+ grid.setWidget(0, 1, updateDimButton);
+
+ // Add labels for the content height and width
+ grid.setHTML(1, 0, "Content Height:");
+ grid.setHTML(2, 0, "Content Width:");
+ }
+
+ /**
+ * Add another widget to the deck panel.
+ */
+ public void addWidgetToDeckPanel() {
+ int numWidgets = deck.getWidgetCount();
+ HTML content = new HTML("Content " + numWidgets) {
+ @Override
+ protected void onLoad() {
+ updateContentDimensions();
+ }
+ };
+
+ content.setStylePrimaryName("deckPanel-content");
+ deck.add(content);
+ deck.showWidget(numWidgets);
+ }
+
+ /**
+ * Retrieve the size of the content widgets.
+ */
+ public void updateContentDimensions() {
+ Widget content = deck.getWidget(deck.getWidgetCount() - 1);
+ grid.setHTML(1, 1, content.getOffsetHeight() + "");
+ grid.setHTML(2, 1, content.getOffsetWidth() + "");
+ }
+ }
+
+ /**
+ * The options panel to control this test.
+ */
+ private ControlPanel controlPanel = new ControlPanel();
+
+ /**
+ * The {@link DeckPanel} to be tested.
+ */
+ private DeckPanel deck;
+
+ /**
+ * The main container that holds the control panel and deck panel.
+ */
+ private VerticalPanel vPanel;
+
+ @Override
+ public Widget createIssue() {
+ // Create the deck panel and grab the size of the contents on load
+ deck = new DeckPanel() {
+ @Override
+ protected void onLoad() {
+ controlPanel.addWidgetToDeckPanel();
+ }
+ };
+ deck.setStylePrimaryName("deckPanel");
+
+ // Combine the control panel and DeckPanel
+ vPanel = new VerticalPanel();
+ vPanel.add(controlPanel);
+ vPanel.add(deck);
+ return vPanel;
+ }
+
+ @Override
+ public String getInstructions() {
+ return "The height and width of the content should be greater than 0.";
+ }
+
+ @Override
+ public String getSummary() {
+ return "DeckPanel should return content dimensions onLoad";
+ }
+
+ @Override
+ public boolean hasCSS() {
+ return true;
+ }
+}
diff --git a/reference/code-museum/src/com/google/gwt/museum/client/viewer/Museum.java b/reference/code-museum/src/com/google/gwt/museum/client/viewer/Museum.java
new file mode 100644
index 0000000..fa258de
--- /dev/null
+++ b/reference/code-museum/src/com/google/gwt/museum/client/viewer/Museum.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.museum.client.viewer;
+
+import com.google.gwt.core.client.EntryPoint;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.LinkElement;
+import com.google.gwt.museum.client.common.AbstractIssue;
+import com.google.gwt.museum.client.common.Utility;
+import com.google.gwt.user.client.DeferredCommand;
+import com.google.gwt.user.client.IncrementalCommand;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.ui.AbstractImagePrototype;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.ChangeListener;
+import com.google.gwt.user.client.ui.ClickListener;
+import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.HasVerticalAlignment;
+import com.google.gwt.user.client.ui.HorizontalPanel;
+import com.google.gwt.user.client.ui.Image;
+import com.google.gwt.user.client.ui.ImageBundle;
+import com.google.gwt.user.client.ui.ListBox;
+import com.google.gwt.user.client.ui.MultiWordSuggestOracle;
+import com.google.gwt.user.client.ui.RootPanel;
+import com.google.gwt.user.client.ui.SimplePanel;
+import com.google.gwt.user.client.ui.SuggestBox;
+import com.google.gwt.user.client.ui.SuggestOracle;
+import com.google.gwt.user.client.ui.SuggestionEvent;
+import com.google.gwt.user.client.ui.SuggestionHandler;
+import com.google.gwt.user.client.ui.Widget;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+
+/**
+ * A repository for demonstrating bugs we once faced.
+ */
+public class Museum implements EntryPoint {
+ /**
+ * Images used in the museum.
+ */
+ public static interface MuseumImages extends ImageBundle {
+ AbstractImagePrototype nextButton();
+
+ AbstractImagePrototype prevButton();
+ }
+
+ /**
+ * Class used to implement a callback when a css file is loaded. We add a
+ * width style of 10px to isLoaded classes, then test to see if the style is
+ * active.
+ */
+ class Poller implements IncrementalCommand {
+ private HTML test;
+ private AbstractIssue issue;
+ private SimplePanel monitor = new SimplePanel();
+
+ public Poller() {
+ RootPanel.get().add(monitor);
+ }
+
+ public boolean execute() {
+ if (test.getOffsetWidth() == 10) {
+ issueContainer.setWidget(issue.createIssue());
+ issue.onAttached();
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ public void startPolling(AbstractIssue issue) {
+ test = new HTML();
+ test.setStyleName("isLoaded");
+ monitor.setWidget(test);
+ this.issue = issue;
+ DeferredCommand.addCommand(this);
+ }
+ }
+
+ /**
+ * The images used in this application.
+ */
+ public static final MuseumImages IMAGES = GWT.create(MuseumImages.class);
+
+ /**
+ * A reference for all issues.
+ */
+ private final ArrayList<AbstractIssue> issues = new ArrayList<AbstractIssue>();
+
+ /**
+ * Add an issue to the list of issues.
+ *
+ * @param issue the issue to add
+ */
+
+ private final Poller poller = new Poller();
+
+ /**
+ * The panel that contains the current example.
+ */
+ private final SimplePanel issueContainer = new SimplePanel();
+
+ /**
+ * A container to hold the CSS that will be applied to the issue.
+ */
+ private LinkElement issueLinkElement = null;
+
+ /**
+ * A description of the current issue.
+ */
+ private final HTML issueDescription = new HTML();
+
+ /**
+ * The list of all issues.
+ */
+ private final ListBox issueList = new ListBox();
+
+ /**
+ * Add an issue to the museum. Should be called in the inherited constructor.
+ *
+ * @param issue the issue to be added
+ */
+ public void addIssue(AbstractIssue issue) {
+ issues.add(issue);
+ }
+
+ public void onModuleLoad() {
+
+ // Add the options and issue containers to the page
+ RootPanel.get().add(createOptionsPanel());
+ RootPanel.get().add(issueDescription);
+ issueDescription.setStylePrimaryName("museum-issueDescription");
+ RootPanel.get().add(issueContainer);
+ issueContainer.setStylePrimaryName("museum-issueContainer");
+
+ // Default to the first issue
+ refreshIssue();
+ }
+
+ /**
+ * Create a suggest box with all current suggestions in it.
+ */
+ private SuggestBox createIssueFinder() {
+ class IssueSuggestion extends MultiWordSuggestOracle.MultiWordSuggestion {
+ private AbstractIssue issue;
+
+ public IssueSuggestion(AbstractIssue issue) {
+ super("", issue.getHeadline());
+ this.issue = issue;
+ }
+
+ public AbstractIssue getIssue() {
+ return issue;
+ }
+ }
+
+ SuggestOracle oracle = new SuggestOracle() {
+
+ @Override
+ public void requestSuggestions(Request request, Callback callback) {
+ String ofInterest = ("(.* )*" + request.getQuery() + ".*").toLowerCase();
+ ArrayList<Suggestion> suggestions = new ArrayList<Suggestion>();
+ HashSet s = new HashSet();
+ for (AbstractIssue issue : issues) {
+ if (issue.getHeadline().toLowerCase().matches(ofInterest)) {
+ s.add(issue);
+ suggestions.add(new IssueSuggestion(issue));
+ }
+ }
+
+ for (AbstractIssue issue : issues) {
+ if (!s.contains(issue) && issue.getInstructions().matches(ofInterest)) {
+ suggestions.add(new IssueSuggestion(issue));
+ }
+ }
+ callback.onSuggestionsReady(request, new Response(suggestions));
+ }
+
+ };
+
+ SuggestBox box = new SuggestBox(oracle);
+ box.addEventHandler(new SuggestionHandler() {
+ public void onSuggestionSelected(SuggestionEvent event) {
+ AbstractIssue issue = ((IssueSuggestion) event.getSelectedSuggestion()).getIssue();
+ int index = issues.indexOf(issue);
+ issueList.setSelectedIndex(index);
+ refreshIssue();
+ }
+
+ });
+ box.setAnimationEnabled(false);
+ return box;
+ }
+
+ /**
+ * Create the options panel.
+ *
+ * @return the options panel
+ */
+ private Widget createOptionsPanel() {
+ // Populate a list box containing all issues
+ for (AbstractIssue issue : issues) {
+ issueList.addItem(issue.getHeadline());
+ }
+ issueList.addChangeListener(new ChangeListener() {
+ public void onChange(Widget sender) {
+ refreshIssue();
+ }
+ });
+
+ // Create a button to refresh the current issue
+ Button refreshIssueButton = new Button("Refresh", new ClickListener() {
+ public void onClick(Widget sender) {
+ refreshIssue();
+ }
+ });
+
+ // Create a suggest box to search for issues
+ SuggestBox suggestBox = createIssueFinder();
+
+ // Create a button to move to the previous issue
+ Image prevButton = IMAGES.prevButton().createImage();
+ prevButton.setStyleName("prevButton");
+ prevButton.addClickListener(new ClickListener() {
+ public void onClick(Widget sender) {
+ int selectedIndex = issueList.getSelectedIndex();
+ if (selectedIndex > 0) {
+ issueList.setSelectedIndex(selectedIndex - 1);
+ refreshIssue();
+ } else {
+ Window.alert("You are already on the first issue");
+ }
+ }
+ });
+
+ // Create a button to move to the next issue
+ Image nextButton = IMAGES.nextButton().createImage();
+ nextButton.setStyleName("nextButton");
+ nextButton.addClickListener(new ClickListener() {
+ public void onClick(Widget sender) {
+ int selectedIndex = issueList.getSelectedIndex();
+ if (selectedIndex < issueList.getItemCount() - 1) {
+ issueList.setSelectedIndex(selectedIndex + 1);
+ refreshIssue();
+ } else {
+ Window.alert("You are already on the last issue");
+ }
+ }
+ });
+
+ // Combine the list box and text into a panel
+ HorizontalPanel hPanel = new HorizontalPanel();
+ hPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
+ hPanel.add(new HTML("Select an issue: "));
+ hPanel.add(issueList);
+ hPanel.add(prevButton);
+ hPanel.add(nextButton);
+ hPanel.add(refreshIssueButton);
+ hPanel.add(suggestBox);
+ SimplePanel wrapper = new SimplePanel();
+ wrapper.setStyleName("museum-optionsPanel");
+ wrapper.setWidget(hPanel);
+ return wrapper;
+ }
+
+ /**
+ * Refresh the current issue in the issue list.
+ */
+ private void refreshIssue() {
+ setIssue(issues.get(issueList.getSelectedIndex()));
+ }
+
+ /**
+ * Set the current issue in the issue container.
+ *
+ * @param issue the issue to set
+ */
+ private void setIssue(AbstractIssue issue) {
+ if (issueLinkElement != null) {
+ Utility.getHeadElement().removeChild(issueLinkElement);
+ issueLinkElement = null;
+ }
+ // Fetch the associated style sheet using an HTTP request
+ issueLinkElement = issue.createCSS();
+ Utility.getHeadElement().appendChild(issueLinkElement);
+ issueDescription.setHTML(issue.getInstructions());
+ poller.startPolling(issue);
+ }
+}
diff --git a/reference/code-museum/src/com/google/gwt/museum/client/viewer/nextButton.png b/reference/code-museum/src/com/google/gwt/museum/client/viewer/nextButton.png
new file mode 100644
index 0000000..5a9cdf0
--- /dev/null
+++ b/reference/code-museum/src/com/google/gwt/museum/client/viewer/nextButton.png
Binary files differ
diff --git a/reference/code-museum/src/com/google/gwt/museum/client/viewer/prevButton.png b/reference/code-museum/src/com/google/gwt/museum/client/viewer/prevButton.png
new file mode 100644
index 0000000..289b0d4
--- /dev/null
+++ b/reference/code-museum/src/com/google/gwt/museum/client/viewer/prevButton.png
Binary files differ