| /* |
| * 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.event.dom.client.ChangeEvent; |
| import com.google.gwt.event.dom.client.ChangeHandler; |
| import com.google.gwt.event.dom.client.ClickEvent; |
| import com.google.gwt.event.dom.client.ClickHandler; |
| import com.google.gwt.event.logical.shared.SelectionEvent; |
| import com.google.gwt.event.logical.shared.SelectionHandler; |
| 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.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.Widget; |
| import com.google.gwt.user.client.ui.SuggestOracle.Suggestion; |
| |
| 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. |
| */ |
| 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<AbstractIssue> s = new HashSet<AbstractIssue>(); |
| 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.addSelectionHandler(new SelectionHandler<Suggestion>() { |
| public void onSelection(SelectionEvent<Suggestion> event) { |
| AbstractIssue issue = ((IssueSuggestion) event.getSelectedItem()).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.addChangeHandler(new ChangeHandler() { |
| public void onChange(ChangeEvent event) { |
| refreshIssue(); |
| } |
| }); |
| |
| // Create a button to refresh the current issue |
| Button refreshIssueButton = new Button("Refresh", new ClickHandler() { |
| public void onClick(ClickEvent event) { |
| 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.addClickHandler(new ClickHandler() { |
| public void onClick(ClickEvent event) { |
| 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.addClickHandler(new ClickHandler() { |
| public void onClick(ClickEvent event) { |
| 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); |
| } |
| } |