001    /*
002     * Copyright 2006 Google Inc.
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005     * use this file except in compliance with the License. You may obtain a copy of
006     * the License at
007     * 
008     * http://www.apache.org/licenses/LICENSE-2.0
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012     * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013     * License for the specific language governing permissions and limitations under
014     * the License.
015     */
016    package com.google.gwt.user.client;
017    
018    import com.google.gwt.core.client.GWT;
019    import com.google.gwt.core.client.GWT.UncaughtExceptionHandler;
020    import com.google.gwt.user.client.impl.HistoryImpl;
021    
022    import java.util.Iterator;
023    import java.util.Vector;
024    
025    /**
026     * This class allows you to interact with the browser's history stack. Each
027     * "item" on the stack is represented by a single string, referred to as a
028     * "token". You can create new history items (which have a token associated with
029     * them when they are created), and you can programmatically force the current
030     * history to move back or forward.
031     * 
032     * <p>
033     * In order to receive notification of user-directed changes to the current
034     * history item, implement the
035     * {@link com.google.gwt.user.client.HistoryListener} interface and attach it
036     * via {@link #addHistoryListener}.
037     * </p>
038     * 
039     * <p>
040     * Example: {@link com.google.gwt.examples.HistoryExample code}
041     * </p>
042     */
043    public class History {
044    
045      private static Vector historyListeners = new Vector();
046      private static HistoryImpl impl;
047    
048      static {
049        impl = (HistoryImpl) GWT.create(HistoryImpl.class);
050        if (!impl.init()) {
051          // Set impl to null as a flag to no-op future calls.
052          impl = null;
053    
054          // Tell the user.
055          GWT.log("Unable to initialize the history subsystem; did you "
056            + "include the history frame in your host page? Try "
057            + "<iframe id='__gwt_historyFrame' style='width:0;height:0;border:0'>"
058            + "</iframe>", null);
059        }
060      }
061    
062      /**
063       * Adds a listener to be informed of changes to the browser's history stack.
064       * 
065       * @param listener the listener to be added
066       */
067      public static void addHistoryListener(HistoryListener listener) {
068        historyListeners.add(listener);
069      }
070    
071      /**
072       * Programmatic equivalent to the user pressing the browser's 'back' button.
073       */
074      public static native void back() /*-{
075       $wnd.history.back();
076       }-*/;
077    
078      /**
079       * Programmatic equivalent to the user pressing the browser's 'forward'
080       * button.
081       */
082      public static native void forward() /*-{
083       $wnd.history.forward();
084       }-*/;
085    
086      /**
087       * Gets the current history token. The listener will not receive an
088       * onHistoryChanged() event for the initial token; requiring that an
089       * application request the token explicitly on startup gives it an opportunity
090       * to run different initialization code in the presence or absence of an
091       * initial token.
092       * 
093       * @return the initial token, or the empty string if none is present.
094       */
095      public static String getToken() {
096        return impl != null ? impl.getToken() : "";
097      }
098    
099      /**
100       * Adds a new browser history entry. In hosted mode, the 'back' and 'forward'
101       * actions are accessible via the standard Alt-Left and Alt-Right keystrokes.
102       * Calling this method will cause {@link #onHistoryChanged} to be called as
103       * well.
104       * 
105       * @param title the title that should appear in the history menu for this
106       *          history item.
107       */
108      public static void newItem(String historyToken) {
109        if (impl != null) {
110          impl.newItem(historyToken);
111        }
112      }
113    
114      public static void onHistoryChanged(String historyToken) {
115        UncaughtExceptionHandler handler = GWT.getUncaughtExceptionHandler();
116        if (handler != null)
117          fireHistoryChangedAndCatch(historyToken, handler);
118        else
119          fireHistoryChangedImpl(historyToken);
120      }
121    
122      private static void fireHistoryChangedAndCatch(String historyToken,
123          UncaughtExceptionHandler handler) {
124        try {
125          fireHistoryChangedImpl(historyToken);
126        } catch (Throwable e) {
127          handler.onUncaughtException(e);
128        }
129      }
130    
131      private static void fireHistoryChangedImpl(String historyToken) {
132        for (Iterator it = historyListeners.iterator(); it.hasNext();) {
133          HistoryListener listener = (HistoryListener) it.next();
134          listener.onHistoryChanged(historyToken);
135        }
136      }
137    
138      /**
139       * Removes a history listener.
140       * 
141       * @param listener the listener to be removed
142       */
143      public static void removeHistoryListener(HistoryListener listener) {
144        historyListeners.remove(listener);
145      }
146    }