blob: d224c0bf11b5c197f0a9541e9fcf2ddf6c7e0b1b [file] [log] [blame]
/*
* 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);
}
}
}