| /* |
| * 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.sample.showcase.client; |
| |
| import com.google.gwt.core.client.GWT; |
| import com.google.gwt.event.logical.shared.HasValueChangeHandlers; |
| import com.google.gwt.event.logical.shared.ValueChangeEvent; |
| import com.google.gwt.event.logical.shared.ValueChangeHandler; |
| import com.google.gwt.event.shared.HandlerRegistration; |
| import com.google.gwt.http.client.Request; |
| import com.google.gwt.http.client.RequestBuilder; |
| import com.google.gwt.http.client.RequestCallback; |
| import com.google.gwt.http.client.RequestException; |
| import com.google.gwt.http.client.Response; |
| import com.google.gwt.i18n.client.LocaleInfo; |
| import com.google.gwt.safehtml.shared.SafeHtml; |
| import com.google.gwt.safehtml.shared.SafeHtmlUtils; |
| import com.google.gwt.user.client.Window; |
| import com.google.gwt.user.client.rpc.AsyncCallback; |
| import com.google.gwt.user.client.ui.SimpleLayoutPanel; |
| import com.google.gwt.user.client.ui.Widget; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * <p> |
| * A widget used to show GWT examples in the ContentPanel. |
| * </p> |
| * <p> |
| * This {@link Widget} uses a lazy initialization mechanism so that the content |
| * is not rendered until the onInitialize method is called, which happens the |
| * first time the {@link Widget} is added to the page. The data in the source |
| * and css tabs are loaded using an RPC call to the server. |
| * </p> |
| */ |
| public abstract class ContentWidget extends SimpleLayoutPanel implements |
| HasValueChangeHandlers<String> { |
| |
| /** |
| * Generic callback used for asynchronously loaded data. |
| * |
| * @param <T> the data type |
| */ |
| public static interface Callback<T> { |
| void onError(); |
| |
| void onSuccess(T value); |
| } |
| |
| /** |
| * Get the simple filename of a class. |
| * |
| * @param c the class |
| */ |
| protected static String getSimpleName(Class<?> c) { |
| String name = c.getName(); |
| return name.substring(name.lastIndexOf(".") + 1); |
| } |
| |
| /** |
| * A description of the example. |
| */ |
| private final SafeHtml description; |
| |
| /** |
| * True if this example has associated styles, false if not. |
| */ |
| private final boolean hasStyle; |
| |
| /** |
| * The name of the example. |
| */ |
| private final String name; |
| |
| /** |
| * A mapping of filenames to their raw source code. The map is populated as |
| * source is loaded. |
| */ |
| private final Map<String, String> rawSource = new HashMap<String, String>(); |
| |
| /** |
| * A list of filenames of the raw source code included with this example. |
| */ |
| private final List<String> rawSourceFilenames = new ArrayList<String>(); |
| |
| /** |
| * The source code associated with this widget. |
| */ |
| private String sourceCode; |
| |
| /** |
| * A style definitions used by this widget. |
| */ |
| private String styleDefs; |
| |
| /** |
| * The view that holds the name, description, and example. |
| */ |
| private ContentWidgetView view; |
| |
| /** |
| * Whether the demo widget has been initialized. |
| */ |
| private boolean widgetInitialized; |
| |
| /** |
| * Whether the demo widget is (asynchronously) initializing. |
| */ |
| private boolean widgetInitializing; |
| |
| /** |
| * Construct a {@link ContentWidget}. |
| * |
| * @param name the text name of the example |
| * @param description a text description of the example |
| * @param hasStyle true if the example has associated styles |
| * @param rawSourceFiles the list of raw source files to include |
| */ |
| public ContentWidget(String name, String description, boolean hasStyle, String... rawSourceFiles) { |
| this(name, SafeHtmlUtils.fromString(description), hasStyle, rawSourceFiles); |
| } |
| |
| /** |
| * Construct a {@link ContentWidget}. |
| * |
| * @param name the text name of the example |
| * @param description a safe html description of the example |
| * @param hasStyle true if the example has associated styles |
| * @param rawSourceFiles the list of raw source files to include |
| */ |
| public ContentWidget(String name, SafeHtml description, boolean hasStyle, |
| String... rawSourceFiles) { |
| this.name = name; |
| this.description = description; |
| this.hasStyle = hasStyle; |
| if (rawSourceFiles != null) { |
| for (String rawSourceFile : rawSourceFiles) { |
| rawSourceFilenames.add(rawSourceFile); |
| } |
| } |
| } |
| |
| public HandlerRegistration addValueChangeHandler(ValueChangeHandler<String> handler) { |
| return addHandler(handler, ValueChangeEvent.getType()); |
| } |
| |
| /** |
| * Get the description of this example. |
| * |
| * @return a description for this example |
| */ |
| public final SafeHtml getDescription() { |
| return description; |
| } |
| |
| /** |
| * Get the name of this example to use as a title. |
| * |
| * @return a name for this example |
| */ |
| public final String getName() { |
| return name; |
| } |
| |
| /** |
| * Get the source code for a raw file. |
| * |
| * @param filename the filename to load |
| * @param callback the callback to call when loaded |
| */ |
| public void getRawSource(final String filename, final Callback<String> callback) { |
| if (rawSource.containsKey(filename)) { |
| callback.onSuccess(rawSource.get(filename)); |
| } else { |
| RequestCallback rc = new RequestCallback() { |
| public void onError(Request request, Throwable exception) { |
| callback.onError(); |
| } |
| |
| public void onResponseReceived(Request request, Response response) { |
| String text = response.getText(); |
| rawSource.put(filename, text); |
| callback.onSuccess(text); |
| } |
| }; |
| |
| String className = this.getClass().getName(); |
| className = className.substring(className.lastIndexOf(".") + 1); |
| sendSourceRequest(rc, ShowcaseConstants.DST_SOURCE_RAW + filename + ".html"); |
| } |
| } |
| |
| /** |
| * Get the filenames of the raw source files. |
| * |
| * @return the raw source files. |
| */ |
| public List<String> getRawSourceFilenames() { |
| return Collections.unmodifiableList(rawSourceFilenames); |
| } |
| |
| /** |
| * Request the styles associated with the widget. |
| * |
| * @param callback the callback used when the styles become available |
| */ |
| public void getStyle(final Callback<String> callback) { |
| if (styleDefs != null) { |
| callback.onSuccess(styleDefs); |
| } else { |
| RequestCallback rc = new RequestCallback() { |
| public void onError(Request request, Throwable exception) { |
| callback.onError(); |
| } |
| |
| public void onResponseReceived(Request request, Response response) { |
| styleDefs = response.getText(); |
| callback.onSuccess(styleDefs); |
| } |
| }; |
| |
| String srcPath = ShowcaseConstants.DST_SOURCE_STYLE + Showcase.THEME; |
| if (LocaleInfo.getCurrentLocale().isRTL()) { |
| srcPath += "_rtl"; |
| } |
| String className = this.getClass().getName(); |
| className = className.substring(className.lastIndexOf(".") + 1); |
| sendSourceRequest(rc, srcPath + "/" + className + ".html"); |
| } |
| } |
| |
| /** |
| * Request the source code associated with the widget. |
| * |
| * @param callback the callback used when the source become available |
| */ |
| public void getSource(final Callback<String> callback) { |
| if (sourceCode != null) { |
| callback.onSuccess(sourceCode); |
| } else { |
| RequestCallback rc = new RequestCallback() { |
| public void onError(Request request, Throwable exception) { |
| callback.onError(); |
| } |
| |
| public void onResponseReceived(Request request, Response response) { |
| sourceCode = response.getText(); |
| callback.onSuccess(sourceCode); |
| } |
| }; |
| |
| String className = this.getClass().getName(); |
| className = className.substring(className.lastIndexOf(".") + 1); |
| sendSourceRequest(rc, ShowcaseConstants.DST_SOURCE_EXAMPLE + className + ".html"); |
| } |
| } |
| |
| /** |
| * Check if the widget should have margins. |
| * |
| * @return true to use margins, false to flush against edges |
| */ |
| public boolean hasMargins() { |
| return true; |
| } |
| |
| /** |
| * Check if the widget should be wrapped in a scrollable div. |
| * |
| * @return true to use add scrollbars, false not to |
| */ |
| public boolean hasScrollableContent() { |
| return true; |
| } |
| |
| /** |
| * Returns true if this widget has a style section. |
| * |
| * @return true if style tab available |
| */ |
| public final boolean hasStyle() { |
| return hasStyle; |
| } |
| |
| /** |
| * When the widget is first initialized, this method is called. If it returns |
| * a Widget, the widget will be added as the first tab. Return null to disable |
| * the first tab. |
| * |
| * @return the widget to add to the first tab |
| */ |
| public abstract Widget onInitialize(); |
| |
| /** |
| * Called when initialization has completed and the widget has been added to |
| * the page. |
| */ |
| public void onInitializeComplete() { |
| } |
| |
| protected abstract void asyncOnInitialize(final AsyncCallback<Widget> callback); |
| |
| /** |
| * Fire a {@link ValueChangeEvent} indicating that the user wishes to see the |
| * specified source file. |
| * |
| * @param filename the filename that the user wishes to see |
| */ |
| protected void fireRawSourceRequest(String filename) { |
| if (!rawSourceFilenames.contains(filename)) { |
| throw new IllegalArgumentException("Filename is not registered with this example: " |
| + filename); |
| } |
| ValueChangeEvent.fire(this, filename); |
| } |
| |
| @Override |
| protected void onLoad() { |
| if (view == null) { |
| view = new ContentWidgetView(hasMargins(), hasScrollableContent()); |
| view.setName(getName()); |
| view.setDescription(getDescription()); |
| setWidget(view); |
| } |
| ensureWidgetInitialized(); |
| super.onLoad(); |
| } |
| |
| /** |
| * Ensure that the demo widget has been initialized. Note that initialization |
| * can fail if there is a network failure. |
| */ |
| private void ensureWidgetInitialized() { |
| if (widgetInitializing || widgetInitialized) { |
| return; |
| } |
| |
| widgetInitializing = true; |
| |
| asyncOnInitialize(new AsyncCallback<Widget>() { |
| public void onFailure(Throwable reason) { |
| widgetInitializing = false; |
| Window.alert("Failed to download code for this widget (" + reason + ")"); |
| } |
| |
| public void onSuccess(Widget result) { |
| widgetInitializing = false; |
| widgetInitialized = true; |
| |
| Widget widget = result; |
| if (widget != null) { |
| view.setExample(widget); |
| } |
| onInitializeComplete(); |
| } |
| }); |
| } |
| |
| /** |
| * Send a request for source code. |
| * |
| * @param callback the {@link RequestCallback} to send |
| * @param url the URL to target |
| */ |
| private void sendSourceRequest(RequestCallback callback, String url) { |
| RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, GWT.getModuleBaseURL() + url); |
| builder.setCallback(callback); |
| try { |
| builder.send(); |
| } catch (RequestException e) { |
| callback.onError(null, e); |
| } |
| } |
| } |